Compare commits

...

1517 Commits
0.2.0 ... 2.5.1

Author SHA1 Message Date
orignal
0e5b32ef13 2.5.1 2016-03-10 13:34:16 -05:00
orignal
0493a321d2 oveeride --log for windows 2016-03-10 12:23:17 -05:00
orignal
38b6c12153 fixed bug with missed data directory 2016-03-10 12:05:28 -05:00
orignal
74d4b8e0b9 invoke win32app functions from main 2016-03-09 14:41:14 -05:00
orignal
95b2bf3645 fixed windows build 2016-03-09 09:38:19 -05:00
hagen
121ac4f1de * move mingw-specific rules to Makefile.mingw 2016-03-09 13:36:39 +00:00
orignal
ec8550d587 use ictoopie_16 in tray 2016-03-08 21:18:48 -05:00
orignal
e403c419e5 16x16 icon added 2016-03-08 17:40:43 -05:00
orignal
4b0d587fe1 Daemon::run 2016-03-08 15:02:32 -05:00
orignal
ebd356c7bd set correct icons 2016-03-08 11:24:29 -05:00
orignal
507093dbad compile with resources 2016-03-07 21:36:11 -05:00
orignal
4cfdc77015 invoke daemon 2016-03-07 16:17:06 -05:00
orignal
9096cacba8 tray icon added 2016-03-07 16:06:34 -05:00
orignal
607336d3ce tray icon added 2016-03-07 15:57:32 -05:00
orignal
6383fc3575 initial commit of Win32App 2016-03-07 14:54:57 -05:00
orignal
a5576ddbf3 don't acquire DH keys pair until connection is established 2016-03-06 09:57:38 -05:00
orignal
e2a70873b8 fixed garbage in console for windows 2016-03-05 21:46:01 -05:00
orignal
380b56a89d 2.5.0 2016-03-04 21:34:23 -05:00
orignal
8e09f3478f fixed warnings 2016-03-04 20:35:53 -05:00
orignal
c1ce51eb12 Merge pull request #403 from xcps/webirc2
variable name
2016-03-04 09:53:52 -05:00
xcps
9aeb773169 variable name 2016-03-04 19:26:28 +05:00
orignal
091c13ff41 Merge pull request #402 from xcps/webirc2
WebIRC support
2016-03-04 07:05:37 -05:00
xcps
ef0bab0c6e webirc support 2016-03-04 11:37:38 +05:00
orignal
70bd16adf6 set established state for zero-hops tunnles 2016-03-03 17:57:15 -05:00
orignal
96a713afeb zero-hops outbound tunnels 2016-03-03 16:24:13 -05:00
orignal
0f56b1c943 show number of received bytes for zero-hops inbound tunnel 2016-03-03 07:30:38 -05:00
orignal
d541572882 enable zero-hops inbound tunnel 2016-03-02 22:41:53 -05:00
orignal
ecfdc377ec send close floodfills only in DatabaseSearchReply 2016-03-02 19:46:32 -05:00
orignal
fa67e90767 inbound zero-hops tunnel 2016-03-02 16:12:02 -05:00
orignal
81b72d5481 fixed crash on termination if proxies were excluded 2016-03-02 12:04:02 -05:00
orignal
ef6028e933 replace std::map to std::list for inbound tunnels 2016-03-02 11:58:52 -05:00
orignal
5d41fe4a35 Merge pull request #400 from majestrate/webui-add-tunnel-count
Add Transit/Client Tunnel Count to web ui
2016-03-02 10:13:51 -05:00
Jeff Becker
1dc6cec1aa add client/transit tunnel count in webui 2016-03-02 10:05:26 -05:00
Jeff Becker
9378668e52 add colin 2016-03-02 10:03:50 -05:00
Jeff Becker
eb96ead80e add tunnel counts to front page of web ui 2016-03-02 10:03:36 -05:00
orignal
9403fbaf81 common tunnels' hash table 2016-03-01 20:48:56 -05:00
orignal
79190f313d use shared_ptr for transit tunnels 2016-03-01 15:22:36 -05:00
orignal
4c124284b6 Merge pull request #399 from PurpleI2P/openssl
irc tunnel and gzip
2016-03-01 13:39:26 -05:00
orignal
6d892179c8 added gzip parameter for server tunnels 2016-02-29 14:44:15 -05:00
orignal
61675c20d8 don't delete log file upon HUP 2016-02-29 11:02:55 -05:00
orignal
4aae878db8 increase LeaseSet expiration threshold 2016-02-28 21:43:18 -05:00
orignal
918884bd11 Merge pull request #398 from xcps/irc_ip2b32
irc tunnel
2016-02-28 16:19:19 -05:00
xcps
8799f9079b change part for replace 2016-02-29 02:15:29 +05:00
orignal
0b471cfd06 Merge pull request #397 from xcps/irc_ip2b32
Irc ip to b32
2016-02-28 14:14:11 -05:00
xcps
7b39a12396 ready 2016-02-28 22:32:34 +05:00
xcps
57a53b4b6c fixed I2PServerTunnelHTTP call 2016-02-28 21:13:01 +05:00
orignal
f6d0b3368f znx cert added 2016-02-28 09:56:17 -05:00
xcps
0fe7bdf849 init 2016-02-28 18:17:36 +05:00
xcps
a26dc39a6d ident fix 2016-02-28 18:17:35 +05:00
xcps
e45cfe7d0c init 2016-02-28 18:17:35 +05:00
orignal
efefa8caf5 Merge pull request #395 from PurpleI2P/openssl
socks outproxy
2016-02-27 16:16:15 -05:00
orignal
cc13db9b1f updated FreeBSD instructions 2016-02-27 15:44:36 -05:00
orignal
f339544256 Merge pull request #394 from majestrate/outproxy-socks
support for outproxy via local upstream socks proxy
2016-02-26 21:42:04 -05:00
Jeff Becker
1a05bcb295 initial support for out proxy via local upstream socks proxy 2016-02-26 17:06:11 -05:00
orignal
190e26276a reuse tunnel pair for LS request 2016-02-26 16:17:29 -05:00
orignal
bb33760e87 don't re-request twice 2016-02-26 16:16:59 -05:00
orignal
9e105b4983 Merge pull request #393 from PurpleI2P/openssl
recent changes
2016-02-26 15:13:31 -05:00
orignal
8dcf70408d hostoverride added 2016-02-25 20:32:05 -05:00
orignal
9d6d1825c7 pass flag to SSU header 2016-02-25 18:40:40 -05:00
orignal
1a4923cdce don't request relayTag if we are reachable 2016-02-25 15:57:58 -05:00
orignal
316e440390 Merge pull request #392 from PurpleI2P/openssl
recent changes
2016-02-25 13:51:03 -05:00
orignal
7d66019220 start checking for expiration after 10 minutes 2016-02-24 11:50:56 -05:00
orignal
f98a6fb665 tighten RouterInfo expiration 2016-02-24 11:31:14 -05:00
orignal
dbdc7279c4 Merge pull request #391 from PurpleI2P/openssl
new fs
2016-02-24 11:21:36 -05:00
orignal
7726705b5c process request relay tag extended SSU option 2016-02-23 12:16:53 -05:00
orignal
34b7e8815a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-23 11:17:14 -05:00
orignal
8ac2b58a44 Merge branch 'master' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-23 11:17:01 -05:00
orignal
fe97f0929b delete expired floodfills thorugh a separate loop 2016-02-22 20:51:32 -05:00
orignal
6eec353c2b moved tunnel config file inialization to ClientContext 2016-02-22 15:27:40 -05:00
orignal
2b4c3b8d1f start up if i2p.conf is not presented 2016-02-22 15:17:58 -05:00
orignal
df99b37c4d Merge pull request #388 from l-n-s/openssl
added family documentation
2016-02-22 13:45:29 -05:00
libre-net-society
ab6f3fcf8e added family documentation 2016-02-22 20:49:17 +03:00
orignal
ca6f656e1b ignore non-reachable floodfills 2016-02-22 10:27:43 -05:00
orignal
88798b1a9e fixed windows build 2016-02-22 09:53:26 -05:00
hagen
c197270125 Merge branch 'new-fs' into openssl
Conflicts:
	Family.cpp
2016-02-22 13:04:53 +00:00
hagen
dc344d4658 * add comment 2016-02-22 12:57:25 +00:00
orignal
b4864831e0 Merge pull request #387 from PurpleI2P/openssl
family
2016-02-21 20:29:49 -05:00
orignal
476dffff13 Create family.md 2016-02-21 15:26:14 -05:00
orignal
389ee974f3 family 2016-02-20 21:20:21 -05:00
hagen
0d15eceacb * Profiling : move storage from FS.cpp to Profiling.cpp 2016-02-21 01:49:35 +00:00
hagen
b69fbdda9a * NetDb : move storage from FS.cpp to NetDb.cpp 2016-02-21 01:49:32 +00:00
hagen
d3746e0119 * FS.h : add include guards 2016-02-21 01:49:29 +00:00
orignal
230af9cafa set router's family 2016-02-20 20:20:19 -05:00
orignal
4db63d113c i2pd-dev certificate updated 2016-02-20 09:22:09 -05:00
orignal
008583396d extract CN 2016-02-20 08:33:13 -05:00
hagen
33a33e3c71 * i2p::util::http::GetHttpContent() : use std::transform instead boost 2016-02-20 01:47:34 +00:00
hagen
d312d753e9 * Destination.cpp : fix lambda with 4.7 2016-02-20 01:47:32 +00:00
hagen
02310d4af6 * Family : use i2p::fs::ReadDir instead direct boost::filesystem call 2016-02-20 01:47:29 +00:00
orignal
0e6d8c4e25 i2pd-dev family certificate added 2016-02-19 20:09:48 -05:00
hagen
55315fca80 Merge branch 'openssl' into new-fs
Conflicts:
	AddressBook.cpp
	NetDb.cpp
	filelist.mk
2016-02-20 00:59:48 +00:00
orignal
4eef9e780f extract and verify family from RouterInfo 2016-02-19 16:37:41 -05:00
orignal
7bfc3562af extract EcDSA key from family certificate 2016-02-19 16:13:46 -05:00
orignal
5b0b0d6d36 Merge pull request #386 from PurpleI2P/openssl
recent changes
2016-02-19 13:18:36 -05:00
orignal
cb64072f7b fixed windows build 2016-02-19 11:18:01 -05:00
orignal
c5b6da7201 case-insensitive http responses 2016-02-19 10:04:52 -05:00
orignal
f1d4818045 Family.cpp added 2016-02-18 22:39:09 -05:00
orignal
76b49f6985 uncompress stream by chunks 2016-02-18 22:34:55 -05:00
orignal
094d9193b9 start addressbook first 2016-02-18 22:34:14 -05:00
orignal
3053a9b6a0 enable i2p gzip compression 2016-02-18 20:35:14 -05:00
orignal
47bf0ef591 free pkey after usage 2016-02-18 16:28:43 -05:00
orignal
e2aa2709ac family added 2016-02-18 15:57:43 -05:00
orignal
9a6d478eb1 handle compressed addressbook 2016-02-18 13:19:31 -05:00
orignal
4f37e7dc3c Merge pull request #383 from PurpleI2P/openssl
recent changes
2016-02-18 09:06:48 -05:00
hagen
2a4ba8d349 * Addressbook : move storage init code from constructor to Init() : was too early 2016-02-18 10:42:50 +00:00
hagen
85bd7a63c6 * AddressBook : embed HashedStorage instance into AddressBookFilesystemStorage class 2016-02-18 10:42:50 +00:00
hagen
138d57143a * FS.cpp : add const to accessors 2016-02-18 10:42:50 +00:00
hagen
464a228106 * FS.cpp : rename method 2016-02-18 10:42:50 +00:00
hagen
2b92a039bb * FS.h : more comments 2016-02-18 10:42:50 +00:00
hagen
f190ee951c * use characters sets from Base.cpp - remove ABook class 2016-02-18 10:42:50 +00:00
hagen
68cc75cada * Base.cpp : add T32 character set + accessor 2016-02-18 10:42:41 +00:00
orignal
b4e324ec0e flood to 3 closest floodfills 2016-02-17 21:24:21 -05:00
orignal
32fe2e7974 correct monotonic expiration time calculation 2016-02-17 19:36:07 -05:00
orignal
713513aacc flood newer RI/LS only 2016-02-17 15:36:55 -05:00
orignal
b4ffca56a3 update lease's expiration time continiously 2016-02-17 13:10:29 -05:00
orignal
f2168774a5 check leaseset timestamp 2016-02-16 22:57:38 -05:00
orignal
febc00d357 fixed race condition of DeliveryStatus message 2016-02-16 16:10:22 -05:00
orignal
01a8c507e5 Merge pull request #381 from PurpleI2P/openssl
recent changes
2016-02-16 16:01:12 -05:00
orignal
bf7982cc2e build with make added 2016-02-16 15:08:35 -05:00
orignal
2e9689886b build with make added 2016-02-16 15:07:56 -05:00
orignal
2003b34036 12 hours expiration if more than 2500 routers 2016-02-15 21:40:49 -05:00
orignal
e1995b5c70 try to download default hosts.txt until success 2016-02-15 18:20:01 -05:00
orignal
3890acabc4 Merge pull request #380 from PurpleI2P/openssl
fixed http issues
2016-02-15 16:22:15 -05:00
orignal
ba6c0d0423 fixed messy http pages 2016-02-15 15:16:53 -05:00
orignal
882e7a845e process remaining data from stream 2016-02-14 22:10:56 -05:00
orignal
ca56d3fc23 handle LeaseSet expiration correctly 2016-02-14 18:30:07 -05:00
orignal
49b1e76585 use rtt for ack timeout 2016-02-13 23:10:51 -05:00
orignal
80f81685d1 use rtt for ack timeout 2016-02-13 23:02:58 -05:00
orignal
21dead3125 increase lease expiration threshold 2016-02-13 17:56:42 -05:00
orignal
1521d08285 family cetificates added 2016-02-13 17:13:07 -05:00
orignal
59b2e31add ssl certificates updated 2016-02-13 17:10:54 -05:00
orignal
b5feb3fd66 update reseeds list 2016-02-13 17:03:25 -05:00
orignal
7785e6ebd2 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-12 20:56:46 -05:00
orignal
c561d71dc0 count lease expiration threshold 2016-02-12 20:56:29 -05:00
orignal
2cfb697867 strip our Referer and replace User-Agent 2016-02-12 15:42:13 -05:00
orignal
c680ff006e Merge pull request #378 from PurpleI2P/openssl
recent changes
2016-02-12 11:23:29 -05:00
orignal
333103f50e shared RTT 2016-02-11 22:18:44 -05:00
orignal
517385fb63 Lease enddate threshold 2016-02-11 22:18:24 -05:00
orignal
ee8ab58d64 don't reply to lookup with expired LeaseSet 2016-02-11 22:17:33 -05:00
orignal
b967acda58 flood to floodfills that are close than us only 2016-02-11 15:05:46 -05:00
orignal
d81ca5f919 local destination leaseset storage verification 2016-02-11 14:45:33 -05:00
orignal
07adf64aec Merge pull request #376 from PurpleI2P/openssl
recent changes
2016-02-11 14:37:11 -05:00
orignal
fbb98e1aec show actual name of an invalid parameter 2016-02-11 11:18:15 -05:00
orignal
2fdf927704 show actual name of an invalid parameter 2016-02-11 10:54:36 -05:00
hagen
4b84656133 * i2p::fs migration: drop unused code from util.* (#314) 2016-02-11 13:05:00 +00:00
hagen
97c136d043 * i2p::fs migration: Daemon, DaemonLinux, api (#290) 2016-02-11 13:05:00 +00:00
hagen
79bf44b3f5 * i2p::fs migration: ClientContext, Destination, HTTPServer, Reseed, RouterContext 2016-02-11 13:05:00 +00:00
hagen
ddd8d4aeb2 * i2p::fs migration: AddressBook.* 2016-02-11 13:05:00 +00:00
hagen
bfcb6f577f * i2p::fs migration: Profiling.* 2016-02-11 13:05:00 +00:00
hagen
2b137b43e6 * i2p::fs migration: I2PControl.* 2016-02-11 13:05:00 +00:00
hagen
6d74493491 * i2p::fs migration: NetDb.* 2016-02-11 13:05:00 +00:00
hagen
6f4271c054 * update buildsystems 2016-02-11 13:05:00 +00:00
hagen
f24054100e * new i2p::fs implementation 2016-02-11 13:05:00 +00:00
hagen
6e98649607 * I2PControl: send valid error response, instead closing connection 2016-02-11 13:00:56 +00:00
hagen
b2108ff2d0 * fix flags on std::ifstream 2016-02-11 13:00:56 +00:00
hagen
8949ebf041 * tune logging 2016-02-11 13:00:56 +00:00
hagen
576801cd32 * Addressbook: load addresses at start, not on first request 2016-02-11 13:00:56 +00:00
hagen
2f2b12811f * Addressbook: don't save to disk if address map is empty 2016-02-11 13:00:56 +00:00
hagen
d8ea3a9035 * make target 'strip' 2016-02-11 13:00:56 +00:00
orignal
45c3b3987b reset floodfill 2016-02-11 07:50:29 -05:00
orignal
93720fffd4 shared path between streams 2016-02-10 22:51:08 -05:00
orignal
61ad6a2b88 set supported transports flag after actual address insertion 2016-02-10 16:09:34 -05:00
orignal
c9d5b3c0ff Merge pull request #373 from PurpleI2P/openssl
recent changes
2016-02-10 10:51:21 -05:00
hagen
d51bf735c4 * fix mistype 2016-02-10 10:37:30 +00:00
hagen
22c388ab18 * fix compilation with gcc 4.7/4.8 2016-02-10 10:37:30 +00:00
hagen
d5f831301f * explicit log message when bandwidth set to 'low' 2016-02-10 10:37:30 +00:00
hagen
dcab37a148 * update debian/i2pd.{init,upstart} : logging options 2016-02-10 10:37:30 +00:00
hagen
60b2da3671 * add --datadir option (not actually works yet) (#290) 2016-02-10 10:37:30 +00:00
hagen
5c1b5816d4 * fix segfault when offline (#330) 2016-02-10 10:37:30 +00:00
hagen
7a0a45e9d2 * use IsDefault() to check explicitly set values 2016-02-10 10:37:30 +00:00
hagen
70f72a78f6 + i2p::config::IsDefault 2016-02-10 10:37:30 +00:00
orignal
e056c9c135 drop expired leasesand renew leaseset 2016-02-09 22:42:01 -05:00
orignal
c754b5ae18 fixed crash 2016-02-09 17:54:22 -05:00
orignal
481fafc11d invalidate excluded leases 2016-02-09 15:27:23 -05:00
orignal
7d927b0e28 shared_ptr for Lease 2016-02-09 10:46:27 -05:00
orignal
c314b07136 Merge pull request #371 from PurpleI2P/openssl
recent changes
2016-02-09 10:37:46 -05:00
orignal
16fe13bf4a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-08 20:30:53 -05:00
orignal
d19eda7e08 moved Config.cpp to libi2pd 2016-02-08 20:29:56 -05:00
orignal
6f0a136727 some cleanup 2016-02-08 20:29:34 -05:00
orignal
e2e101e4fb queue up out of sequence packets 2016-02-08 15:47:39 -05:00
orignal
74f03202b7 queue up out of sequence packets 2016-02-08 15:02:17 -05:00
orignal
3d19e92059 queue up out of sequence packets 2016-02-08 14:42:20 -05:00
orignal
bfff125cc5 Merge pull request #370 from PurpleI2P/openssl
recent changes
2016-02-08 14:41:40 -05:00
orignal
e90baf3ca6 correct required base64 buffer size 2016-02-07 21:35:06 -05:00
orignal
f3b277aeef doesn't store leases in netdb 2016-02-07 19:45:06 -05:00
orignal
76096747b6 cleanup incoming and outgoing tags together 2016-02-07 17:45:11 -05:00
orignal
4c6ef32d72 fixed #369 2016-02-06 08:52:02 -05:00
orignal
a8e12e624d fixed hanging connection 2016-02-05 21:38:03 -05:00
orignal
88a43bfc28 Merge pull request #368 from majestrate/fix_sam_b32_naming
allow resolving of .b32.i2p addresses in SAM name lookup
2016-02-05 16:37:00 -05:00
Jeff Becker
3b268fe3cc allow resolving of .b32.i2p addresses in SAM name lookup 2016-02-05 16:17:53 -05:00
orignal
4c72d43a8a use more efficient XOR over ChipherBlocks for win32 2016-02-05 15:58:14 -05:00
orignal
0a5f8527b2 Merge pull request #366 from majestrate/bug_fixes
Squash potential future bugs.
2016-02-05 13:12:08 -05:00
Jeff Becker
9f1b84d6f2 use const size_t instead of size_t 2016-02-05 12:39:17 -05:00
Jeff Becker
babcbcbcea use const size_t instead of size_t 2016-02-05 12:32:50 -05:00
orignal
823a6017fe Merge pull request #364 from 0niichan/patch-6
Added instructions for a 64-bit OS
2016-02-05 11:12:57 -05:00
0niichan
f034aef2ae Added instructions for a 64-bit OS 2016-02-05 22:58:04 +07:00
Jeff Becker
bf38bd5a1d * Fill padding with random in NTCP phase3
* Fill padding with random in NTCPSession::CreateMsgBuffer

* Silence unused variable warnings in NTCPSession.cpp
2016-02-05 10:40:23 -05:00
Jeff Becker
b922809c9d Merge branch 'openssl' into bug_fixes 2016-02-05 10:20:22 -05:00
Jeff Becker
05b0bda8bb Merge remote-tracking branch 'purple/openssl' into openssl 2016-02-05 10:16:42 -05:00
orignal
0aa3aa1b8d Merge pull request #363 from majestrate/fix_su3_overflow
fix issue #362 , add bounds check to su3 fileNameLength
2016-02-05 10:03:34 -05:00
Jeff Becker
d4febb4e84 * bounds check on Identity::FromBuffer
* properly indet last commits
2016-02-05 08:52:07 -05:00
Jeff Becker
21090eaa39 forgot to commit Base.cpp changes 2016-02-05 08:46:08 -05:00
Jeff Becker
d0ea59c568 add base64 buffer encoding bounds checking 2016-02-05 08:44:09 -05:00
Jeff Becker
a292bc77ba fix issue #362 , add bounds check to su3 fileNameLength 2016-02-05 07:55:28 -05:00
orignal
98d5e0b56d #355. reopen log file by SIGHUP 2016-02-04 13:53:38 -05:00
orignal
7ca1cfab1a use shared_ptr for log's stream 2016-02-04 12:36:58 -05:00
orignal
2e7ce38552 compatibility with gcc 4.6 2016-02-04 12:36:54 -05:00
orignal
0ef3a2472d Merge pull request #361 from PurpleI2P/openssl
rebase master to 2.4.0
2016-02-04 10:08:17 -05:00
orignal
b97f095de4 Merge pull request #360 from AMDmi3/build-type
Do not force build type
2016-02-04 10:06:09 -05:00
orignal
10e2b35483 Merge pull request #359 from AMDmi3/system-include-dirs
Include system directories as SYSTEM
2016-02-04 10:00:47 -05:00
orignal
16920a89f3 Merge pull request #358 from AMDmi3/local-include-dirs-before
Always place local include directories before all others
2016-02-04 10:00:10 -05:00
orignal
1a5b9de82e Merge pull request #357 from AMDmi3/conditional-miniupnp
Do not try to use miniupnp if upnp support is disabled
2016-02-04 09:59:18 -05:00
Dmitry Marakasov
4ef183fee6 Do not force build type 2016-02-04 16:53:24 +03:00
Dmitry Marakasov
2115ce6606 Do not try to use miniupnp if upnp support is disabled 2016-02-04 16:52:41 +03:00
Dmitry Marakasov
61d1b733f7 Include system directories as SYSTEM 2016-02-04 16:52:20 +03:00
Dmitry Marakasov
4978edb8be Always place local include directories before all others 2016-02-04 16:49:07 +03:00
orignal
51f7aba807 fixed crash 2016-02-03 16:18:49 -05:00
orignal
b9b143e4e7 don't persist proxy keys by defualt 2016-02-03 15:29:15 -05:00
orignal
0e7596a205 Update build_notes_windows.md 2016-02-03 15:01:28 -05:00
orignal
8c401cf01b check for USE_AESNI=1 2016-02-03 15:00:55 -05:00
orignal
6782e6a532 AES-NI 2016-02-03 13:46:26 -05:00
orignal
4386bd93c3 handle USE_AESNI for mingw 2016-02-03 12:48:59 -05:00
hagen
72b3c10ebd * fix updating address in RI 2016-02-03 14:21:22 +00:00
hagen
62cec2a31c * correct shutdown of httpserver & socksproxy 2016-02-03 13:14:54 +00:00
hagen
0c442622af * chg default for --host= option : was broken in 900fc1c 2016-02-03 12:28:33 +00:00
hagen
bf3c4bc588 * bump version 2016-02-03 11:28:58 +00:00
hagen
d98dd83369 * sync actial options and docs (#356) 2016-02-03 11:28:55 +00:00
hagen
21ecf309bb * Daemon.cpp : --log option now uses descriptive values: file, stdout (#356) 2016-02-03 11:28:53 +00:00
hagen
4bb4012d87 * Daemon.cpp : move logs init to single place 2016-02-03 11:28:52 +00:00
hagen
10fd8eb709 * Daemon.cpp : move ParseCmdline() before use of i2p::fs -- allow redefined paths 2016-02-03 11:28:49 +00:00
orignal
b1cc1db967 fixed POST for server http tunnel 2016-02-02 22:00:51 -05:00
orignal
77d8bae2c2 fixed server http tunnel header 2016-02-02 19:24:49 -05:00
orignal
7274d43645 fixed incorrect long fragment size 2016-02-02 18:27:52 -05:00
orignal
3eeee1b08d set correct log level for console 2016-02-02 12:16:29 -05:00
orignal
64b2a32c9a #343. check for malformed messages 2016-02-02 11:55:38 -05:00
orignal
4ced1e5075 proccess loglevel and logfile correctly 2016-02-02 07:24:14 -05:00
orignal
8de15c9d0d fixed bandwidth logic 2016-02-01 18:10:45 -05:00
orignal
31d716bd0c fixed race condition 2016-02-01 14:19:54 -05:00
hagen
3da6b3930b * I2PControl.cpp : fix handling relative paths for cert/key 2016-02-01 15:57:25 +00:00
hagen
900fc1cb46 Merge branch 'new-cmdline' into openssl
Conflicts:
	ClientContext.cpp
	Daemon.cpp
	I2PControl.cpp
	I2PControl.h
	docs/configuration.md
2016-02-01 15:08:20 +00:00
hagen
deb87f1d4c * for compatibility - leave --log option with arg 2016-02-01 14:42:52 +00:00
hagen
ed44d23afb * update docs/ 2016-02-01 14:26:40 +00:00
hagen
8baf7f3f6a * temporary remove short options : conflicts with remapping 2016-02-01 14:26:26 +00:00
hagen
d2d4fa29e4 * add --logfile option 2016-02-01 10:53:17 +00:00
hagen
0c56cd63bd * chg default port for http proxy 2016-02-01 10:53:15 +00:00
orignal
c9cf84f2f4 correct SAM datagram size for Windows 2016-01-31 22:37:38 -05:00
orignal
0966369723 copy transit message for nedb 2016-01-31 18:27:47 -05:00
orignal
4f6c3d52b3 Merge pull request #354 from h0bbyte/openssl
I2PControl add total.sent|received.bytes
2016-01-31 15:55:47 -05:00
orignal
97f8ab5c51 Update build_notes_windows.md 2016-01-31 14:58:49 -05:00
h0bbyte
8805f1e4d6 I2PControl add total.sent|received.bytes 2016-01-31 22:52:20 +03:00
orignal
3ae57e0ca9 Merge pull request #353 from 0niichan/patch-5
correct BOOST_SUFFIX
2016-01-31 14:20:11 -05:00
0niichan
a8e4301f23 correct BOOST_SUFFIX 2016-01-31 23:59:37 +06:00
orignal
68bc78d00b Update README.md 2016-01-30 21:04:02 -05:00
orignal
1dc9e74df4 check TunnelBuild message size 2016-01-30 10:35:32 -05:00
orignal
a69cee03e5 remove coreVersion and stat_update 2016-01-29 22:35:51 -05:00
orignal
bf15ad3bba 0.9.24 2016-01-29 21:53:57 -05:00
orignal
bb3f50f967 Merge pull request #352 from 0niichan/patch-4
httpProxyPort 4444
2016-01-28 19:45:30 -05:00
0niichan
1042e19845 httpProxyPort 4444 2016-01-29 07:18:49 +07:00
orignal
85830d5076 fixed race condtion #350 2016-01-27 22:09:35 -05:00
orignal
c053bebccd reduced numeber of error messages 2016-01-27 21:54:42 -05:00
hagen
d6d6ae8af2 * Config.cpp : add old options remapping (revert this after 2.6.0) 2016-01-27 12:17:41 +00:00
orignal
6d8b0e3a5d control logs destination through -log parameter 2016-01-26 22:30:00 -05:00
orignal
cfd7f1571b check clock skew 2016-01-26 19:02:06 -05:00
orignal
f31c04d92a Merge pull request #347 from evgkrsk/log-subdir
Write service log to separate directory
2016-01-26 09:53:09 -05:00
orignal
89b58ec3af Removed confusing accesslist 2016-01-26 09:45:29 -05:00
Evgenii Terechkov
ab0d66c2ef Write service log to separate directory 2016-01-26 21:12:48 +07:00
hagen
9774865d4a * docs/configuration.md 2016-01-26 08:03:44 +00:00
hagen
3817a0c2a1 Merge branch 'openssl' into new-cmdline 2016-01-26 08:03:18 +00:00
orignal
5215bdc035 clean up remote destinations without outgoing and unconfirmed tags 2016-01-25 22:10:06 -05:00
orignal
8061d306dd check tunnel payload size 2016-01-25 14:31:51 -05:00
orignal
30f68759ff fixed race condition 2016-01-25 13:34:04 -05:00
orignal
3f0b595085 fixed typo 2016-01-24 22:24:39 -05:00
orignal
0c9ce6258c sockoutproxy params added 2016-01-24 09:59:02 -05:00
hagen
7da17ba21e * tune logs 2016-01-24 12:41:08 +00:00
hagen
7b23d79dc2 * util.cpp : update Get*ConfigFile() : autodetect configs 2016-01-24 11:14:19 +00:00
hagen
415314a90d * update docs 2016-01-24 11:14:19 +00:00
hagen
0f7e2ad11a * Daemon_Singleton::init : rewrite setting bandwidth limit and floodfill mode 2016-01-24 11:14:19 +00:00
hagen
26d232c567 * Daemon_Singleton::init : unwrap spagetti-code 2016-01-24 11:14:19 +00:00
hagen
efa48a7e39 * tune logs 2016-01-24 11:05:16 +00:00
hagen
022642f4d5 * Config.cpp : don't try to parse config, if path is empty 2016-01-24 11:04:41 +00:00
hagen
e6e2f04a10 * Config.cpp : set default value for boolean options 2016-01-24 11:04:15 +00:00
orignal
f7e21dbe5c show tags for local destinations 2016-01-23 22:53:19 -05:00
orignal
f593802a51 I2CP option crypto.tagsToSend added for I2P tunnels 2016-01-23 20:52:21 -05:00
orignal
f545e6eb27 Merge pull request #340 from al42and/openssl-boost-asio-check
Better checking if boost::asio::buffer works with std::array
2016-01-23 13:53:12 -05:00
Andrey Alekseenko
1778d82bc3 Better checking if boost::asio::buffer works with std::array
Otherwise, had troubles with clang 3.4 and boost 1.54
2016-01-23 17:36:06 +03:00
orignal
03587d7035 changed data path back to AppData/Roaming 2016-01-22 10:56:25 -05:00
orignal
6663788612 fixed some coding style 2016-01-22 07:08:21 -05:00
hagen
ac2cb773df * I2PControl.cpp : tune logs 2016-01-22 12:04:14 +00:00
hagen
b70b3ec85b * I2PControl : drop I2P_CONTROL_ID* vars : ugly 2016-01-22 12:04:11 +00:00
orignal
1e69b8c41d Merge pull request #335 from xcps/http_remove_referer
Http remove referer
2016-01-22 07:00:44 -05:00
hagen
d5aa1a4880 * use GetOption instead hardcoded values in header * move cert/key from $DATADIR/i2pcontrol/ to $DATADIR/ 2016-01-22 11:59:15 +00:00
hagen
de0658eaab * I2PControlService::CreateCertificate : use function parameters instead direct GetPath calls 2016-01-22 11:59:13 +00:00
xcps
939c28b74b removed extra lines 2016-01-22 16:30:24 +05:00
o
c10d628a45 Merge branch 'openssl' into http_remove_referer 2016-01-22 16:13:12 +05:00
o
92830172f9 asdf 2016-01-22 16:08:54 +05:00
orignal
431af2c0dd fixed issue #331. reuse existing local detination for tunnels 2016-01-21 15:51:08 -05:00
hagen
97ca8b7ada * fix build 2016-01-21 12:59:00 +00:00
hagen
f3a7c233b3 * I2PControl.cpp : #329 2016-01-21 12:40:07 +00:00
hagen
928abf7094 - I2PControlService::LoadConfig : not used anymore 2016-01-21 07:46:17 +00:00
hagen
2cace0008e - I2PControlService::SaveConfig : not used anymore 2016-01-21 07:41:09 +00:00
hagen
db9c20f3dd * I2PControl : move boost1.49+gcc4.7 hack 2016-01-21 07:38:11 +00:00
hagen
e1a1aef990 * I2PControl : use password option from main config 2016-01-21 07:37:38 +00:00
hagen
23cf6ebc89 * add new option 'i2pcontrol.password' 2016-01-21 07:35:26 +00:00
orignal
55c279cc7e Rolled back to working Makefile.mingw 2016-01-20 21:51:18 -05:00
orignal
4e89f90c4f Merge pull request #328 from 0niichan/patch-3
add "mkdir obj/Win32" in Windows
2016-01-20 16:01:38 -05:00
0niichan
bd0eb81c1b add "mkdir obj/Win32" in Windows 2016-01-21 03:52:13 +07:00
orignal
a77a0d98e0 Merge pull request #327 from 0niichan/patch-2
new default boost' suffix; new paths
2016-01-20 14:16:44 -05:00
0niichan
e5037fc9f9 new default boost' suffix; new paths 2016-01-21 02:05:16 +07:00
orignal
7ac2022159 Merge pull request #326 from 0niichan/patch-2
Update Makefile.mingw
2016-01-20 13:39:53 -05:00
0niichan
bc41a15eba Update Makefile.mingw
new default boost' suffix; new paths
2016-01-21 01:33:28 +07:00
hagen
8aa158c1e0 * update debian/ 2016-01-20 11:32:12 +00:00
hagen
1f6f4d9c49 + docs/config_opts_after_2.3.0.md 2016-01-20 11:31:07 +00:00
hagen
3686a27c19 * update docs/configuration.md 2016-01-20 11:31:04 +00:00
hagen
1bcc311738 - drop i2p::util::config namespace : not used anymore 2016-01-20 11:30:09 +00:00
hagen
2335d3879e * migrate to new settings 2016-01-20 11:29:54 +00:00
hagen
209934ad67 * update buildsystems to include Config.cpp 2016-01-20 11:29:38 +00:00
hagen
35200a1ee5 + new cmdline & config impl 2016-01-20 11:28:57 +00:00
hagen
6c4977ee78 * tune log messages 2016-01-20 11:25:43 +00:00
orignal
5482a57c45 add clock skew to expiration 2016-01-19 11:16:50 -05:00
orignal
18914978d5 pass X-I2P_DestB32 and X-I2P-DestB64 2016-01-19 09:36:56 -05:00
hagen
36750ab900 * DaemonWin32 : separate --service (boolean) from --svcctl (string) option 2016-01-19 11:07:25 +00:00
hagen
c5f6a690de * Daemon.h : use boolean variables for flags 2016-01-19 11:07:23 +00:00
orignal
9611f80a39 check I2NP messages fro expiration 2016-01-18 21:13:43 -05:00
orignal
eb2d68fc28 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-01-18 10:29:37 -05:00
orignal
937d346676 set clove expiration time interval to 8 seconds 2016-01-18 10:29:07 -05:00
hagen
7565843fbe * move ReadConfigFile() : i2p::filesystem -> i2p::config * don't export i2p::config::mapArgs outside namespace 2016-01-18 09:05:03 +00:00
hagen
6740ec464c * unwrap i2p::util::config::* calls in Daemon.cpp 2016-01-18 09:04:25 +00:00
hagen
314e1e4bfe * unwrap i2p::util::config::* calls in ClientContext.cpp 2016-01-18 09:04:25 +00:00
hagen
45d68d89a9 * clean outdated declaration 2016-01-18 09:03:35 +00:00
hagen
1d5194a138 * drop mapMultiArgs : it's not used anywhere 2016-01-18 09:03:06 +00:00
hagen
05043f30dc * tune logs 2016-01-18 09:02:34 +00:00
orignal
cd549937c5 support multiple server tunnels with same destination and different ports 2016-01-17 18:55:09 -05:00
orignal
efdea07b7b change message expiration timeout to 8 secs (RTT) 2016-01-17 18:03:40 -05:00
orignal
06d4998d87 Merge pull request #323 from zenjy/openssl
Misc fixes
2016-01-17 14:38:27 -05:00
zenjy
02b566055e * HTTPServer.cpp: add space after "Queue size:" 2016-01-17 18:18:21 +00:00
zenjy
c312dbaac1 * Daemon.h: replace "#pragma once" with "#define" 2016-01-17 18:13:36 +00:00
orignal
b6dcb2f4c0 show streams as table (byt sha-db) 2016-01-17 11:10:56 -05:00
orignal
a85d3f2573 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-01-16 15:36:58 -05:00
orignal
0ca3fb5af0 specify and check netId 2016-01-16 15:36:30 -05:00
orignal
2a4d78d9bf wordwrapping (by sha-db) 2016-01-16 15:36:26 -05:00
orignal
d9e199092d fixed race condition 2016-01-15 16:23:03 -05:00
orignal
02bbb46d2e separate keys and destination creation 2016-01-15 14:46:29 -05:00
orignal
13ffdc6dd2 common ReadI2CPOptions 2016-01-15 12:24:40 -05:00
orignal
c8c2c4d376 Merge pull request #321 from scrrrapy/fedora-docs
Fedora/Centos docs to build from source
2016-01-15 07:13:38 -05:00
scrrrapy
01f7343781 added instructions to build on Fedora/Centos 2016-01-15 03:14:44 +00:00
scrrrapy
3acc244692 reordered unix targeted documentation to be more user-friendly
Also fixed wrong build root.
2016-01-15 03:14:40 +00:00
orignal
094068e4ff Merge pull request #320 from 0niichan/patch-1
Update util.cpp
2016-01-14 19:46:24 -05:00
0niichan
ec958697e2 Update util.cpp
change i2pd home data path for Windows
2016-01-15 07:44:26 +07:00
orignal
208e8f8247 new webconsole style by sha-db 2016-01-14 19:05:46 -05:00
orignal
3d4890a28b handle I2CP keys correctly 2016-01-14 18:45:47 -05:00
orignal
fe4362f459 tunnel parameters 2016-01-14 15:57:55 -05:00
orignal
81d3ad2d35 Update README.md 2016-01-14 11:06:05 -05:00
orignal
ffb8c3e53c Update README.md 2016-01-14 07:47:58 -05:00
orignal
2d4d2374e3 Update README.md 2016-01-14 07:47:04 -05:00
orignal
09f31a9278 Merge pull request #319 from scrrrapy/travis-ci
Travis-CI integration
2016-01-14 07:19:40 -05:00
scrrrapy
80b0a3cdec fixed travis badge branch 2016-01-14 10:16:42 +00:00
scrrrapy
c533bfc83d integration with travis-ci 2016-01-14 02:45:06 +00:00
orignal
8fa053f7c7 show I2P tunnels at web console 2016-01-13 20:21:53 -05:00
orignal
b152bb26e3 more parameters 2016-01-13 09:42:06 -05:00
orignal
a0816b04e5 purple links, coloured tunnels 2016-01-12 20:31:25 -05:00
hagen
0819517902 * Addressbook : readable http-req assembly 2016-01-12 23:34:59 +00:00
hagen
55ea8c82e9 * Addressbook : logging 2016-01-12 23:32:32 +00:00
hagen
ffbbf88de4 * DaemonLinux : restore old behaviour: always write pidfile by default, but allow override path 2016-01-12 23:31:01 +00:00
orignal
e2ff49825f favicon added 2016-01-12 13:18:01 -05:00
orignal
7f325827c4 Merge pull request #317 from l-n-s/openssl
Prepare documentation for Read the docs
2016-01-12 11:30:16 -05:00
orignal
cae9ccfda1 version 2.3.0 2016-01-12 10:14:22 -05:00
orignal
248ae7d4d5 do nothing upon SIGHUP for now 2016-01-12 10:12:55 -05:00
orignal
7f08bbe938 handle -pidfile parameter correctly 2016-01-12 10:00:17 -05:00
orignal
81b2f2114d purplei2p webconsole style(by sha-db) 2016-01-12 09:36:01 -05:00
orignal
5eee430be3 fixed typo 2016-01-12 09:34:46 -05:00
libre-net-society
623edf3bc9 Tuning docs for Sphinx 2016-01-12 07:18:17 +03:00
libre-net-society
bd4a224051 Added Sphinx documentation files 2016-01-12 07:03:29 +03:00
orignal
870e84a700 new webconsole layout (by nda) 2016-01-11 22:05:10 -05:00
orignal
8d4fae24ea fixed misalignment 2016-01-11 19:03:04 -05:00
orignal
7a84daf3f7 temporary disable openssl mutexes 2016-01-11 17:37:20 -05:00
orignal
7968279bc2 send X-I2P-DestHash 2016-01-11 13:48:18 -05:00
orignal
258be40285 notransit parameter added 2016-01-11 11:08:06 -05:00
hagen
b2ae30eba1 * fix cmake output library name (#315) 2016-01-11 11:31:55 +00:00
hagen
daaba1dbc0 * prevent zero-division exception when running offline 2016-01-11 11:31:02 +00:00
hagen
a3c6ed4dd2 * fix warnings from -Wunused-result 2016-01-11 11:31:02 +00:00
hagen
e4255ed712 + add --pidfile cmdline option 2016-01-11 11:31:02 +00:00
hagen
5d510f1cf4 * DaemonLinux : set umask to 0027 instead 0000 2016-01-11 10:55:50 +00:00
hagen
1819bd910a * add log message when fork failed 2016-01-11 10:55:18 +00:00
hagen
43eecdbb3f * update default tunnels.cfg 2016-01-11 02:55:21 +00:00
hagen
108c1bcac4 * update docs/configuration.md 2016-01-11 02:52:16 +00:00
hagen
4b7e5864d4 * cleanup default config file 2016-01-11 02:52:00 +00:00
hagen
fb1d2abbfa * cleanup manpage 2016-01-11 02:51:35 +00:00
orignal
0c290e65ef removed deprecated parameters 2016-01-10 21:39:29 -05:00
orignal
5487fad2ce fixed race conditin 2016-01-10 18:55:00 -05:00
orignal
d41f930f69 fixed unintialized reply key 2016-01-10 16:40:28 -05:00
orignal
595b2619fd fixed misalignment for timestamp 2016-01-09 19:24:52 -05:00
orignal
26d305d866 fixed misalignment of certificate length 2016-01-09 17:26:17 -05:00
orignal
c9d95ff161 eliminate one extra multipilication 2016-01-08 10:21:43 -05:00
orignal
9cc592b564 correct buffer size for deflate 2016-01-05 14:50:45 -05:00
orignal
ff48422ec0 check I2NP message buffer size 2016-01-05 14:29:18 -05:00
orignal
a26c5f85c3 ignore LeaseSets coming from transit tunnels 2016-01-04 19:56:46 -05:00
orignal
727436e1cf specify signature type for I2P tunnels 2016-01-03 21:43:43 -05:00
orignal
d1c57a1872 New bandwidth values 2016-01-03 19:15:12 -05:00
orignal
b7c021af8c clear extra bandwidth bit 2016-01-03 09:54:03 -05:00
orignal
7149b509d7 extra bandwidth caps 2016-01-02 22:17:04 -05:00
orignal
45e7111dda publish stats for floodfill 2016-01-02 17:23:20 -05:00
orignal
9fc69db9eb reserve extra 16 bytes for padding 2016-01-01 17:39:12 -05:00
orignal
2ba314d9d9 count checksum and padding for buffer size 2016-01-01 15:41:18 -05:00
orignal
f35660c8e2 fixed windows build 2016-01-01 08:30:38 -05:00
orignal
68b1fe8631 use TUNNEL_DATA_ENCRYPTED_SIZE for tunnel encryption 2015-12-31 19:46:14 -05:00
orignal
4242c86d40 check for buffer overflow during flood 2015-12-31 17:09:04 -05:00
orignal
ef4dc3cbc9 fixed race condition of openssl calls 2015-12-31 16:02:10 -05:00
orignal
8daa7561fa pass ident hash by values to RequestComplete 2015-12-31 11:21:01 -05:00
orignal
2cc3dfc2ce fixed windows build 2015-12-28 12:26:10 -05:00
orignal
459800568a fixed windows build 2015-12-28 11:55:55 -05:00
orignal
3a35b84b03 fixed FreeBSD build 2015-12-28 10:52:02 -05:00
orignal
79cfa52bf9 fixed windows build 2015-12-28 08:30:40 -05:00
hagen
a0e8fe5848 * implement --loglevel option 2015-12-28 10:54:00 +00:00
hagen
2dae5bccb2 * util.cpp : reorder defines 2015-12-28 10:54:00 +00:00
hagen
8e867ab0c0 * util.cpp : reorder defines 2015-12-28 10:54:00 +00:00
hagen
1b2c88fe38 * drop i2p::util::config::GetCharArg 2015-12-28 10:54:00 +00:00
hagen
f3bee5ff3f * log message fix 2015-12-28 10:53:48 +00:00
orignal
196d7e8f72 send correct RouterInfo statistics 2015-12-26 09:41:12 -05:00
orignal
16596c18fb log max packet size 2015-12-23 20:47:44 -05:00
hagen
7ea3a87bfc * missing initializer for member ‘i2p::transport::Peer::delayedMessages‘ 2015-12-24 00:55:53 +00:00
hagen
a57905b6cd * merged branch 'sane-log-messages' 2015-12-24 00:46:32 +00:00
orignal
f9c592ca22 static link against libgcc, libstdc++ and libwinpthread 2015-12-23 10:24:10 -05:00
hagen
aecac0ef85 * bump version in debian/changelog 2015-12-23 01:32:01 +00:00
orignal
ca315c51a0 version 2.2.0 2015-12-21 20:49:27 -05:00
orignal
45c8858140 persist temporary keys 2015-12-21 10:17:00 -05:00
orignal
06e45bff24 removed unused parameter 2015-12-21 09:33:09 -05:00
Mikhail Titov
2635a658d0 Fix missing cached openssl in appveyor 2015-12-20 22:57:55 -06:00
hagen
f48a98f691 * disable AESNI by default for .deb package (#312) 2015-12-21 03:48:35 +00:00
hagen
3badda95c1 * reseed now https only 2015-12-21 03:46:35 +00:00
hagen
364ccc05d5 * Log.h: drop unused template 2015-12-21 03:00:12 +00:00
hagen
d09fedf208 * sane log messages: TransitTunnel.cpp 2015-12-21 03:00:12 +00:00
hagen
7936f8730f * sane log messages: Reseed.cpp 2015-12-21 03:00:12 +00:00
hagen
6c0dfc4356 * sane log messages: Log.cpp 2015-12-21 03:00:12 +00:00
hagen
d9af8c31a2 * sane log messages: LeaseSet.cpp 2015-12-21 03:00:12 +00:00
hagen
ca375314f0 * sane log messages: Identity.cpp 2015-12-21 03:00:12 +00:00
hagen
5266d4d79c * sane log messages: RouterInfo.cpp 2015-12-21 03:00:12 +00:00
hagen
1cb0826de0 * sane log messages: SSUData.cpp 2015-12-21 03:00:12 +00:00
hagen
89e3178ea3 * sane log messages: HTTPServer.cpp 2015-12-21 03:00:12 +00:00
hagen
3b5d9d6cee * sane log messages: RouterContext.cpp 2015-12-21 03:00:12 +00:00
hagen
ce4ed19029 * sane log messages: SSU.cpp 2015-12-21 03:00:11 +00:00
hagen
01a502339c * sane log messages: api.cpp 2015-12-21 03:00:11 +00:00
hagen
642d0e6f74 * sane log messages: Streaming.cpp 2015-12-21 03:00:11 +00:00
hagen
d9e659deb0 * sane log messages: Destination.cpp 2015-12-21 03:00:11 +00:00
hagen
830fe7f9b8 * sane log messages: Transports.cpp 2015-12-21 03:00:11 +00:00
hagen
3e8c247c05 * sane log messages: ClientContext.cpp 2015-12-21 03:00:11 +00:00
hagen
16880074fa * sane log messages: DaemonWin32.cpp 2015-12-21 03:00:11 +00:00
hagen
19c74ce9fa * sane log messages: DaemonLinux.cpp 2015-12-21 03:00:11 +00:00
hagen
56ef0dad9c * sane log messages: Daemon.cpp 2015-12-21 03:00:11 +00:00
hagen
8d99808821 * sane log messages: I2PTunnel.cpp 2015-12-21 03:00:11 +00:00
hagen
1cb08fdecc * sane log messages: util.cpp 2015-12-21 03:00:11 +00:00
hagen
e8952d7e02 * sane log messages: TunnelPool.cpp 2015-12-21 03:00:11 +00:00
hagen
18fad9c9d9 * sane log messages: Garlic.cpp 2015-12-21 03:00:11 +00:00
hagen
89a0a94f3e * sane log messages: SAM.cpp 2015-12-21 03:00:11 +00:00
hagen
0859cf30f8 * sane log messages: UPnP.cpp 2015-12-21 03:00:11 +00:00
hagen
a0fe02a560 * sane log messages: BOB.cpp 2015-12-21 03:00:11 +00:00
hagen
3156f7dacd * sane log messages: Tunnel.cpp 2015-12-21 03:00:11 +00:00
hagen
c3958bf042 * sane log messages: NTCPSession.cpp 2015-12-21 03:00:11 +00:00
hagen
facc5f8aa7 * sane log messages: SSUSession.cpp 2015-12-21 03:00:11 +00:00
hagen
8170257c26 * sane log messages: AddressBook.cpp 2015-12-21 03:00:11 +00:00
hagen
489e37b2a1 * sane log messages: NetDb.cpp 2015-12-21 03:00:11 +00:00
hagen
4899e0d2d5 * sane log messages: I2NPProtocol.cpp 2015-12-21 03:00:11 +00:00
Mikhail Titov
762f9c4b23 Merge pull request #308 from mlt/openssl-cmake
Cleaned up appveyor CI along with MSVC & mingw builds
2015-12-20 13:38:12 -06:00
Mikhail Titov
6d3dac0ec1 Windows build status badge update in README.md 2015-12-19 22:19:06 -06:00
Mikhail Titov
f684815272 Build miniupnpc.dll on Appveyor 2015-12-19 22:19:05 -06:00
Mikhail Titov
8e04218c95 Install optional miniupnpc.dll if exist 2015-12-19 22:19:04 -06:00
Mikhail Titov
23cb45454b Set default install folder for CMake based NSIS 2015-12-19 22:19:02 -06:00
Mikhail Titov
7fc9a161b1 Default NSIS template from CMake 2015-12-19 22:19:01 -06:00
Mikhail Titov
95a5473051 Initial CMake based packaging 2015-12-19 22:19:00 -06:00
Mikhail Titov
66ceb573dc Update Windows build docs 2015-12-19 22:18:59 -06:00
Mikhail Titov
5f8223ebb5 Patch for 64-bit zlib build with MSVC assembly 2015-12-19 22:18:58 -06:00
Mikhail Titov
51146d4152 MSVC optimization & hardening 2015-12-19 22:18:57 -06:00
Mikhail Titov
3334281949 Search for patch tool with CMake
This is to enable static build of "bundled" zlib with MSVC
2015-12-19 00:03:32 -06:00
Mikhail Titov
8e85d9ac00 Sync Windows VERSIONINFO resource with version.h 2015-12-19 00:03:31 -06:00
Mikhail Titov
e1c69a6250 Transparency in icon 2015-12-19 00:03:30 -06:00
Mikhail Titov
edd9a18257 Cleanup some CMake msys specifics
* Exclude MSYS from -z relro
* WIN32_LEAN_AND_MEAN
2015-12-19 00:03:28 -06:00
Mikhail Titov
65f993677f Remove unnecessary thread & chrono Boost libs
Prevent boost thread auto-link erroneous attempts with MSVC
2015-12-19 00:03:27 -06:00
Mikhail Titov
bc775140bb appveyor.yml 2015-12-19 00:03:26 -06:00
Mikhail Titov
4b2bd6e18f Include dir for precompiled headers with gcc 2015-12-19 00:03:24 -06:00
orignal
c36a810bcb ignore extended options for SessionCreated and SessionConfirmed 2015-12-18 11:52:44 -05:00
orignal
a994bbc36b call CryptoConstants from Crypto.cpp only 2015-12-18 10:09:25 -05:00
hagen
c3238f4d0b * fix warnings of type mismatch (#298) 2015-12-17 08:10:17 +00:00
hagen
632d26e398 * update maintainer scripts 2015-12-17 07:16:26 +00:00
hagen
214cc8b810 * install reseed certs 2015-12-17 05:00:03 +00:00
hagen
8f218141f4 * add 'i2pd' user (#313) 2015-12-17 04:39:08 +00:00
hagen
3676304751 * provide default subscriptions.txt 2015-12-17 04:39:08 +00:00
hagen
c605fd57aa * AddressBook.cpp : mistype in log message 2015-12-17 04:39:08 +00:00
orignal
4599f6919c shared_ptr for local destination in TunnelPool 2015-12-16 14:52:48 -05:00
hagen
8ad20c0db3 * allow parallel builds (#310) 2015-12-16 01:12:14 +00:00
hagen
638a69e5f0 * fix comments in default config (#311) 2015-12-16 00:19:52 +00:00
orignal
9fa6b1ebe1 keep pending incoming streams if acceptor is not set 2015-12-14 22:36:23 -05:00
orignal
5930e2d221 keep pending incoming streams if acceptor is not set 2015-12-14 22:23:28 -05:00
orignal
fdd96975fb cancel destination request 2015-12-13 14:40:43 -05:00
orignal
de6dd77046 use shared_ptr for LeaseSet request 2015-12-13 10:51:43 -05:00
orignal
1b6ad8413e spread addresses between subdirectories 2015-12-11 15:48:33 -05:00
orignal
6096d572f3 handle RelayResponse 2015-12-09 22:17:43 -05:00
orignal
badcd64b62 print full tunnel path 2015-12-09 19:07:12 -05:00
orignal
a7b8b52dbd fixed crash 2015-12-09 18:01:42 -05:00
orignal
d89f0f51df show full tunnel path 2015-12-09 10:35:04 -05:00
orignal
be358f3f2e enable RI catch for OBEP back 2015-12-09 10:03:51 -05:00
orignal
f122da1485 change and save I2PControl password 2015-12-08 10:40:43 -05:00
hagen
0dda4728b6 * update README 2015-12-08 12:18:25 +00:00
hagen
45fd95e02b * update default/i2pd and traditional init script 2015-12-08 11:38:28 +00:00
hagen
91aa2d7f6f + add example config files 2015-12-08 11:38:28 +00:00
hagen
a96b7d2a80 * drop patch not needed anymore 2015-12-08 11:38:28 +00:00
hagen
8f9cea54c5 * rename main binary 2015-12-08 11:38:28 +00:00
orignal
045558bede correct path to openssl 2015-12-06 22:48:08 -05:00
hagen
58124ebaab * update debian/docs 2015-12-05 11:55:27 +00:00
hagen
0c87dd5624 * added debian/logrotate (thanks to kytv) 2015-12-05 11:55:27 +00:00
hagen
b87f986a49 * added manpage (thanks to kytv) 2015-12-05 11:55:27 +00:00
hagen
c6a6035bb9 * debian/control : compat level -> 9 2015-12-05 07:56:51 +00:00
orignal
1ef12f0645 updated reseeders list 2015-12-04 14:59:31 -05:00
orignal
ef3ec33ba3 create all subdirectories for non-case sensitive systems 2015-12-04 14:06:37 -05:00
orignal
c82ef1ee8f link against openssl for Mac OS X 2015-12-04 13:19:08 -05:00
Mikhail Titov
23b8a60242 Appveyor status badge 2015-12-03 23:39:09 -06:00
Mikhail Titov
ac9511165e Merge pull request #307 from mlt/openssl-cmake
Better CMake support for MSVC builds
2015-12-03 23:35:14 -06:00
Mikhail Titov
9d70851eb9 Respect static for zlib with CMake 2015-12-03 23:23:26 -06:00
Mikhail Titov
759dfb28ce Increase PCH heap limit for MSVC 2015-12-03 23:07:31 -06:00
Mikhail Titov
ff356b1f21 Use assembly language when building zlib for MSVC++ 2015-12-03 23:07:30 -06:00
Mikhail Titov
b2a6c1bc68 fixup! read Content-Length from http header
MSVC++ complains on ssize_t
2015-12-03 23:07:29 -06:00
Mikhail Titov
76549d0a4a Fix win32 resource compilation with msys
squash! Fix win32 resource

winres.h is missing for mingw
2015-12-03 23:07:27 -06:00
Mikhail Titov
e5c72cae83 Fix CMake stuff for msys2 2015-12-03 23:07:26 -06:00
orignal
bf47df46c9 allow DNS names for SSU 2015-12-03 15:45:01 -05:00
orignal
0ef42870e5 try SSU if NTCP address is not presented 2015-12-02 12:48:10 -05:00
orignal
da8a6a4c2b make sure to use ipv4 introducers only 2015-12-01 09:21:02 -05:00
orignal
988007a8c9 pass correct pointer to sessions table 2015-11-30 19:45:57 -05:00
hagen
710439e83c * cleanup README
* move all docs to single dir
2015-12-01 00:06:00 +00:00
hagen
80a0a3d4fb * BUILD_NOTES.md : add hints for building deb 2015-12-01 00:02:59 +00:00
hagen
43299aea10 * BUILD_NOTES.md : update 2015-12-01 00:02:59 +00:00
hagen
f5aea766a7 * move 'Requirements' sections to BUILD_NOTES.md 2015-12-01 00:02:59 +00:00
orignal
c5308e3f2f separate SSU sessions lists for V4 and V6 2015-11-30 15:53:07 -05:00
orignal
2b8e662f81 connect through introducer in v4 thread 2015-11-30 14:59:32 -05:00
orignal
0a6d849435 pass shared_ptr to SendRelayIntro 2015-11-30 10:23:05 -05:00
orignal
a0106fe5d8 Merge pull request #306 from erlend1/openssl
Configurable addresses from master
2015-11-30 10:20:19 -05:00
erlend1
cee1b8a64a Configurable addresses from master 2015-11-30 16:44:32 +02:00
orignal
4e2ba71d59 more introducers 2015-11-29 17:25:42 -05:00
orignal
fb2bdfb9ee create SSU session in SSU thread 2015-11-29 09:10:49 -05:00
orignal
72785f6740 eliminate some unnecessary calculations 2015-11-27 22:16:10 -05:00
orignal
a94a05fac9 replaced radix-16 to radix-256 2015-11-27 19:02:54 -05:00
orignal
430368de97 temporary disable Ed25519 per thread 2015-11-27 15:46:30 -05:00
orignal
7bfb499549 reduce number of precalculated points 2015-11-27 13:19:45 -05:00
hagen
9bc477e1b6 * use stricter linker options for .deb packages 2015-11-27 14:02:19 +00:00
hagen
f84ac18472 * set defaults to *FLAGS instead redefining them 2015-11-27 14:01:15 +00:00
hagen
cd515a2e54 * fix Depends: for i2pd-dbg 2015-11-27 14:01:15 +00:00
hagen
c73c8fdc47 * fix building of empty -dbg package 2015-11-27 13:35:24 +00:00
hagen
e755a32b23 * take some enchancements for debian/ dir from kytv (#1) 2015-11-27 12:09:31 +00:00
hagen
d4d1768575 * Makefile.* : fix build with gcc 4.7.2 (#299) 2015-11-27 11:42:44 +00:00
orignal
0a5745c559 Update README.md 2015-11-26 21:04:19 -05:00
orignal
b24959205b Update README.md 2015-11-26 21:03:58 -05:00
orignal
d69f297c05 split between CreateSession and CreateSessionThrough Introducer 2015-11-26 16:20:24 -05:00
orignal
3c8e331809 Ed25519 per thread 2015-11-26 14:00:40 -05:00
orignal
d169471e8c copy constructor for Ed22519 2015-11-26 13:31:30 -05:00
orignal
56453f6b5c moved BN_CTX creation to curve's Verify and Sign 2015-11-26 10:25:51 -05:00
orignal
dac2e8c79e use left sift instead multipilication by 2 2015-11-26 09:48:06 -05:00
orignal
ccc96bc610 Merge pull request #304 from hagen-i2p/fixes
Misc fixes
2015-11-26 07:05:46 -05:00
hagen
654371cb6a fix debian/ directory 2015-11-26 11:24:28 +00:00
hagen
1af8d873bb delete build/cmake_modules/FindCryptoPP.cmake (now using openssl) 2015-11-26 11:24:02 +00:00
hagen
b7a0e23309 fix BUILD_NOTES: url, md-format, crypto++ reference 2015-11-26 11:23:32 +00:00
hagen
4a0f868941 fix Dockerfile : drop crypto++, add openssl 2015-11-26 11:23:30 +00:00
hagen
448073cdd6 format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'size_t {aka unsigned int} 2015-11-26 11:23:27 +00:00
orignal
ad79ec7b1f async handshake 2015-11-25 13:11:02 -05:00
orignal
e194854c6d replace GetSession to CreateSession 2015-11-25 12:51:35 -05:00
orignal
d01d033209 eliminate session creation collision 2015-11-25 11:51:35 -05:00
orignal
06c4aca490 always use shared_ptr for I2NPMessage 2015-11-24 13:09:12 -05:00
orignal
885d57138a read Content-Length from http header 2015-11-23 19:47:08 -05:00
orignal
9e2a770a26 read complete request 2015-11-23 16:40:06 -05:00
orignal
942b699bb9 fixed few SSL errors 2015-11-23 14:48:56 -05:00
orignal
c9d03a8094 I2PControl through SSL 2015-11-23 13:22:02 -05:00
orignal
d015538bb4 create certificate for https 2015-11-23 11:56:00 -05:00
orignal
90d6c5c5bb fixed race condition 2015-11-23 09:26:32 -05:00
orignal
387ce4b6fa fixed access to eepsites from webconsole 2015-11-22 21:02:02 -05:00
orignal
7943b13891 use shared_ptr for sockets 2015-11-22 17:01:37 -05:00
orignal
50a7cd19b4 Update README.md 2015-11-22 16:48:38 -05:00
orignal
53e9335bb0 Update README.md 2015-11-22 16:47:55 -05:00
orignal
e5cb70972e moved status_string to reply structure 2015-11-22 10:58:57 -05:00
orignal
0d84871037 backport of 'make http server http/1.1 compliant' 2015-11-21 17:26:12 -05:00
orignal
1d37745c0c more separation between api and executable builds 2015-11-21 17:04:40 -05:00
orignal
ad9ade7849 reduce number of transient BIGNUM allocations 2015-11-20 21:27:16 -05:00
orignal
c1e2ee32b4 fixed mingw build error 2015-11-20 12:34:53 -05:00
orignal
1588d2734c use path.string () instead path.c_str () 2015-11-20 12:30:20 -05:00
orignal
50dda4263f fixed mingw build error 2015-11-20 11:42:38 -05:00
orignal
a8f2239495 backport GetMTU 2015-11-20 10:36:04 -05:00
orignal
c42636b0ee check for zero-length 2015-11-20 10:10:13 -05:00
orignal
54b2c8bd7e backport fix build for clang 2015-11-20 10:02:54 -05:00
orignal
d01a21a867 backport openbsd support 2015-11-20 09:55:34 -05:00
orignal
5d43052c05 Merge pull request #296 from edwtjo/make-tunnelscfg-configurable-again
Make tunnels.cfg configurable
2015-11-20 09:08:26 -05:00
Edward Tjörnhammar
4109ab1590 Make tunnels.cfg configurable 2015-11-20 13:59:00 +01:00
orignal
f6eabd695b don't store B explicitly 2015-11-19 22:38:18 -05:00
orignal
24d9dacfd9 fixed mingw build 2015-11-19 21:02:55 -05:00
orignal
66d51a9eb1 0.9.23/2.1.0 version update 2015-11-19 09:26:38 -05:00
orignal
302df75d83 skip extended options in SSU header 2015-11-16 13:27:27 -05:00
orignal
11b7e637e9 fixed complation error for boost 1.49 and gcc 4.7 and higher 2015-11-12 15:39:48 -05:00
orignal
135c92bd85 Merge pull request #295 from mlt/openssl-cmake
Bring CMake stuff in agreement with #294 discussion
2015-11-12 07:18:26 -05:00
Mikhail Titov
c15c26a233 Bring CMake stuff in agreement with #294 discussion 2015-11-11 21:24:53 -06:00
orignal
5d94760cce eliminate some transient BIGNUM allocations 2015-11-11 16:24:56 -05:00
orignal
79517a0ba3 fixed clobbed z and t for Double 2015-11-11 15:19:00 -05:00
orignal
64295e3541 Merge pull request #294 from mlt/openssl-cmake
Updated CMake stuff to build OpenSSL version with MSVC++
2015-11-11 15:03:47 -05:00
Mikhail Titov
cc2816aaf5 Use OpenSSL & zlib in precompiled headers 2015-11-11 13:48:49 -06:00
Mikhail Titov
4a2fcb9deb Use OpenSSL & zlib with CMake instead of Crypto++ 2015-11-11 13:46:29 -06:00
Mikhail Titov
7f27580f1b Proper miniupnpc CMake detection 2015-11-11 13:45:37 -06:00
Mikhail Titov
94d0915004 Reorder ssl/boost includes to avoid winsock complains 2015-11-11 13:44:52 -06:00
Mikhail Titov
88db99e593 Minor omissions
* Missing UPnP namespace
* Public key pointer dereferencing for MSVC
* Redundant WIN32_LEAN_AND_MEAN found in Makefile.mingw as well
2015-11-11 13:21:52 -06:00
orignal
593b25a5cd fix build error 2015-11-11 13:32:58 -05:00
orignal
5c58bf44c0 Makefile.mingw added 2015-11-10 14:09:26 -05:00
orignal
73ae6cf164 (h*a)%l for signing 2015-11-09 14:41:04 -05:00
orignal
7749319c75 h%l for verification 2015-11-07 18:07:59 -05:00
orignal
73037b86ac fixed build for gcc 4.6 and boost 1.46 2015-11-06 09:01:02 -05:00
orignal
d50ba1259c calculations in projective coordinates 2015-11-05 15:02:10 -05:00
orignal
962261fee7 EdDSA speed improvement 2015-11-04 13:48:30 -05:00
orignal
4dea2ef1a4 use EdDSA as default for RouterInfo 2015-11-03 13:05:37 -05:00
orignal
aa12eb4ed4 removed autotools build 2015-11-03 09:37:08 -05:00
orignal
8a75363784 Update README.md 2015-11-03 09:34:44 -05:00
orignal
01dd982587 Update README.md 2015-11-03 09:22:09 -05:00
orignal
62cf83921b cumulative update from bitbucket 2015-11-03 09:15:49 -05:00
orignal
73d4025256 version 0.10.0 2015-07-06 12:11:17 -04:00
orignal
3405ffd8d8 check for buffer size 2015-07-05 07:59:38 -04:00
orignal
e03f1597a0 don't send DatabaseStore until time sync complete 2015-07-03 21:50:26 -04:00
orignal
c5644e0e32 const I2NP messages 2015-07-03 21:27:40 -04:00
orignal
bf14b7da9a move FillI2NPMessageHeader into I2NPMessage 2015-07-03 11:11:07 -04:00
orignal
0c8fb376db some cleanup 2015-07-03 10:11:55 -04:00
orignal
17acdcc4d5 temporary fix of crash 2015-07-02 14:11:30 -04:00
orignal
654357f5ce copy shared_ptr 2015-07-02 13:43:03 -04:00
orignal
fbebdd3055 fixed race condition 2015-07-01 17:20:41 -04:00
orignal
83e76c6b53 use shared flood message 2015-07-01 14:13:42 -04:00
orignal
adf12b6084 handle DeliveryStatus garlic clove directly 2015-06-29 21:40:43 -04:00
orignal
047c6a93a3 don't copy transit DatabaseStore 2015-06-27 22:02:00 -04:00
orignal
bf4c33325c random non-zero padding 2015-06-26 16:06:59 -04:00
orignal
be1a4548e6 pass const I2NP message to HandleTunnelDataMsg 2015-06-25 21:49:16 -04:00
orignal
d8cd2afd12 different input anf output I2NP message for tunnel encryption 2015-06-24 22:19:56 -04:00
orignal
6ff3f8df87 Merge pull request #218 from mlt/fix208
Check for invalid SAM destination
2015-06-24 15:49:25 -04:00
Mikhail Titov
95c4a87ccc Check for invalid SAM destination
This closes #208
2015-06-24 14:20:16 -05:00
orignal
206f094dd4 use shared_ptr for DeliverStatus 2015-06-24 10:45:58 -04:00
orignal
a05a20440e deleted deprecated SendMessage 2015-06-24 10:25:05 -04:00
orignal
ff12421d60 shared_ptr for lookup messages 2015-06-22 15:47:45 -04:00
orignal
2cbd6e85c6 use shared_ptr for garlic messages 2015-06-21 22:29:50 -04:00
orignal
1fc50a59f5 different in and out buffers for tunnel encryption 2015-06-21 17:05:01 -04:00
orignal
9c9401ce2f use shared_ptr for all incoming I2NP messages 2015-06-21 15:08:22 -04:00
orignal
f732a84a7c Merge pull request #214 from mlt/cmake-upnp-libdl
Missing libdl for UPnP
2015-06-20 14:39:41 -04:00
Mikhail Titov
efe7e469ce Missing libdl for UPnP 2015-06-20 12:16:36 -05:00
orignal
ed136c9d8b Merge pull request #213 from mlt/fix-upnp
fixup! Fix UPnP for Win32
2015-06-20 06:56:01 -04:00
Mikhail Titov
60e2722a21 fixup! Fix UPnP for Win32 2015-06-20 00:50:12 -05:00
orignal
4fab07b4da fixed build error 2015-06-19 16:06:14 -04:00
orignal
d07c68bd9a Merge pull request #210 from mlt/fixes
Few fixes
2015-06-19 16:00:12 -04:00
Mikhail Titov
2738169a9d Use static for now while returning HTTP 500 error 2015-06-19 14:47:44 -05:00
Mikhail Titov
490b65dfe2 Materialize temporary string obtained from boost path 2015-06-19 14:47:33 -05:00
Mikhail Titov
38ebe28923 Rearrange eol removal for handshake 2015-06-19 14:47:20 -05:00
orignal
4ed7e29896 use shared_ptr for I2NP messages through tunnels 2015-06-19 14:38:31 -04:00
orignal
122b8c2a84 use shared_ptr for transit tunnel participant 2015-06-17 12:31:28 -04:00
orignal
98c91a01e3 use shared_ptr for outbound tunnel build messages 2015-06-17 12:26:07 -04:00
orignal
a7cd16c159 use shared_ptr for direct DatabaseLookup message 2015-06-17 12:25:02 -04:00
orignal
5ca86b87f5 create shared I2NP tunnel message in OBGW 2015-06-17 12:08:06 -04:00
orignal
25a163cdeb send I2NP messages as shared_ptr 2015-06-17 11:41:07 -04:00
orignal
3a63f6775a pass I2NP message to transport session as shared_ptr 2015-06-17 10:47:26 -04:00
orignal
d65257c7b0 pass I2NP as shared_ptr to netDB 2015-06-16 13:32:42 -04:00
orignal
465945f8a8 more generic queue 2015-06-16 13:14:33 -04:00
orignal
a0de60e179 use share_ptr for garlic messages 2015-06-16 10:14:14 -04:00
orignal
b48682012d verify adler checksum 2015-06-14 10:37:15 -04:00
orignal
e624cb31bd Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-06-11 11:43:58 -04:00
orignal
20e43951e5 reduce CPU usage 2015-06-11 11:43:35 -04:00
orignal
576802a1d6 Merge pull request #202 from mlt/fix201
This closes #201
2015-06-10 17:18:36 -04:00
Mikhail Titov
23a3d48611 This closes #201 2015-06-10 16:11:13 -05:00
orignal
b6ec0a3526 Merge pull request #200 from mlt/cmake
Cmake: fix static, precompiled headers, fix crypto++ include dir search
2015-06-10 15:40:42 -04:00
orignal
ef6a038451 handle explicitPeers I2CP parameter 2015-06-10 15:32:55 -04:00
Mikhail Titov
0354685e35 Precompiled headers
Sample times:
MSVC 2013, debug x64: 5min 15sec -> 2min 15sec
Ubuntu 15.04, with hardening, static, release: 5min 21sec -> 3min 24sec
2015-06-10 13:41:08 -05:00
Mikhail Titov
ba2b792916 Cleanup cryptopp headers path search 2015-06-10 13:41:07 -05:00
Mikhail Titov
44768e92ad CMake: fix static builds, add LTO for MinSizeRel 2015-06-10 13:41:06 -05:00
orignal
0e8bdf8299 fixed race condition 2015-06-09 22:14:31 -04:00
orignal
09298d7457 changed profiling algorithm 2015-06-09 14:04:25 -04:00
orignal
e8d80e16ba very hash in one pass 2015-06-09 13:02:37 -04:00
orignal
e461982a31 support multiple transport sessions to the same peer 2015-06-09 11:00:37 -04:00
orignal
c896f6d0d7 select first hop for inbound tunnel from connected peers 2015-06-07 08:37:34 -04:00
orignal
9a9b38a8c3 Merge pull request #199 from mlt/cmake-msvc
MSVC specific debug symbols don't belong to other platforms
2015-06-06 21:47:49 -04:00
orignal
b26b52cca8 Merge pull request #198 from mlt/upnp
Fix UPnP for Win32
2015-06-06 21:47:28 -04:00
Mikhail Titov
b5ee997da9 MSVC specific debug symbols don't belong to other platforms 2015-06-06 14:16:29 -05:00
Mikhail Titov
046ffd8648 Fix UPnP for Win32
* find_package for headers
* Swap includes order to pass compilation with MSVC 2013
* Enforce SO address resolution checks
* Change SO/DLL name on Windows
* Portable sleep from C++11

This closes #186
2015-06-06 13:53:22 -05:00
orignal
d7e7823606 Merge pull request #197 from mlt/cmake-msvc
Fix Win32 build with CMake and MSVC
2015-06-06 14:19:38 -04:00
Mikhail Titov
2d3493a225 Perhaps bitness detection is an introspection
http://www.cmake.org/cmake/help/v3.0/command/find_library.html
2015-06-06 12:34:06 -05:00
Mikhail Titov
a3b08c0016 Fix Win32 build with CMake and MSVC 2015-06-06 12:21:35 -05:00
orignal
d9c0f52846 don't pick node for 5 minutes if declined 2015-06-05 22:09:16 -04:00
orignal
a96482b186 skip missing sections 2015-06-05 21:15:02 -04:00
orignal
10e78785cd additional statistics for profiling 2015-06-05 15:55:21 -04:00
orignal
da56397b39 fixed bug with zero-size clove 2015-06-04 11:31:22 -04:00
orignal
abc05b4485 version 0.9.20 2015-06-04 09:54:46 -04:00
orignal
09fd0baf78 replace Host: for server http tunnels 2015-06-03 12:30:15 -04:00
orignal
d7deb938c5 catch HTTP header of HTTP server tunnel connection 2015-06-02 16:21:38 -04:00
orignal
68834df271 use addresses in server tunnel configuration 2015-06-02 13:18:41 -04:00
orignal
8a3c276e66 I2PTunnelConnectionHTTP added 2015-06-02 13:03:22 -04:00
orignal
6a043649f5 use random msg_id for I2NP messages 2015-05-27 13:35:54 -04:00
orignal
019af7bd3a http server tunnel added 2015-05-20 16:00:09 -04:00
orignal
4f2f67d5b1 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-05-17 19:40:57 -04:00
orignal
2a59ae294d check length of garlic message 2015-05-17 19:40:46 -04:00
6d586bde6c Note that Boost 1.58 works 2015-05-14 08:29:17 +00:00
orignal
9510bba3b0 excluded dead reseeds 2015-05-12 11:56:42 -04:00
orignal
eb559f7b6a excluded dead reseeds 2015-05-12 11:51:03 -04:00
orignal
64dbd9abdf Merge pull request #195 from ipslot/master
Update Log.cpp
2015-05-12 06:28:47 -04:00
ipslot
dfd41385b1 Update Log.cpp
set default log to std::cerr stream
2015-05-12 13:27:02 +06:00
orignal
2b797fcd54 use shared_ptr for NetDb's I2NPMessages 2015-05-11 15:17:43 -04:00
orignal
5cd557ef9d check for I2NP message buffer boudary 2015-05-11 12:53:08 -04:00
orignal
8baab2de37 Merge pull request #191 from apprb/dev
CMakeLists.txt: compilation speed up
2015-05-11 06:33:36 -04:00
apprb
c266cff956 CMakeLists.txt: compilation speed up 2015-05-11 15:56:13 +06:00
orignal
53affa3303 Merge pull request #190 from multikatt/patch-1
typo: Gralic -> Garlic
2015-05-09 19:28:22 -04:00
David
ec772c5d46 typo: Gralic -> Garlic 2015-05-09 19:25:11 -04:00
orignal
7b5a7e10a9 fixed log crash at shutdown 2015-05-08 21:42:28 -04:00
orignal
188f1fcff8 rewrite tunnel path inversion code 2015-05-08 14:07:33 -04:00
orignal
39c346df10 created paired inbound tunnel after outbound 2015-05-07 16:03:12 -04:00
orignal
490e829083 Merge pull request #189 from hagen-i2p/gcc5-makefile
* add gcc 5.* to supported compilers
2015-05-07 11:16:20 -04:00
hagen
846128a791 * add gcc5 to supported compilers 2015-05-07 03:40:19 +00:00
orignal
6bad2daa62 fixed build errors for gcc 4.6 2015-05-06 19:18:00 -04:00
orignal
4c91d08cea pass TunnelConfig as shared_ptr 2015-05-06 16:17:48 -04:00
orignal
2442d0e910 moved UPnP instance to Transports. Use actual port from RouterContext 2015-05-06 12:19:20 -04:00
orignal
7c13194d5a don't recalculate timestamp for each log message 2015-05-06 11:24:35 -04:00
orignal
0ae7bbd34d Update README.md 2015-05-05 17:30:32 -04:00
orignal
0b2654f6b1 Update README.md 2015-05-05 17:30:14 -04:00
orignal
42d49bde86 handle tunnels quantity params 2015-05-05 12:32:13 -04:00
orignal
d2b4a6fd50 select first hop from existing connections if applicable 2015-05-05 10:33:19 -04:00
orignal
7f172964f6 check profile only once 2015-05-04 13:01:27 -04:00
b8b8d70c7f reseed certificate updates 2015-05-02 21:00:43 +00:00
orignal
969695f318 check garlic clove length 2015-04-21 18:59:35 -04:00
orignal
7ec701a816 uin32_t for elapsed time 2015-04-21 18:33:04 -04:00
orignal
c96b81206d changed some profiling parameters 2015-04-21 15:59:40 -04:00
orignal
5f8356741e fixed potential memory leak 2015-04-18 13:55:15 -04:00
orignal
3987d5e5a0 recreate tunnel after 9.5 minutes 2015-04-17 11:36:42 -04:00
orignal
fcb56db224 try to pick an outbound tunnel with same endpoint instead expired 2015-04-17 10:11:51 -04:00
orignal
873754c6ca select next lease with same gateway if possible 2015-04-16 11:38:36 -04:00
orignal
12465f840a check outbound tunnles for readiness 2015-04-15 18:25:05 -04:00
orignal
e8c9d2db10 double RTO after every resend attempt 2015-04-15 11:52:49 -04:00
orignal
a8b4f38865 router don't expire if less than 75 2015-04-15 07:30:37 -04:00
orignal
27bd193708 re-create tunnel before expiration 2015-04-14 21:37:21 -04:00
orignal
c56ddce2f6 some cleanup 2015-04-14 10:46:44 -04:00
orignal
5d2f9f9f0b fixed potential memory leak 2015-04-14 10:40:46 -04:00
orignal
864aba9f4e version 0.9.19 2015-04-13 18:54:13 -04:00
orignal
4d27399ce3 check profile for high bandwidth peer selection only 2015-04-13 18:51:31 -04:00
orignal
76c54ffdef always check profile for peer selection 2015-04-13 18:41:19 -04:00
orignal
c873e9dd68 don't send reset message due problem at other side 2015-04-13 11:38:23 -04:00
orignal
ce99357ebe check for zero ident 2015-04-12 16:59:59 -04:00
orignal
562de3514a check database lookup type 2015-04-12 15:54:28 -04:00
orignal
128a8f3b48 delete obsolete profiles 2015-04-11 15:39:23 -04:00
orignal
1839b85d97 Merge pull request #180 from 7histle/master
Fix for #179
2015-04-11 07:03:10 -04:00
7histle
f0f154cd10 Fix for #179 2015-04-11 13:47:49 +03:00
orignal
624bff3036 reduced log file size 2015-04-10 19:58:32 -04:00
orignal
1d2950b4a7 reduced CPU load at floodfill 2015-04-10 19:49:58 -04:00
orignal
9072a018dd reduced CPU load at floodfill 2015-04-10 18:13:11 -04:00
orignal
2a997d94bf GetClosestFloodfills added 2015-04-10 16:15:13 -04:00
orignal
2741e94a72 fixed infinite loop 2015-04-10 13:19:23 -04:00
orignal
7c660ee556 show local destination for SAM sessions 2015-04-10 12:11:10 -04:00
orignal
51b850aa85 show windows size and connection status 2015-04-10 11:52:14 -04:00
orignal
b29e94005d fixed crash 2015-04-10 09:58:08 -04:00
orignal
ddd506fde7 Merge pull request #178 from yuri-sevatz/master
Fix -lboost_date_time missing from CMakeLists.txt
2015-04-10 06:57:37 -04:00
Yuri Sevatz
20310cb109 Fix -lboost_date_time missing from CMakeLists.txt 2015-04-10 00:10:35 -04:00
orignal
11177d37ea send and handle RESET flag 2015-04-09 21:09:30 -04:00
orignal
da006a1d6e use AsyncSend 2015-04-09 18:40:23 -04:00
orignal
451b0382ea implemented AsyncSend 2015-04-09 15:07:25 -04:00
orignal
950f250d66 NetDb/NetDbRequests split 2015-04-09 12:45:00 -04:00
orignal
01913d2b14 EdDSA signer added 2015-04-09 10:03:21 -04:00
orignal
e0b19a6383 fixed crash 2015-04-08 19:06:47 -04:00
orignal
48289845df EdDSA signature type added 2015-04-08 16:28:52 -04:00
orignal
454f2dabbd EdDSA signature type added 2015-04-08 16:18:16 -04:00
orignal
8891d9aa4d Decode point 2015-04-08 15:31:13 -04:00
orignal
49d59fc116 IsOnCurve added 2015-04-08 14:07:45 -04:00
orignal
8c92c50f9a multiplication by integer 2015-04-08 13:49:27 -04:00
orignal
75d45ae988 initial code for Ed25519 added 2015-04-08 13:21:49 -04:00
orignal
d5e1d5db9c validate leaseset for zero leases 2015-04-08 10:34:16 -04:00
orignal
9ce9d9b7fc variable length buffer for LeaseSet 2015-04-08 09:39:02 -04:00
orignal
e9edc7b205 Merge pull request #177 from robertfoss/asan_1
Fixed memory leak: delete -> delete[]
2015-04-07 17:27:18 -04:00
orignal
56822d9424 fixed null pointer exception 2015-04-07 17:22:14 -04:00
Robert Foss
2c480bee9a Fixed memory leak: delete -> delete[] 2015-04-07 22:37:24 +02:00
orignal
3983838694 use unique_ptr for ElGamalEncryption 2015-04-07 16:02:07 -04:00
orignal
1e74ff8a85 use shared_ptr for CreateDatabaseStore 2015-04-07 15:15:27 -04:00
orignal
8c47bf9dd3 use shared_ptr for local LeaseSet 2015-04-07 15:02:00 -04:00
orignal
3a26383c4d made Encrypt const 2015-04-07 14:40:36 -04:00
orignal
634976cdde pass LeaseSet to callback of RequestDestination 2015-04-07 12:02:25 -04:00
orignal
bc21f5955f use shared_ptr for AddressReceiver 2015-04-06 15:02:37 -04:00
orignal
e72eb35cc2 use shared_ptr for socket in I2PTunnelConnection 2015-04-06 14:41:07 -04:00
orignal
fbe4e64e44 lookup always takes full address from LeaseSet 2015-04-06 12:22:13 -04:00
orignal
be301dc090 4 tags for LeaseSet request 2015-04-05 20:07:32 -04:00
orignal
250af7f247 fixed race condition 2015-04-05 13:56:41 -04:00
orignal
10577cd1e5 select tunnel from TunnelPool rather than from LeaseSet for DeliveryStatus 2015-04-05 12:54:15 -04:00
orignal
62593f60c5 fixed memory leak 2015-04-04 15:44:29 -04:00
orignal
89ed8c2173 set datagram receiver per port 2015-04-03 20:34:37 -04:00
orignal
321dd252ea fixed crash if no routers available 2015-04-03 10:02:45 -04:00
orignal
19325d552a fixed race condition 2015-04-02 15:10:02 -04:00
orignal
43f8ec46cc fixed crash if can't connect to a reseed 2015-04-02 10:27:07 -04:00
orignal
cb9f78540a Update README.md 2015-04-02 09:01:15 -04:00
orignal
0a15088572 RC4_SHA cipher suite 2015-04-01 20:23:06 -04:00
orignal
ec68338a20 fixed typo 2015-04-01 15:42:26 -04:00
orignal
173b35f77d https://netdb.rows.io:444/ added 2015-04-01 15:07:45 -04:00
orignal
f47831c339 RSA_WITH_AES_256_CBC_SHA support 2015-04-01 14:41:36 -04:00
orignal
e7d4f63884 use SendFinished 2015-04-01 13:55:50 -04:00
orignal
4e81083bb4 AES256 cipher template 2015-04-01 12:56:41 -04:00
orignal
0446a5ae73 moved encryption/decryption to TlsCipher 2015-04-01 12:03:58 -04:00
orignal
a2aec4340d Merge pull request #173 from hagen-i2p/readme-fix
* README.md
2015-04-01 10:37:05 -04:00
hagen
fda68c114c * README.md 2015-04-01 13:48:54 +00:00
orignal
de5c55160b count tunnel acceptance ratio for peer selection 2015-03-31 17:13:01 -04:00
orignal
d0ac6345c2 0.9.0 2015-03-31 08:34:55 -04:00
orignal
d9911f4314 save last update time 2015-03-30 21:05:04 -04:00
orignal
92bd29ebf1 delete trailing paddings of SSU packtes 2015-03-30 13:10:36 -04:00
orignal
00ac1f7ec9 check # of block to encrypt/decrypt for zero 2015-03-30 11:56:24 -04:00
orignal
12641ab0c0 fixed addressbook crash at shutdown 2015-03-30 10:21:52 -04:00
orignal
629b5ff171 fixed typo 2015-03-30 07:21:47 -04:00
orignal
6ec5858561 Merge pull request #170 from guanqun/fix-wrong-buffer-size
setup the correct buffer size
2015-03-30 07:04:48 -04:00
guanqun
ab7550947a setup the correct buffer size 2015-03-30 09:26:37 +08:00
orignal
f2078f6d5b schedule interoducers again if still testing 2015-03-29 21:05:12 -04:00
orignal
1a440e3a83 count non-replied tunnel build requests 2015-03-28 16:09:34 -04:00
orignal
b8acce115f repeat peer test if previous was not successive 2015-03-28 14:57:39 -04:00
orignal
577ba9b397 initial filtration of bad peers 2015-03-27 20:34:31 -04:00
orignal
5f199432f0 proper size of remaining data 2015-03-27 16:31:53 -04:00
orignal
8b10fc497d pass correct datagram size 2015-03-27 16:09:43 -04:00
orignal
e80d09aa17 fixed typo 2015-03-27 15:50:24 -04:00
orignal
4c1e12921a restore imcomplete string back 2015-03-27 15:29:46 -04:00
orignal
1824a9a38e try to receive remanining message 2015-03-27 15:22:56 -04:00
orignal
3e59535a53 try to receive remanining message 2015-03-27 14:58:26 -04:00
orignal
8c174cd548 merge incomplete DATAGRAM SEND message 2015-03-27 14:36:54 -04:00
orignal
2a4d1c978e process multiple DATAGRAM SEND message 2015-03-27 14:02:27 -04:00
orignal
cc91a6d96f send ports with datagram 2015-03-27 11:29:40 -04:00
orignal
96ace89789 increase SAM connection buffer size 2015-03-27 09:44:27 -04:00
orignal
d226477852 Merge pull request #168 from guanqun/master
fix the 'sleep_for' missing error on linux platform
2015-03-27 06:59:18 -04:00
guanqun
eddb5fa91e fix the 'sleep_for' missing error on linux platform 2015-03-27 10:48:26 +08:00
orignal
c92e00c21f proper verification for DSA_SHA1 2015-03-26 22:17:26 -04:00
orignal
f07d29bc8a handle DATAGRAM SEND 2015-03-26 21:23:59 -04:00
orignal
f7b4cbc94a Merge pull request #167 from guanqun/fix-readme
fix tab in README.md
2015-03-26 16:14:47 -04:00
orignal
d226a4a0b7 store session with Alice with PeerTest 2015-03-26 16:10:52 -04:00
orignal
c62659cdbc single buf for ProcessPeerTest 2015-03-26 15:05:52 -04:00
orignal
4831e9705c const buffer for ProcessPeerTest 2015-03-26 14:35:20 -04:00
guanqun
fce74a710f fix tab in README.md 2015-03-26 23:31:03 +08:00
orignal
39641f05b9 create datagram destination for DATAGRAM session 2015-03-26 11:15:29 -04:00
orignal
2a23537dbd check lease expiration with threshold 2015-03-26 10:30:29 -04:00
orignal
ca3b9f253d pass data for streams only 2015-03-25 22:23:41 -04:00
orignal
1a0abfbc06 create keys only by DEST GENERATE 2015-03-25 19:13:30 -04:00
orignal
6e9149afd4 fixed misalignment 2015-03-25 09:04:19 -04:00
orignal
0a7bf4db47 load profiles 2015-03-25 08:45:50 -04:00
orignal
8a790fd0b3 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-03-24 18:48:53 -04:00
orignal
fd3dab35cc collect and save participation agreed/declined stats 2015-03-24 18:48:16 -04:00
orignal
5fd179524f --host is deprecated 2015-03-24 13:33:29 -04:00
orignal
f8a7beb001 --host is deprectaed 2015-03-24 13:31:56 -04:00
orignal
02903cc98c moved Dockerfile to build 2015-03-24 13:31:01 -04:00
orignal
7c5993b1cd Merge pull request #166 from multikatt/docker
adding docker and fig files
2015-03-24 12:20:52 -05:00
orignal
a6f78134c0 Profiling added 2015-03-24 12:47:57 -04:00
multikatt
21d4f49d31 adding docker and fig files 2015-03-24 12:47:43 -04:00
orignal
3f4bd13091 don't wait for remote LeaseSet request complete 2015-03-23 22:23:40 -04:00
orignal
6125288e95 select really other remote lease 2015-03-23 22:18:30 -04:00
orignal
7f91c9e63e close stream is SYN has not been received 2015-03-23 18:07:43 -04:00
orignal
ec319f950f add random milliseconds to end date 2015-03-23 15:53:20 -04:00
orignal
217ddfe98d fixed crash 2015-03-23 13:08:04 -04:00
orignal
57578a3aa3 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-03-23 12:56:47 -04:00
orignal
9a7e21778e eliminate misalignment for LeaseSet 2015-03-23 12:55:42 -04:00
orignal
cdb42b33ce Removed external IP detection step 2015-03-22 18:42:18 -04:00
orignal
4b47bfb5db re-request remote LeaseSet 2015-03-22 18:34:39 -04:00
orignal
18deb8b4f2 DeliveryStatus for LeaseSet 2015-03-22 14:59:27 -04:00
orignal
e58ebb8656 increased transit tunnels # to 2500 2015-03-21 18:39:48 -04:00
orignal
05d7b79d07 proper formatting 2015-03-21 18:35:17 -04:00
orignal
d5c43e66fa Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-03-21 16:26:33 -04:00
orignal
5f3b17af64 better resend and tunnel reselection algorithm 2015-03-21 16:26:14 -04:00
orignal
7bec2b8417 Merge pull request #165 from hagen-i2p/tun-config
* README : describe format of tunnels.cfg
2015-03-21 14:55:39 -05:00
hagen
70396240c3 * README : describe format of tunnels.cfg 2015-03-21 12:34:20 +00:00
orignal
ac79d8ed3e fixed misspelling 2015-03-20 21:13:06 -04:00
orignal
02de91f7f2 use unique_ptr for socket in I2PTunnelConnection 2015-03-20 16:04:48 -04:00
orignal
77fd296095 wait for LeaseSet request completion 2015-03-20 15:53:53 -04:00
orignal
1cc3dd5ee4 send I2PControl response without write_json 2015-03-20 14:45:22 -04:00
orignal
41ad5d12a3 use propery tree for params 2015-03-20 11:36:57 -04:00
orignal
b26c960450 fixed outgoing message size 2015-03-19 22:18:18 -04:00
orignal
7ea12ddd61 RouterInfo version and status 2015-03-19 21:51:18 -04:00
orignal
4e1e47d9e5 always use 'O' class for high-bandwidth 2015-03-19 11:52:46 -04:00
orignal
3ccc8d9508 badwidth parameter 2015-03-19 11:14:21 -04:00
orignal
123e6b7de4 made keys parameter optional for client tunnels 2015-03-19 10:22:01 -04:00
orignal
1caacaacf0 UpdateStats for RouterContext 2015-03-18 15:36:07 -04:00
orignal
0b754ec65d publish number of leasets and routers for floodfill 2015-03-18 15:06:15 -04:00
orignal
647bb501d1 make sure packet size is multiple of 16 bytes 2015-03-18 13:30:38 -04:00
orignal
47c3d5ed23 don't accept tunnels if bandwidth is exceeded 2015-03-18 13:07:11 -04:00
orignal
b693d13144 Merge pull request #164 from ir4y/master
Add libi2pd.so to .gitignore
2015-03-18 06:17:45 -05:00
Ilya Beda
a5ada6f487 Add libi2pd.so to .gitignore 2015-03-18 11:26:17 +06:00
orignal
60351f2677 send exporatory message directly if connected to a floodfill 2015-03-17 20:56:51 -04:00
orignal
1e2f038ef5 proper badnwidth calculation 2015-03-17 19:04:58 -04:00
orignal
708e17162c handle i2p.router.net.bw requests 2015-03-17 15:36:15 -04:00
orignal
60c60b4db1 calculate bandwidth 2015-03-17 15:19:38 -04:00
orignal
a09c67772c specify keys file for proxy's local destination 2015-03-17 11:44:01 -04:00
orignal
028e3a6c35 show total send/received bytes 2015-03-16 19:33:59 -04:00
orignal
a72d7652af moved num sent/received bytes to TransportSession 2015-03-16 18:57:22 -04:00
orignal
76ad7f24ee access list for server tunnels 2015-03-16 14:52:42 -04:00
orignal
1ac689b886 Merge pull request #163 from ygrishin/master
Added MTU detection code for Windows XP and newer
2015-03-15 15:32:26 -04:00
Yuriy Grishin
f32399454c Whitespace to follow coding style convention 2015-03-15 10:07:39 -07:00
Yuriy Grishin
f2665262f7 Added MTU detection code for Windows 2015-03-15 08:51:07 -07:00
orignal
fee68cf333 read optional destination port form config 2015-03-15 11:28:30 -04:00
orignal
1d737409ec fill padding with random data 2015-03-14 17:15:23 -04:00
orignal
897af615f9 create persistent destinations with ECDSA 2015-03-13 22:11:51 -04:00
orignal
ea6597c3ad .ini file format for tunnel.cfg 2015-03-13 19:51:31 -04:00
orignal
5a31d2e817 read tunnels.cfg using property tree 2015-03-13 16:05:39 -04:00
orignal
09f1966e5f pass destination port to client tunnel 2015-03-13 13:29:27 -04:00
orignal
ad649aba48 reduce memory usage 2015-03-12 22:38:22 -04:00
orignal
c7edc36106 reduce memory usage 2015-03-12 16:26:08 -04:00
orignal
57d4ccfdfd reduced memory usage 2015-03-12 11:43:36 -04:00
orignal
550f9de41a fixed memory corruption 2015-03-12 07:31:46 -04:00
orignal
f3aab569ca don't return error if received data is available 2015-03-11 21:43:30 -04:00
orignal
2f9e510f4f check I2NP message size for saved fragments 2015-03-11 14:34:39 -04:00
orignal
1d7fd8ac96 reduce memory usage 2015-03-11 13:24:13 -04:00
orignal
ccb4e967cf fixed crash 2015-03-11 12:17:38 -04:00
orignal
08f5af68f0 reduce memory usage 2015-03-11 11:28:51 -04:00
orignal
d51b87e80a reduced memory usage 2015-03-10 21:54:25 -04:00
orignal
c2edbdc487 drop RTO to initial if tunnels pair changes 2015-03-10 18:51:04 -04:00
orignal
5bc854bedc reduced memory usage 2015-03-10 15:38:37 -04:00
orignal
3e889ee06c resend packing with RTO interval 2015-03-10 11:11:42 -04:00
orignal
9c338a5c81 don't lost received data 2015-03-09 22:05:26 -04:00
orignal
d13b4f6698 send FIN if closed by peer 2015-03-09 21:37:51 -04:00
orignal
cd0933522d handle stream close depending on state 2015-03-09 12:06:35 -04:00
orignal
dc599bbc63 stream status 2015-03-08 19:36:33 -04:00
orignal
4b82e90ffb send proper send time for NACK reply 2015-03-07 20:16:31 -05:00
orignal
36de881041 send close after buffer 2015-03-06 21:39:05 -05:00
orignal
e0f80c9f91 save and check token 2015-03-06 11:33:54 -05:00
orignal
55f6fe6d3a exclude Token from requests 2015-03-05 15:33:45 -05:00
orignal
42f314669f show router status through I2PControl 2015-03-05 15:17:16 -05:00
orignal
9d273acd42 resend message immediately if NACK recieved 2015-03-04 21:55:40 -05:00
orignal
7fbe2754a4 use v4 only sessions between Bob and Charlie 2015-03-04 12:50:31 -05:00
orignal
c0043e5098 send outstanding data on close 2015-03-03 22:46:52 -05:00
orignal
756e15fe19 fixed typo 2015-03-03 16:00:02 -05:00
orignal
d9218134e2 pass source and destination ports to datagram receiver 2015-03-03 15:31:49 -05:00
orignal
c66ba370d5 bind streaming destination to port for server tunnel 2015-03-03 14:52:16 -05:00
orignal
6fc0b2ecfb create additional streaming destination 2015-03-02 22:04:59 -05:00
orignal
7b938b246a choose streaming destination for destination port 2015-03-02 16:09:59 -05:00
orignal
45cb98c8de shared_ptr for streaming destination 2015-03-02 15:54:38 -05:00
orignal
0f8ea92a53 handle local destination port 2015-03-01 21:08:34 -05:00
orignal
ac57e7ced2 fixed typo 2015-03-01 17:13:55 -05:00
orignal
513dc2fcc5 don't use explicit unreachable parameter anymore 2015-03-01 07:55:03 -05:00
orignal
3c10ba4511 show base64 address 2015-02-28 20:12:21 -05:00
orignal
6f9d8ed01b show tunlel creation success ratio 2015-02-28 07:59:34 -05:00
orignal
3977cec408 fixed crash at startup 2015-02-27 17:58:01 -05:00
orignal
7949ffe41e fixed crash 2015-02-27 13:07:32 -05:00
orignal
bd9e68e69f fixed api build 2015-02-27 11:47:32 -05:00
orignal
ccf689ffd5 connect to introducer if not any yet 2015-02-26 21:05:35 -05:00
orignal
b95b49bd19 start unsing introducers after firewall detection 2015-02-26 19:19:03 -05:00
orignal
dc8209837c cleanup expired peer tests 2015-02-26 14:54:28 -05:00
orignal
582daffd7f select peer test capable routers 2015-02-26 14:17:16 -05:00
orignal
30715c2512 firewall detection 2015-02-26 13:44:18 -05:00
orignal
51aea367c3 full implementation of peer test 2015-02-25 21:56:51 -05:00
orignal
34e31f3d78 PeerTest from Alice to Charlie 2015-02-25 16:39:48 -05:00
orignal
e250628174 global peer tests 2015-02-25 15:26:06 -05:00
orignal
eee968ce56 fixed incorrect instantiation in gcc 4.6 2015-02-24 22:17:46 -05:00
orignal
52f806ff94 use shared_ptr for ClientDestination 2015-02-24 15:40:50 -05:00
orignal
58ebd8cc59 1 hours expiration of RI in case of floodfill 2015-02-24 11:58:39 -05:00
orignal
d08d1c9127 show uptime properly 2015-02-23 17:54:51 -05:00
orignal
1a307f3093 limit number of transit tunnels 2015-02-23 15:06:14 -05:00
orignal
dc18c012ed show uptime through I2PControl 2015-02-23 14:57:57 -05:00
orignal
c46a82420d show uptime 2015-02-23 14:41:56 -05:00
orignal
e002c05350 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-02-22 17:07:42 -05:00
orignal
6401ed71ae Update version.h
0.8.0
2015-02-22 17:05:52 -05:00
orignal
621bfde961 handle incoming connection failure 2015-02-22 17:04:42 -05:00
orignal
5b5c06179c original_at_mail.i2p.crt added 2015-02-22 09:12:43 -05:00
orignal
b187babd20 128 bytes key expansion 2015-02-21 22:07:55 -05:00
orignal
05e49bbeab show SAM sessions through web interface 2015-02-20 22:47:36 -05:00
orignal
c88c6a9b63 include https hosts to reseeder's list 2015-02-20 15:15:23 -05:00
orignal
8e795cc2aa include https hosts to reseeder's list 2015-02-20 15:11:00 -05:00
orignal
ad5bac6598 ClientKeyExchange length depend on key length from ceritifcate 2015-02-20 14:26:49 -05:00
orignal
0d468a8f48 extract https content 2015-02-20 12:21:33 -05:00
orignal
71dae29077 send and receive data 2015-02-20 10:47:44 -05:00
orignal
d1b26b72e3 proper handshake messages hash calculations 2015-02-19 22:13:41 -05:00
orignal
64a4799c8c fixed incorrect MAC calculation 2015-02-19 16:35:07 -05:00
orignal
e5d9c26868 use 256 bytes block for keys expansion 2015-02-19 13:50:04 -05:00
orignal
5deccd7833 calculate MAC 2015-02-19 11:26:37 -05:00
orignal
e1f64e2476 moved https code to TlsSession 2015-02-19 10:06:11 -05:00
orignal
cf5499375e encrypt finishes message 2015-02-18 22:25:30 -05:00
orignal
a3736fc06e tls encrypt and decrypt 2015-02-18 16:52:00 -05:00
orignal
68a03c2134 send ChangeCipherSpecs 2015-02-18 11:44:32 -05:00
orignal
dce8cf1af2 tls 1.2 and RSA_WITH_AES_256_CBC_SHA256 2015-02-18 10:28:29 -05:00
orignal
29d118a19a hanshakes hash and finishes message 2015-02-17 22:45:55 -05:00
orignal
ec50b97aa8 reduced amount of logging 2015-02-17 19:14:31 -05:00
orignal
fbf672288f MAC and encryption keys 2015-02-17 16:50:37 -05:00
orignal
47e8cfd91e calculate master secret 2015-02-17 15:34:30 -05:00
orignal
9968485cdd send ClientKeyExchange 2015-02-16 21:28:37 -05:00
orignal
6ea037cc47 process server certificate 2015-02-16 11:21:33 -05:00
orignal
c5f0be126e client hello for HTTPS 2015-02-15 23:03:04 -05:00
8db34a4d7f Merge branch 'master' of github.com:PurpleI2P/i2pd 2015-02-15 19:20:41 +00:00
732ff67076 New certificate; will be switched on the server for a future release. 2015-02-15 19:19:09 +00:00
orignal
e3190a4ca9 SSU data receive batching 2015-02-15 14:17:55 -05:00
f2c849703a Remove reseed host, no longer in use 2015-02-15 19:17:41 +00:00
orignal
e901307d8d fixed crash 2015-02-15 10:23:06 -05:00
orignal
e038467c89 unique_ptr for sent and incomplete messages 2015-02-14 22:24:10 -05:00
orignal
776582e019 fixed typo 2015-02-14 21:00:40 -05:00
orignal
018daa8837 catch sendto exception 2015-02-14 17:23:15 -05:00
orignal
10b733d215 don't throw exception 2015-02-14 17:20:21 -05:00
orignal
97ec65cccd read all sections from tunnels.cfg 2015-02-13 19:00:17 -05:00
orignal
d2d7b3b348 multiple server I2P tunnels 2015-02-13 15:56:57 -05:00
orignal
b0052eae05 doesn't create same local destination twice 2015-02-13 14:55:48 -05:00
orignal
ed692ffba3 make sure only one I2P tunnel per local prot 2015-02-13 10:18:42 -05:00
orignal
242bc3624a read tunnels.cfg 2015-02-12 22:20:07 -05:00
orignal
18121a99ca config file for I2PTunnels 2015-02-12 16:11:56 -05:00
orignal
93c2c13f96 fixed memory leak 2015-02-12 14:03:59 -05:00
orignal
b11fd250c1 fixed race condition 2015-02-12 11:40:42 -05:00
orignal
93857b690a fixed hand at shutdown 2015-02-11 22:48:26 -05:00
orignal
34d88db0b2 Merge pull request #158 from multikatt/master
typo: stoped->stopped
2015-02-11 22:10:07 -05:00
multikatt
dcad550a2f typo: stoped->stopped 2015-02-11 21:06:52 -05:00
orignal
816e4d533d fixed race condition 2015-02-11 18:47:20 -05:00
orignal
53adf7a793 cleanup dead peers 2015-02-11 14:45:25 -05:00
orignal
0ed9dc4e93 complete session if presented 2015-02-10 17:36:39 -05:00
orignal
9eba7923a7 use insert instead [] 2015-02-10 16:51:47 -05:00
orignal
3263f6fefc ban abusing ipv6 addresses 2015-02-10 15:54:07 -05:00
orignal
a27dd17cef fixed memory leak 2015-02-10 15:51:56 -05:00
orignal
52f9d5f0aa ban abusing IPs 2015-02-10 13:05:08 -05:00
orignal
6783b22ad0 terminate non-responding NTCP sessions by timeout 2015-02-10 10:54:04 -05:00
orignal
a3c1c314d0 don't insert same transit tunnel twice 2015-02-10 09:39:49 -05:00
orignal
d01f3b094b allow netDb cleanup after every 500 messages 2015-02-10 09:30:48 -05:00
orignal
8492e87d29 fxied race condition 2015-02-09 22:19:29 -05:00
orignal
923b64eb48 delete messages sent to disconnected session 2015-02-09 16:33:52 -05:00
orignal
89883e2078 don't look for session if a packet is from same endpoint as previous 2015-02-09 14:50:19 -05:00
orignal
23907e6cb1 don't look for session if a packet is from same endpoint as previous 2015-02-09 14:48:01 -05:00
orignal
c6c414c382 receive multiple UDP packets 2015-02-09 11:53:11 -05:00
orignal
c349652a27 terminate receivers 2015-02-08 21:12:38 -05:00
orignal
f9731a76ec separate receivers thread 2015-02-08 20:28:18 -05:00
orignal
7dd159add2 use 'available' method 2015-02-08 20:27:17 -05:00
orignal
1ad04bf8bf read more data from socket if available 2015-02-08 11:52:03 -05:00
6064d12af3 Sync reseed servers with the java router
contrib/certificates/ssl/netdb.i2p2.no2.crt will be needed
when netdb.i2p2.no updates to a stronger certificate later this year
2015-02-08 16:27:01 +00:00
orignal
9b7820a09d clean obsolete SSU data 2015-02-08 09:15:13 -05:00
orignal
e5fdee272b clean obsolete SSU data 2015-02-08 08:50:05 -05:00
orignal
a80e4ef0ea fixed memory leak 2015-02-07 17:58:29 -05:00
orignal
ab0bd908ec fixed race condition 2015-02-07 15:25:06 -05:00
orignal
9c7fcfbe3f Merge pull request #157 from klondi/httpproxy
Get Jump services working again although, at what price!
2015-02-07 13:34:31 -05:00
Francisco Blas (klondike) Izquierdo Riera
1bbaa5ba22 URLdecode the base64 part of the key 2015-02-07 18:34:25 +01:00
Francisco Blas (klondike) Izquierdo Riera
b22423e2d5 Get Jump services working again although, at what price\! 2015-02-07 17:57:16 +01:00
orignal
5730b15f01 fixed race condition 2015-02-06 20:53:48 -05:00
orignal
8305fd5f82 check if connection to peer exists already 2015-02-06 18:37:37 -05:00
orignal
0518b08ca6 drop second incoming connection with same identity 2015-02-06 16:03:47 -05:00
orignal
8e75d8c39a check accepted socket for error 2015-02-06 13:49:00 -05:00
orignal
6beb527058 teminate non-connected NTCP session 2015-02-06 11:14:41 -05:00
orignal
b9e3931e80 use shared_ptr for inbound tunnels 2015-02-05 18:53:43 -05:00
orignal
7945126e86 use unique_ptr for requested destination 2015-02-05 13:36:28 -05:00
orignal
408a67f34f use unique_ptr for incomplete message 2015-02-05 12:13:37 -05:00
orignal
0dac2a74d3 check for duplicate msgID 2015-02-04 22:16:44 -05:00
orignal
9896570e32 cleanup from extra log messages 2015-02-04 22:05:09 -05:00
orignal
9639ab7f1e fixed memory leak 2015-02-04 21:24:48 -05:00
orignal
778d1afda0 cleand destination requests every 15 seconds 2015-02-04 15:34:52 -05:00
orignal
02c22b850b fixed compilation bug for boost < 1.49 and boost 1.49 with gcc 4.7 2015-02-04 11:08:26 -05:00
orignal
bd035e1c3d fixed build for boost below 1.49 2015-02-04 09:40:00 -05:00
orignal
b923b1e31d Update version.h
release 0.7.0
2015-02-03 20:59:12 -05:00
orignal
14f448f4c7 show tunnels queue size 2015-02-03 16:45:19 -05:00
orignal
cd8e9e59fa don't request same RouterInfo twice 2015-02-03 16:14:33 -05:00
orignal
4a6847da8d RTT 2015-02-03 13:46:44 -05:00
orignal
c5a3832eae handle exploratory lookups 2015-02-02 22:34:55 -05:00
orignal
7f75d250c7 Merge pull request #151 from ygrishin/master
fixed HTTP connection being reset on Windows
2015-02-02 21:58:53 -05:00
orignal
95dbc20350 log more disgnostics data 2015-02-02 20:15:49 -05:00
orignal
f846b87590 make sure DeliveryStatus and DatabseStore are sent first 2015-02-02 16:08:35 -05:00
orignal
e3764bef37 fixed incorect reply data parsing for DatabaseStore 2015-02-02 13:06:02 -05:00
orignal
d7c5c24ce4 pass ident hash by value to transports thread 2015-02-02 12:01:21 -05:00
orignal
60ba7be319 floodfill parameter 2015-02-02 11:15:38 -05:00
orignal
2215dd7bd7 ability to turn floodfill off 2015-02-02 11:14:09 -05:00
orignal
07ce9c41bf DatabaseLookup flags 2015-02-02 11:06:36 -05:00
orignal
24564f1faa moved UPNP to common files 2015-02-02 09:58:34 -05:00
orignal
ba11971513 don't delete updated RouterInfo 2015-02-02 09:40:03 -05:00
ygrishin
803737011a fixed HTTP connection being reset on Windows 2015-02-01 22:24:47 -07:00
orignal
8ad9f2681c send 3 closest floodfills 2015-02-01 19:58:26 -05:00
orignal
b46c3036d8 flood 2015-02-01 18:30:27 -05:00
orignal
02ed7d6059 fixed non-responding destination request 2015-02-01 16:18:08 -05:00
orignal
2c2acae50d invoke acceptor on reset 2015-02-01 09:34:32 -05:00
orignal
ac438fbd7d use stream buffer for sending identity 2015-01-31 21:49:54 -05:00
orignal
feffbc9330 fixed typo 2015-01-31 10:39:29 -05:00
orignal
908404ab62 show send buffer size 2015-01-30 21:41:32 -05:00
orignal
618abd6320 use unique_ptr for sent fragments 2015-01-30 20:30:39 -05:00
orignal
79087f6942 detect congesion for first message in sned queue only 2015-01-30 16:43:31 -05:00
orignal
9a43f0d54c don't send DatabaseStore reply for local destinations 2015-01-30 15:29:33 -05:00
orignal
79af7c22d9 send DatabaseStore reply 2015-01-30 15:13:09 -05:00
orignal
0b911a5caa use I2NPMessagesHandler for SSU 2015-01-29 22:35:57 -05:00
orignal
aae837f642 congesion control during retransmission 2015-01-29 19:17:44 -05:00
orignal
5887e8c8c4 slow start and congestion avoidance 2015-01-29 15:34:43 -05:00
orignal
974a7ff3f5 shared_ptr for RoutingDestination 2015-01-28 21:37:08 -05:00
orignal
938fa00469 publishing with flood 2015-01-28 21:01:16 -05:00
orignal
679faf5149 specify reply token for RIs DatabaseStore 2015-01-28 16:16:25 -05:00
orignal
7e45233c7d floodfill parameter 2015-01-28 15:12:15 -05:00
orignal
2ed69ef602 clean up expired LeaseSets 2015-01-28 14:20:28 -05:00
orignal
192a08b5bf check tunnel status instead fidning it every time 2015-01-27 22:31:57 -05:00
orignal
763547f465 fixed corrupted NTCP messages 2015-01-27 19:12:27 -05:00
orignal
b3e08b2cf4 shared_ptr for tunnels 2015-01-27 14:55:46 -05:00
orignal
d1d6797d3e store LeaseSet as shared_ptr 2015-01-27 11:27:58 -05:00
orignal
4b094b2156 handle tunnel build messages in tunnels thread 2015-01-26 20:49:16 -05:00
orignal
562cdc12d1 check message type of follow on message 2015-01-26 14:48:24 -05:00
orignal
8f562215b0 separate inbound and outbound pending tunnels 2015-01-26 11:56:10 -05:00
orignal
8a478e4616 check for max number of NACKs 2015-01-25 22:01:09 -05:00
orignal
f75de6af82 window 2015-01-25 17:43:34 -05:00
orignal
a061f3339e use send buffer for a stream 2015-01-25 16:18:26 -05:00
orignal
724c417f09 fixed typo 2015-01-25 11:43:27 -05:00
orignal
9c23d03d8d fixed typo 2015-01-25 10:05:50 -05:00
orignal
b8740c008b fixed crash 2015-01-25 10:05:19 -05:00
orignal
588c613043 naming lookup of .b32 address through LeaseSet request 2015-01-24 15:34:46 -05:00
orignal
1dc166f0f8 transit tunnel gateway batching 2015-01-23 22:05:33 -05:00
orignal
82103e6a39 process TunnelGateway message in tunnel thread 2015-01-23 16:26:39 -05:00
orignal
33bce67a4e Update copyright
name changed to PurpleI2P
2015-01-23 14:03:41 -05:00
orignal
aeb3c8b8b9 Update LICENSE
name changed to PurpleI2P
2015-01-23 14:02:37 -05:00
orignal
24c00b0985 schedule routing session cleanup 2015-01-23 12:48:25 -05:00
orignal
a25646a129 cleanup routing sessions 2015-01-23 10:07:11 -05:00
orignal
0c73aff0a2 I2NPMessagesHandler 2015-01-22 22:00:41 -05:00
orignal
3ed1fee7ce remove stream on close 2015-01-22 19:02:28 -05:00
orignal
b269bda52b shared_ptr for GarlicRouting Session 2015-01-22 15:31:34 -05:00
orignal
2ab0ff8aea TransitTunnelParticipant 2015-01-21 21:50:46 -05:00
orignal
46a36f766f don't restart subscriptions update timer if no subscription presented 2015-01-21 16:34:50 -05:00
orignal
276d5097c1 fixed 'bad descriptor' exception 2015-01-21 15:59:05 -05:00
orignal
ec980edf56 don't look for tunnel again if tunnelID is the same as for previous message 2015-01-21 15:13:46 -05:00
orignal
89dead79c4 common HandleTunnelData for own and transit tunnels 2015-01-21 14:40:48 -05:00
orignal
e7f849184c some cleanup 2015-01-21 12:08:15 -05:00
orignal
c61cd350ee send multiple messages though single write call 2015-01-20 22:35:27 -05:00
orignal
ea353ac3ba send batch of I2NP messages 2015-01-20 21:05:57 -05:00
orignal
74c89ce06e proper cleanup of pending tunnels 2015-01-20 18:06:42 -05:00
orignal
42354ee5d5 removed useless mutex lock 2015-01-20 07:50:25 -05:00
orignal
e4d0fdaa56 Merge pull request #150 from chris-barry/patch-1
Update copyright
2015-01-19 22:29:45 -05:00
orignal
ebb5c53c3a use shared_ptr for TunnelPool 2015-01-19 22:28:13 -05:00
Chris Barry
676892cb00 Update copyright
Changing Debian information to match information in root directory.
2015-01-19 21:06:30 -05:00
orignal
e09da5cb54 correct CRC32 verification at big endian CPU 2015-01-19 14:30:30 -05:00
orignal
027c43c99c Reseed through I2PControl 2015-01-19 13:57:37 -05:00
orignal
f5b937667a don't try to re-request expired LeaseSet 2015-01-19 12:31:14 -05:00
orignal
f36229bd95 make sure DatabaseStore message is first 2015-01-18 18:46:22 -05:00
orignal
3c9e6054b5 use shared local destination for proxies 2015-01-17 09:42:44 -05:00
orignal
284fb5458e fixed resolve bug 2015-01-17 08:22:43 -05:00
orignal
bf7b53a2a6 resolve address for NTCP 2015-01-16 23:01:40 -05:00
orignal
07c6f2a20b make HTTP header if necessary 2015-01-16 16:51:52 -05:00
orignal
514947ba49 skip HTTP header 2015-01-16 16:19:17 -05:00
orignal
f3fbf6bd89 address resolver for NTCP address 2015-01-16 15:25:44 -05:00
Mikal Villa
0f227e8317 For some reason, the daemon src don't include I2PControl.cpp for OSX
builds.
2015-01-16 20:37:52 +01:00
Mikal Villa
905aa7cdc4 Adding download link 2015-01-16 02:44:18 +01:00
orignal
25cc118890 I2PControl method 2015-01-15 16:42:28 -05:00
orignal
72a4f8a9a1 fixed crash on shutdown 2015-01-14 20:27:19 -05:00
orignal
e898e6bf82 use RouterInfo request callback instead timeout 2015-01-14 16:37:03 -05:00
orignal
ad9d7931f5 RequestComplete for RouterInfo 2015-01-14 16:11:09 -05:00
orignal
fb3c577601 handle i2p.router.netdb.activepeers 2015-01-14 13:24:25 -05:00
orignal
02b7cd71c5 handle i2p.router.netdb.activepeers 2015-01-14 13:21:41 -05:00
orignal
98e930bd46 moved delayed queue to Peer 2015-01-13 22:19:13 -05:00
orignal
3481161616 send messages through Peer 2015-01-13 21:31:39 -05:00
orignal
a3352ac1dc Merge pull request #148 from Nefelim4ag/master
Makefile fixes
2015-01-13 11:26:05 -05:00
Timofey Titovets
214eb0caa5 Not overwrite -fPIC with CXXFLAGS 2015-01-13 15:14:52 +03:00
Timofey Titovets
3a30c00dae Fix: mkdir exist in multi thread building 2015-01-13 12:55:14 +03:00
orignal
d971dff593 introduced Peer 2015-01-12 22:53:35 -05:00
orignal
1eef996701 NetworkSetting method 2015-01-12 14:31:45 -05:00
orignal
dcae7fc541 participating request 2015-01-12 14:03:20 -05:00
orignal
aeb2e235e5 use lookup tables for requests 2015-01-12 13:38:16 -05:00
orignal
ff856d2f20 fixed race condition 2015-01-12 12:15:54 -05:00
Riccardo Spagni
a15c2c5d86 license -> BSD 3-clause 2015-01-12 17:54:09 +02:00
orignal
c37d13af84 Merge pull request #147 from space-and-time/master
Fixed windows build, assertion failed
2015-01-12 07:29:03 -05:00
root
bf443265ff fixed boost\asio\detail\socket_types.hpp(24) fatal error C1189: "WinSock.h has already been included" 2015-01-12 08:53:24 +04:00
root
9ebe38e59d added I2PService to VS project 2015-01-12 08:50:22 +04:00
orignal
a85cc6aa77 fixed race condition 2015-01-11 21:00:38 -05:00
orignal
6683a9cf76 moved NTCP to separate thread 2015-01-11 17:41:56 -05:00
orignal
e3e0702813 fxied crash at startup 2015-01-11 17:40:11 -05:00
orignal
d8942a3359 use TransportSession for sending messages 2015-01-10 23:00:27 -05:00
orignal
4d25634b66 less agressive exploratory 2015-01-10 16:08:13 -05:00
orignal
717940d969 some cleanup 2015-01-10 09:07:07 -05:00
orignal
912146b1c9 shutdown and graceful shutdown through I2PControl 2015-01-09 22:27:52 -05:00
orignal
70b6c024bf handle i2p.router.netdb.knownpeers RouterInfo request 2015-01-09 11:58:14 -05:00
orignal
047f08b482 put dot-separated params 2015-01-09 11:12:22 -05:00
orignal
4ce3817d28 put dot-separated params 2015-01-09 11:11:35 -05:00
orignal
8910412068 pass results by reference 2015-01-09 10:28:16 -05:00
orignal
c61ed150b7 check for pending LeaseSet request 2015-01-08 22:04:41 -05:00
orignal
1bbb86d304 changed addressbook subscription update interval to 12 hours 2015-01-08 16:16:56 -05:00
orignal
2a76f1decd publish own RouterInfo every 40 minutes if nothing changed 2015-01-08 16:14:05 -05:00
orignal
f3548daede I2PControl Authenticate and Echo 2015-01-08 16:11:40 -05:00
orignal
efdadfd7c5 added I2PControl to ClientContext 2015-01-08 13:28:51 -05:00
orignal
e82507ca4e call TCPIPAccetor::Start from I2PClientTunnel::Start 2015-01-08 07:39:35 -05:00
orignal
d67db32015 Merge pull request #146 from klondi/i2pservice
I2pservice
2015-01-07 22:52:21 -05:00
Francisco Blas (klondike) Izquierdo Riera
56014962d4 Make I2PClientTunnel use TCPIPAcceptor 2015-01-08 03:49:35 +01:00
Francisco Blas (klondike) Izquierdo Riera
df3e8ce937 Move Stream creation to its own handler for cleanliness, it will hand over to a tunnel connection when done 2015-01-08 03:28:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
7d9c0b76fc Make SOCKS use TCPIPAcceptor 2015-01-08 01:45:49 +01:00
Francisco Blas (klondike) Izquierdo Riera
8a6bea64bc Make the HTTP Proxy use TCPIPAcceptor 2015-01-08 01:35:42 +01:00
Francisco Blas (klondike) Izquierdo Riera
114022d18a Add the TCPIPAcceptor class for handling TCP/IP services on clearnet 2015-01-08 01:34:31 +01:00
orignal
a85af553b8 Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2015-01-07 16:51:09 -05:00
orignal
1cf5a0c948 Merge pull request #145 from klondi/i2pservice
Solve weak_ptr issue
2015-01-07 16:50:22 -05:00
Francisco Blas (klondike) Izquierdo Riera
6643b4188a Solve weak_ptr issue 2015-01-07 22:49:28 +01:00
orignal
bc11689f35 extract params 2015-01-07 16:41:11 -05:00
orignal
0339a4f963 JSON parser 2015-01-07 16:09:32 -05:00
orignal
6a39f48a9e Merge pull request #144 from klondi/i2pservice
I2pservice
2015-01-07 16:01:56 -05:00
Francisco Blas (klondike) Izquierdo Riera
b3232b42db Use shared_from_this to avoid being killed easily on stop 2015-01-07 21:58:58 +01:00
Francisco Blas (klondike) Izquierdo Riera
c1a29b08ac Remove ClientContext.h dependency 2015-01-07 21:58:58 +01:00
orignal
75f6cc4319 Merge pull request #143 from klondi/i2pservice
I2pservice
2015-01-07 15:20:18 -05:00
Francisco Blas (klondike) Izquierdo Riera
bcbe207515 Make HTTPProxy use SIGNING_KEY_TYPE_DSA_SHA1 2015-01-07 21:15:04 +01:00
Francisco Blas (klondike) Izquierdo Riera
29039fd039 Merge with upstream 2015-01-07 20:47:47 +01:00
Francisco Blas (klondike) Izquierdo Riera
90005c8237 Migrate to I2Pservice 2015-01-07 20:44:24 +01:00
orignal
cdc0aa658a I2PControl added 2015-01-07 13:26:44 -05:00
Francisco Blas (klondike) Izquierdo Riera
dd42819a2f Create I2Pservice as a way to integrate service management, hide unnecessary handlers 2015-01-07 19:09:59 +01:00
orignal
88560d06a1 Merge pull request #141 from klondi/httpproxy
Force Connection: Close and allow http/1.1
2015-01-06 20:42:41 -05:00
Francisco Blas (klondike) Izquierdo Riera
cb8a465605 Force Connection: Close and allow http/1.1 2015-01-07 02:40:57 +01:00
orignal
fe13a85c0f read all available data from closed stream 2015-01-06 19:05:48 -05:00
orignal
89b6be91a3 Merge pull request #140 from klondi/httpproxy
Httpproxy
2015-01-06 19:00:41 -05:00
Francisco Blas (klondike) Izquierdo Riera
6aca908462 Initial HTTPProxy support by simply transferring control to a tunnel 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
634718d6b4 Detect null stream on I2PConnect 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
2fca028161 Remove unnecessary header 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
5e8d28abba Reorder SOCKS headers for cleanness 2015-01-07 00:15:54 +01:00
Francisco Blas (klondike) Izquierdo Riera
bc78460f63 Enter state BEFORE reading data to avoid race conditions 2015-01-07 00:15:54 +01:00
orignal
e228ba963d Merge pull request #139 from iShift/patch-1
fix deps for ubuntu/debian
2015-01-06 16:00:47 -05:00
iShift
63927fc1fa fix deps for ubuntu/debian
fix https://github.com/PrivacySolutions/i2pd/issues/137
2015-01-06 23:52:13 +03:00
orignal
cbcfe50eb5 Merge pull request #138 from klondi/master
Reintroduce Request verification, allow for extra data after connect request.
2015-01-06 12:51:25 -05:00
Francisco Blas (klondike) Izquierdo Riera
7da95bd28a Introduce missing call to request verification, so unknown address types and commands are handled properly, allow for extra socket data after the request for fast request sending, it will just be forwarded on I2PConnect 2015-01-06 18:49:00 +01:00
orignal
5444889715 Update version.h
Version updated
2015-01-06 07:52:36 -05:00
orignal
3e13a1feed long ElGamal private key 2015-01-05 19:32:46 -05:00
orignal
7e4c416bc1 Merge pull request #136 from klondi/master
Improve SOCKS, handle  .b32.i2p conversions into address book
2015-01-05 15:09:16 -05:00
Francisco Blas (klondike) Izquierdo Riera
882b559d3a Reduce the parsing to a single state machine, automatic removal of objects and destruction of sockets/streams 2015-01-05 20:17:07 +01:00
Francisco Blas (klondike) Izquierdo Riera
610fd2ac67 Reduce memory usage a bit 2015-01-05 20:17:07 +01:00
Francisco Blas (klondike) Izquierdo Riera
f383ebb718 Return adequate SOCKS replies 2015-01-05 20:17:07 +01:00
Francisco Blas (klondike) Izquierdo Riera
cb94d43092 Use the address book for conversion into .b32.i2p 2015-01-05 20:17:07 +01:00
orignal
8812a45607 DatabaseLookup for RouterInfo 2015-01-04 17:25:16 -05:00
orignal
37a374000c elimimated cast to ClearText 2015-01-04 09:33:19 -05:00
orignal
ac17f116be Merge pull request #134 from klondi/httpproxy
More SOCKS Proxy cleanups
2015-01-03 23:14:38 -05:00
orignal
ecf709cbba fill clear text buffer of BuildRequestRecord 2015-01-03 23:13:07 -05:00
Francisco Blas (klondike) Izquierdo Riera
225aa7fa6a Merge SOCKS4a and SOCKS5 request parsers, add stub support for SOCKS4 and IPv4 and IPv6 addresses on SOCKS5 2015-01-04 04:47:03 +01:00
Francisco Blas (klondike) Izquierdo Riera
46b16237b6 Use enums instead of magic numbers on SOCKS 2015-01-04 04:47:03 +01:00
Mikal Villa
7a387b9a9f Updating UPnP code for windows. (untested) 2015-01-04 00:06:43 +01:00
orignal
3c5e9ddd4e eliminated cast to ecnryptted build record 2015-01-03 16:31:44 -05:00
orignal
02851d7587 Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2015-01-03 16:30:07 -05:00
orignal
1b3652e135 Merge pull request #133 from klondi/httpproxy
SOCKS5 support
2015-01-03 16:29:41 -05:00
Francisco Blas (klondike) Izquierdo Riera
17ccaab792 Clean up SOCKS a bit 2015-01-03 22:18:05 +01:00
Francisco Blas (klondike) Izquierdo Riera
c9576dcdbe Add Initial SOCKS5 support 2015-01-03 22:10:03 +01:00
Mikal Villa
7acdc0a606 Libminiupnpc library support (v1.5 and v1.6), dynamic runtime linking. 2015-01-03 21:38:48 +01:00
Mikal Villa
f552f24e6e Following BSD standards 2015-01-03 21:35:30 +01:00
Mikal Villa
79d13eb6cb Fix Mac OSX build error. 2015-01-03 21:33:27 +01:00
orignal
41974b8c75 fixed race condition 2015-01-03 15:20:11 -05:00
orignal
729cc4330e Merge pull request #132 from klondi/httpproxy
SOCKSProxy
2015-01-03 10:30:29 -05:00
Francisco Blas (klondike) Izquierdo Riera
27d510d1b7 Handle port correctly 2015-01-03 16:16:27 +01:00
orignal
8a293f45fa eliminated cast to BuildResponse Record 2015-01-03 09:47:24 -05:00
Francisco Blas (klondike) Izquierdo Riera
44bc09b007 Rewrite SOCKS proxy parsing to allow for SOCKS5, also fix any remaining logs 2015-01-03 15:40:01 +01:00
orignal
b58d58ef29 Merge pull request #131 from klondi/httpproxy
Get SOCKS proxy back in working state
2015-01-03 08:03:28 -05:00
Francisco Blas (klondike) Izquierdo Riera
ba12331a11 Rewrite SOCKS proxy so it uses I2PTunnelConnection and I2PTunnel 2015-01-03 06:44:21 +01:00
Francisco Blas (klondike) Izquierdo Riera
b88b04515e Remove unnecessary RemoteLeaseSet 2015-01-03 06:44:21 +01:00
Francisco Blas (klondike) Izquierdo Riera
ba9a0c0b2e Fix strange indentation. 2015-01-03 06:44:21 +01:00
orignal
9237174026 request failed in destination is not ready 2015-01-02 22:37:46 -05:00
orignal
fd9a8fd2b1 eliminated cast to DatabaseStoreMsg 2015-01-02 21:11:40 -05:00
orignal
7b59ce61bb Merge pull request #130 from klondi/httpproxy
Clean up  I2PClientTunnel
2015-01-02 21:06:44 -05:00
Francisco Blas (klondike) Izquierdo Riera
1ae55e5872 Use the new asynchronous API on I2PClientTunnel and clean up after ourselves 2015-01-03 02:43:59 +01:00
Francisco Blas (klondike) Izquierdo Riera
6489230e68 Simplify and merge the identHash caching codepath on I2PClientTunnel 2015-01-03 02:17:01 +01:00
Francisco Blas (klondike) Izquierdo Riera
c05f411ba0 Fix a memory leak in ClientConnection 2015-01-03 02:07:55 +01:00
orignal
b30de01b12 eliminated cast to DeliveryStatus struct 2015-01-02 17:39:35 -05:00
orignal
79e1096eca process follow-on packets with zero send stream id 2015-01-02 10:04:57 -05:00
orignal
f1ae5817eb Merge pull request #129 from klondi/httpproxy
Asynchronous stream creation
2015-01-02 08:12:54 -05:00
Francisco Blas (klondike) Izquierdo Riera
a906d7f02f Allow for asynchronous creation of streams 2015-01-02 13:37:26 +01:00
Francisco Blas (klondike) Izquierdo Riera
50fb373655 Use i2p::data::SigningKeyType instead of uint16_t 2015-01-02 13:37:26 +01:00
orignal
29d1aa0146 more fixes of misalignment 2015-01-01 23:00:33 -05:00
orignal
fa4009821e Merge pull request #128 from klondi/httpproxy
Httpproxy
2015-01-01 19:12:11 -05:00
Francisco Blas (klondike) Izquierdo Riera
e5503c51b4 Allow for easy stream creation at LocalDestination (for now it's only synchronous) 2015-01-02 00:56:26 +01:00
Francisco Blas (klondike) Izquierdo Riera
ccb68088a8 Allow for I2PTunnels without LocalDestination 2015-01-02 00:56:21 +01:00
orignal
0f07b04627 eliminated TunnelGatewayHeader 2015-01-01 18:53:44 -05:00
orignal
bfc6274cd8 methods for I2NP header access 2015-01-01 16:51:15 -05:00
orignal
c5c0d2060c rollback 2015-01-01 11:18:24 -05:00
orignal
dde2b4a879 destroy local destination on 'clear' 2015-01-01 09:54:15 -05:00
orignal
df21a067ff print out number of NACKs 2015-01-01 09:53:30 -05:00
orignal
8c49f76534 copy request to buffer if LeaseSet is requested 2014-12-31 21:47:49 -05:00
orignal
96a9575049 Merge pull request #127 from klondi/alignmentfixes
Fix even more alignment problems
2014-12-31 14:06:53 -05:00
orignal
0a9368fc70 Merge pull request #126 from klondi/cmakefixes
Cmakefixes
2014-12-31 14:03:22 -05:00
Francisco Blas (klondike) Izquierdo Riera
84f7966a0b Fix even more alignment problems 2014-12-31 19:48:46 +01:00
Francisco Blas (klondike) Izquierdo Riera
8f7dea698e Allow disabling binary compilation (useful for multilib builds in Gentoo) 2014-12-31 19:46:09 +01:00
Francisco Blas (klondike) Izquierdo Riera
9e7e0a456d Use better install paths 2014-12-31 19:46:09 +01:00
Francisco Blas (klondike) Izquierdo Riera
64b47a29cf Fix library compilation 2014-12-31 19:46:09 +01:00
orignal
cbfe8b8232 invoke accepter after receiving remote identity 2014-12-30 22:37:14 -05:00
orignal
583838e2c2 fixed misalignment 2014-12-30 15:33:11 -05:00
orignal
7bfb73dacf LeaseSet database lookup 2014-12-30 12:25:08 -05:00
orignal
350e942b6a fixed windows build 2014-12-30 10:30:45 -05:00
orignal
ed82f388e6 Merge pull request #123 from klondi/master
Fix any remaning aliasing rules with propper memcpy wrappers
2014-12-30 09:50:28 -05:00
Francisco Blas (klondike) Izquierdo Riera
b5f624a10f Use htobuf16 for 0 value instead of htobe16buf 2014-12-30 15:42:24 +01:00
Francisco Blas (klondike) Izquierdo Riera
700c53e60a Fix any remaning aliasing rules with propper memcpy wrappers 2014-12-30 15:37:24 +01:00
2ed99ba245 fix calling individual targets
(broken in 82af922b40)

Otherwise commands like `make i2p` will fail.
2014-12-30 13:34:25 +00:00
472c5f542f Merge branch 'master' of https://github.com/torbjo/i2pd 2014-12-30 02:32:33 +00:00
Francisco Blas (klondike) Izquierdo Riera
1636187e26 Avoid aliasing problems by using special buf endian handling functions wrapping memcpy 2014-12-29 23:04:02 +01:00
orignal
4d640dac2a always use destination's thread to avoid race conditions 2014-12-29 14:29:55 -05:00
Torkel Bjørnson-Langen
7bf11df3b8 Minor: Makefile - added mk_build_dir to .PHONY target 2014-12-29 17:40:55 +01:00
Torkel Bjørnson-Langen
ddf2aa38cc Minor: fixed clang compile warning [-Wmismatched-tags] 2014-12-29 17:40:03 +01:00
Meeh
e8c544c774 Merge pull request #120 from torbjo/master
Fixed building on OS X. - Tested and accepted :)
2014-12-29 17:31:36 +01:00
Torkel Bjørnson-Langen
82af922b40 Fixed building on OS X.
I got it to work on OS X 10.10.2 using clang.

Note: I'm an OS noob :)
2014-12-29 17:12:05 +01:00
orignal
446e5fd665 report about termination after actual processing 2014-12-28 15:45:58 -05:00
orignal
7847982a57 fixed crash 2014-12-27 22:43:00 -05:00
orignal
86a7f96a46 take tunnels from exploratory pool only 2014-12-27 20:36:32 -05:00
orignal
da01ea997d wait from LeaseSet request comlete 2014-12-27 10:09:55 -05:00
orignal
59aa40e2b0 wait until LeaseSet request complete instead timeout 2014-12-26 21:06:24 -05:00
orignal
6fb5fa1c52 LeaseSet request complete callback 2014-12-26 19:09:44 -05:00
orignal
64df22def8 read reamaining data from closed stream 2014-12-26 08:55:24 -05:00
orignal
bbe403fb40 main netdb can request router info only 2014-12-25 21:13:51 -05:00
orignal
3547a4042c request and handle LeaseSets through local destination only 2014-12-25 16:47:15 -05:00
orignal
46ea2291fe read incoming data util it's available 2014-12-25 10:42:49 -05:00
orignal
66c2c7f789 wait until download complete 2014-12-25 08:41:21 -05:00
orignal
78c06bdd22 manage non-reponded database requests 2014-12-24 11:20:38 -05:00
orignal
afd69e4afd send if-none-match 2014-12-24 09:45:25 -05:00
orignal
ff7ff3b55b check status code 304 2014-12-24 07:48:18 -05:00
orignal
14e2c76799 Merge pull request #118 from hagen-i2p/make-deps
* new makefile target: deps
2014-12-24 07:19:24 -05:00
hagen
677e3585c9 * new makefile target: deps 2014-12-24 05:08:43 +00:00
orignal
e349facd65 HTTP 1.1 for addressbook 2014-12-23 21:03:00 -05:00
orignal
7b5e8a9661 check and update addressbook subscriptions 2014-12-23 13:57:09 -05:00
orignal
3f314d8355 merge chunked responses 2014-12-22 21:20:39 -05:00
orignal
325117114a fixed typo 2014-12-22 21:02:32 -05:00
orignal
e1d445ab50 parse HTTP header fields 2014-12-22 16:45:50 -05:00
orignal
b1b72d2d33 rolled back to HTTP 1.0 since chunked is not implemented 2014-12-22 16:10:32 -05:00
orignal
bb5e520a79 reduce minimal # of routers to 50 2014-12-22 15:14:49 -05:00
orignal
fd0069cb0e fixed merge conflict 2014-12-22 15:09:50 -05:00
orignal
52ee861d3a load subscriptions 2014-12-22 15:06:54 -05:00
42075e74ad fetch with HTTP/1.1 2014-12-22 18:01:59 +00:00
orignal
1e87aedbb8 Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2014-12-21 21:33:00 -05:00
orignal
5221e09b67 use VerifyDigest for verification 2014-12-21 21:32:21 -05:00
orignal
0972782553 delete useless field 2014-12-21 17:27:46 -05:00
425c746b87 typo fix 2014-12-21 21:48:30 +00:00
orignal
db2d0df2c4 download hosts.txt without proxy 2014-12-21 09:33:02 -05:00
orignal
da1397ff76 fixed build error 2014-12-20 21:21:43 -05:00
orignal
c009fc5d72 look for LeaseSet in shared local destination 2014-12-20 20:16:45 -05:00
993b4c92b0 slightly stricter use of mkdir/rm -rf 2014-12-20 20:11:55 +00:00
b26bc5c7f4 Add api target 2014-12-20 20:10:44 +00:00
e4cc15d19e Don't build the lib when making static binaries
Without this, building on 64-bit systems will fail.
2014-12-20 19:48:53 +00:00
e5c2022f71 Fix building in kFreeBSD
broken in 8c218bd5
2014-12-20 16:57:41 +00:00
b343c24a9f add explanatory comments to Makefiles 2014-12-20 16:57:41 +00:00
21e3778e69 Revert the 'clean-up' commits so that custom build-time flags can be set
These commits removed the 'NEEDED*' vars which were added so that CXX*
and LDFLAGS could be specified at build time. By doing away with these
and using solely CXXFLAGS and LDFLAGS, special flags cannot be added.
Indeed, specifying your own CXXFLAGS would cause the build to fail. We
want the build flags to be APPENDED, not overwritten.
2014-12-20 16:57:41 +00:00
orignal
f4f6e74ea2 process addressbook request response 2014-12-19 22:03:34 -05:00
orignal
c887f54740 fixed crash 2014-12-19 17:41:08 -05:00
orignal
1179731959 send request to addressbook link directly without proxy 2014-12-19 14:40:02 -05:00
orignal
91e833cdaf fixed crash at shutdown 2014-12-19 12:07:54 -05:00
orignal
84e5f30c70 delete local destination on session close 2014-12-18 18:48:36 -05:00
orignal
b007b66b15 store SAMSession by pointer 2014-12-17 19:02:16 -05:00
orignal
8b05455545 Cancel added 2014-12-17 15:31:13 -05:00
orignal
a44ca91409 terminate acceptor and sessions on stop 2014-12-17 15:21:50 -05:00
orignal
84235fe479 fixed typo 2014-12-17 11:42:16 -05:00
orignal
25ccfef4b0 version updated 2014-12-17 09:20:58 -05:00
orignal
ec8e3574f9 delete local destination on session close 2014-12-16 21:26:29 -05:00
orignal
7da694825d fixed handshake 2014-12-16 19:04:13 -05:00
orignal
9606883b78 handle signature type for SAM 3.1 2014-12-16 16:23:42 -05:00
orignal
54cffb583f check max version 2014-12-16 15:54:02 -05:00
orignal
fe4d640504 drop incoming garlic messages if local destination doesn't exist anymore 2014-12-16 14:50:29 -05:00
orignal
1a69770e15 fixed compilation warning 2014-12-16 14:35:39 -05:00
orignal
75b67b7ea7 CRC32 verification 2014-12-16 12:48:42 -05:00
orignal
e3f077ee9a create io_service together with destination 2014-12-15 22:50:11 -05:00
orignal
f357a5864c pass local destination by pointer 2014-12-15 21:24:01 -05:00
orignal
6e32c389b1 some cleanup 2014-12-15 19:08:46 -05:00
orignal
219abaa7e1 don't use AESNI if not supported 2014-12-15 16:15:06 -05:00
orignal
82d5bf2f8a load certificates before reseed 2014-12-15 12:38:35 -05:00
orignal
1ffe7955a3 RSA raw verifier 2014-12-15 12:32:06 -05:00
orignal
ef73353a0a use RSA-raw for SU3 verification 2014-12-14 22:45:09 -05:00
orignal
52a0b9ca03 extend LeaseSet buffer to fir RSA-4096 key and signature 2014-12-13 20:54:18 -05:00
orignal
0a9eca9f94 load certificates and verify signatures 2014-12-13 18:35:53 -05:00
orignal
b399d45d66 extract public key from ceritificate 2014-12-13 15:01:08 -05:00
orignal
d6fe4556fb don't make executable as shared 2014-12-13 14:45:44 -05:00
orignal
6ad0313dbe Merge pull request #117 from hagen-i2p/library-build-2
Library build 2
2014-12-13 13:56:48 -05:00
hagen
ca2566e778 * reorder makefile rules : fix depends on header 2014-12-13 13:39:57 +00:00
orignal
2b093a0e3f extract issuer name 2014-12-13 08:32:41 -05:00
hagen
0e212f29d0 * update build/CMakeLists.txt 2014-12-13 13:21:24 +00:00
hagen
cd3a7040b0 * Makefile : add dist target 2014-12-13 13:21:24 +00:00
hagen
cf8e229098 * almost fixed static linking 2014-12-13 13:21:24 +00:00
hagen
05a62af99b * move USE_* to main Makefile 2014-12-13 13:21:24 +00:00
hagen
56c404e6c1 * Makefile.linux : use idents with spaces 2014-12-13 13:21:24 +00:00
hagen
5ee4969322 * Makefile.linux : reorder checks 2014-12-13 13:21:24 +00:00
hagen
8a43e070d8 * Makefile.osx : exchange options 2014-12-13 13:21:24 +00:00
hagen
aa0cb6b2c4 * (3/3) cleanup : unused variable 2014-12-13 13:21:24 +00:00
hagen
d6ec412422 * (2/3) cleanup : use $CXXFLAGS instead $NEEDED_FLAGS 2014-12-13 13:21:24 +00:00
hagen
4e94bc9efc * (1/3) cleanup : use $CXXFLAGS instead $CPU_FLAGS 2014-12-13 13:21:24 +00:00
hagen
bc0f0e96a3 * (3/3) 2nd try : drop api/ subdir 2014-12-13 13:21:24 +00:00
hagen
9552edf82d * (2/3) update main Makefile 2014-12-13 13:21:24 +00:00
hagen
8c218bd5df * (1/3) update filelist.mk : 3 lists of sources: common, daemon-specific and library-specific 2014-12-13 13:21:24 +00:00
hagen
01bb492faf * cleanup : remove 100% garbage in Makefile.osx 2014-12-13 13:21:24 +00:00
orignal
afd0a43725 read certificate file 2014-12-12 16:17:53 -05:00
orignal
366b160727 initial parsin of X.509 certificate 2014-12-12 15:45:39 -05:00
8b61aedc3f sync certificates with the java router 2014-12-12 19:43:09 +00:00
orignal
23e49e730f process SU3 files with data descriptor 2014-12-12 13:36:02 -05:00
orignal
0e935a3511 check for data descriptor flag 2014-12-12 10:34:50 -05:00
orignal
56a95fa9b5 check for RI size 2014-12-11 22:31:39 -05:00
orignal
6ac846f95d SU3 resseed first 2014-12-11 21:14:04 -05:00
orignal
9ce56eb95f handle compression method field 2014-12-11 20:44:08 -05:00
orignal
3643d2f1da reseed from SU3 2014-12-11 15:41:04 -05:00
orignal
96851ab2fd Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2014-12-11 13:29:03 -05:00
orignal
7e52f8af5e pass params to destination 2014-12-11 13:28:37 -05:00
ca3817abe3 minor tweaks to the README 2014-12-11 18:05:21 +00:00
orignal
aaf9a70153 pass RSA public key in constructor 2014-12-11 12:33:32 -05:00
orignal
0ee7f02f51 extend temporary buffer size to 1024 2014-12-11 12:22:22 -05:00
orignal
18d9325800 fixed tabs 2014-12-11 08:07:45 -05:00
orignal
bec638914a reaturned api folder back 2014-12-11 08:02:35 -05:00
orignal
67c4c237a9 Merge pull request #116 from hagen-i2p/library-build
update buildsystem (cleanup and library build for cmake)
2014-12-11 07:32:25 -05:00
orignal
6c98c80268 Merge pull request #115 from hagen-i2p/conf-option
* README.md : add -conf option
2014-12-11 07:29:39 -05:00
hagen
342c87e15b * fix cmake build 2014-12-11 07:57:46 +00:00
hagen
5966113268 * build library in the same tree as main binary 2014-12-11 07:48:08 +00:00
hagen
a6ff3df591 * README.md : add -conf option 2014-12-11 04:59:22 +00:00
orignal
9438f388ad copy private signing key of proper size 2014-12-10 22:38:01 -05:00
orignal
0b3ee77717 RSA signatures 2014-12-10 21:31:06 -05:00
orignal
7ccb7f05bf added RSA verifier 2014-12-10 15:48:07 -05:00
orignal
5b53665f4f rolled back 2014-12-10 12:10:07 -05:00
orignal
b66aa7408e use Inflator instead Gunzip 2014-12-10 12:07:25 -05:00
orignal
5dbee6b300 fixed fields length 2014-12-10 10:06:37 -05:00
orignal
a0893eabfa fixed build error 2014-12-10 07:28:49 -05:00
orignal
c8e34052a7 some cleanup 2014-12-09 21:07:54 -05:00
orignal
a2d69a8b66 fixed build error 2014-12-09 17:21:19 -05:00
orignal
fd7fca1d54 process SU3 header 2014-12-09 16:44:58 -05:00
orignal
47589125e0 look for LeaseSet in local database 2014-12-09 14:59:19 -05:00
orignal
02256be720 fxied build error for VS 2014-12-09 14:15:02 -05:00
orignal
b9b224fdc3 fixed crash 2014-12-08 21:28:11 -05:00
orignal
cb06f8e0bb enale tunnl test encryption back 2014-12-08 19:33:50 -05:00
orignal
bb05bcf39f pass instance of std::ostream for logging from API 2014-12-08 16:27:10 -05:00
orignal
b7d1b74ffa add single tag from destination's tread 2014-12-08 15:36:00 -05:00
orignal
d1cca92459 fixed typo 2014-12-08 11:45:13 -05:00
orignal
1d497cf8d6 BOB 'clear' command 2014-12-07 21:06:04 -05:00
orignal
efa771310d Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2014-12-07 21:04:27 -05:00
orignal
8f12881162 BOB 'clear' command 2014-12-07 21:04:02 -05:00
orignal
85624e4f4b verify tag count field 2014-12-07 21:00:19 -05:00
orignal
5bbe661392 Update README.md
-bobport added
2014-12-07 17:29:09 -05:00
orignal
5e31d6b2bd proper response to 'setkeys' 2014-12-07 17:19:56 -05:00
orignal
06621a2198 don't publish to non-responding floodfill twice 2014-12-07 16:10:25 -05:00
orignal
4d77dad9cc Merge pull request #113 from arabesc/master
Fix build under FreeBSD 10.1 + Clang
2014-12-07 13:09:05 -05:00
orignal
e89938e9df fixed crash at startup 2014-12-07 12:54:35 -05:00
Igor Pavlov
1568128356 change asm labels to temporarily labels to fix clang compilation 2014-12-07 19:37:52 +03:00
Igor Pavlov
d13329471e add SSUSession.cpp to CMakeList.txt 2014-12-07 19:34:42 +03:00
orignal
9dbd5a583a AddressReceiver for inbound BOB tunnel 2014-12-07 09:48:03 -05:00
orignal
07ad7fea9e inbound and outbound BOB tunnels per destination 2014-12-06 21:23:43 -05:00
orignal
fbff749838 pass BOB options to destination 2014-12-06 16:06:46 -05:00
orignal
797c8750d8 pass BOB options to destination 2014-12-06 15:31:39 -05:00
orignal
9164ac8a3e BOB 'list' command 2014-12-05 22:25:31 -05:00
orignal
8788e1b2fd BOB 'clear' command 2014-12-05 19:16:54 -05:00
orignal
9a9d6e8e00 BOB lookup command 2014-12-05 16:03:43 -05:00
orignal
047a371050 send base64 adress from outbound non-quiet BOB connections 2014-12-05 14:46:59 -05:00
orignal
392075bf60 BOB quiet command 2014-12-05 14:13:16 -05:00
orignal
07d5e8c756 don't close session if unknown command 2014-12-05 10:59:37 -05:00
orignal
e84d4e5f42 populate session keys 2014-12-04 19:29:20 -05:00
orignal
4c8d85870b check identity buffer size 2014-12-04 19:28:20 -05:00
orignal
519330015f handle .i2p address for BOB inbound connections 2014-12-04 14:16:33 -05:00
orignal
08762870b4 fixed crash on termination 2014-12-04 11:24:00 -05:00
orignal
517a7ba3ab BOB setkeys/getkeys 2014-12-03 21:01:40 -05:00
orignal
3d0349191d fixed memory corruption 2014-12-03 21:00:25 -05:00
orignal
76478ceaa2 send recived data after address from BOB inbound connection 2014-12-03 20:37:20 -05:00
orignal
9328bd1caf temporary disabled tunnel test encryption due instability 2014-12-03 20:06:56 -05:00
orignal
b2a6b6657f handle stop command 2014-12-03 15:24:30 -05:00
orignal
0214ad69a6 inbound tunnels 2014-12-03 15:02:19 -05:00
orignal
1c0c530769 BOB inbound tunnel 2014-12-03 14:48:41 -05:00
orignal
75666e3e39 return public key only for newkeys 2014-12-02 21:55:30 -05:00
orignal
60e4e52373 BOB outgoing tunnels 2014-12-02 21:45:01 -05:00
orignal
30a1f9d447 send BOB message reply 2014-12-02 16:45:51 -05:00
orignal
cb293b93d2 BOB command parser 2014-12-02 15:47:44 -05:00
orignal
3ab65bbe0d command channel acceptor 2014-12-02 11:42:35 -05:00
orignal
e7f05cc462 BOB added 2014-12-02 10:34:02 -05:00
orignal
cfcec8229c delete non-responding streams 2014-12-02 08:07:31 -05:00
orignal
4e54fbec08 delete stream from inside 2014-12-01 21:26:51 -05:00
orignal
38ee813e41 wait for all messages acked before termination of a stream 2014-12-01 18:29:46 -05:00
orignal
9acf80e563 from/to base64 2014-12-01 14:50:10 -05:00
orignal
9539fb8cb0 fixed memory leak 2014-11-30 20:18:31 -05:00
orignal
bd13406f42 don't delete HTTP connection twice 2014-11-30 18:19:32 -05:00
orignal
fbec753dcd show local destination's tuunels state 2014-11-30 17:29:26 -05:00
orignal
e6c92a535d handle ipv6 sessions in separate thread 2014-11-30 17:19:21 -05:00
orignal
32a767dc91 pass I2CP options to local destination. Process tunnel length 2014-11-30 10:51:22 -05:00
orignal
3ca560b895 different tunnel length for IB and OB 2014-11-29 22:00:52 -05:00
orignal
5e83d950f5 count NACKs in plain ack message size 2014-11-29 19:15:41 -05:00
orignal
95027930f2 send NACKs 2014-11-29 16:21:19 -05:00
orignal
3ff1158384 fixed crash 2014-11-29 08:31:12 -05:00
orignal
404f21ea76 fixed API build error 2014-11-28 16:46:26 -05:00
orignal
49173da84d encypt tunnel test messages 2014-11-28 16:19:56 -05:00
orignal
d794dfb7e5 moved private keys creation to client context 2014-11-28 16:04:57 -05:00
orignal
8350f16b20 store SAM destinations if address book 2014-11-28 15:08:23 -05:00
orignal
e1c25fedb0 wait for confirmantion of publishing 2014-11-28 13:01:35 -05:00
orignal
3e3cfa3d68 load from addressbook first 2014-11-28 09:40:27 -05:00
orignal
092b445e36 store addresses in csv format 2014-11-27 16:26:55 -05:00
orignal
b9806ac86b fixed crash 2014-11-26 21:59:07 -05:00
orignal
a8c08563f1 alignment 2014-11-26 21:42:14 -05:00
orignal
eb6b04d6c2 send packets of a longer message at the that time 2014-11-26 19:25:12 -05:00
orignal
68c321609d naming lookup through addressbook 2014-11-26 16:51:36 -05:00
orignal
fae01f61d2 filesytem-based addressbook 2014-11-26 16:19:36 -05:00
orignal
aca87b5fd1 remember last outgoing tunnel per stream rather than per client destination 2014-11-26 13:20:35 -05:00
orignal
77687a70a2 adjust message length after alignment 2014-11-26 11:54:35 -05:00
orignal
357a9a6a56 16 bytes alignmen of AES block 2014-11-26 11:04:49 -05:00
orignal
f7d90648e3 drop verifier not used anymore 2014-11-26 10:28:06 -05:00
orignal
6153d799bc use shared pointer for NTCPSession 2014-11-25 16:30:15 -05:00
orignal
f15508aff5 Merge branch 'master' of https://github.com/orignal/i2pd 2014-11-25 15:16:35 -05:00
orignal
0ccb66476e moved NTCP client code to Transports 2014-11-25 15:16:03 -05:00
orignal
6c13ad78a5 eliminated NTCPServerConnection 2014-11-25 14:29:06 -05:00
orignal
8fde36a4b0 Update version.h
version 0.4.0
2014-11-25 12:46:30 -05:00
orignal
de14f8dcd7 handle Phase3 in two steps 2014-11-25 12:33:51 -05:00
orignal
1e81652a62 send Phase3 with proper identity 2014-11-25 10:59:29 -05:00
orignal
f7ce86e0c4 pass tsA to SendPhase4 2014-11-25 10:35:35 -05:00
orignal
9eb5982ea3 use generic receive buffer for phase 4 2014-11-25 10:14:18 -05:00
orignal
4778c8bdfb fixed excess data for P521 2014-11-24 21:48:01 -05:00
orignal
9574163aeb fixed incorrect certificate length 2014-11-24 21:23:12 -05:00
orignal
199ff0c210 ECDSA P384 and ECDSA P521 support 2014-11-24 20:19:13 -05:00
orignal
e0635548e9 handle EcDSA signatures 2014-11-24 15:26:57 -05:00
orignal
95524c8db3 shared pointer for SSU 2014-11-24 12:26:11 -05:00
orignal
1a0957b571 move WaitForConnect away from constructor 2014-11-24 11:18:12 -05:00
orignal
97656e7349 shared pointer for I2PTunnelConnection 2014-11-23 22:23:17 -05:00
orignal
0262a8b057 replaced boost::bind to std::bind 2014-11-23 17:00:45 -05:00
orignal
4bd8b44ab2 shared pointers for streams 2014-11-23 11:33:58 -05:00
orignal
4dc33a6f45 fixed crash 2014-11-22 21:56:59 -05:00
Mikal Villa
335de27095 Merge branch 'master' of github.com:PrivacySolutions/i2pd
* 'master' of github.com:PrivacySolutions/i2pd:
  shared pointers for SAM sockets
2014-11-22 23:12:34 +01:00
Mikal Villa
fa461b1254 Added prefix support, which enables the homebrew package to work. 2014-11-22 23:12:05 +01:00
orignal
81c63b0c30 shared pointers for SAM sockets 2014-11-22 16:35:58 -05:00
orignal
dcefe7d221 fixed typo 2014-11-21 13:49:49 -05:00
orignal
ed3aaefe96 delete delete routers from memory 2014-11-21 13:29:19 -05:00
orignal
6042aefd17 delete dead floodfill 2014-11-21 13:02:46 -05:00
orignal
1c3f70056a use shared pointer of RI for transports 2014-11-21 12:34:17 -05:00
orignal
d8b9968aed use shared pointer for RI in requested destination 2014-11-21 11:37:17 -05:00
orignal
8a357ac46c store shared pointer to RI in tunnel config 2014-11-21 10:46:11 -05:00
Mikal
5187701af1 Updated NSI file
Forgot to update version in NSI installer script
2014-11-21 00:28:21 -08:00
Mikal
6cc3021baa MSVC13 Fix
Fixed build issue on MSVC13. Bumping to v0.3.0 as we release binaries
and SU3 is added.
2014-11-20 23:46:36 -08:00
orignal
0f4e4a7944 return shared_ptr to RI 2014-11-20 16:20:02 -05:00
orignal
683c97d5c8 shared pointer for local RI 2014-11-20 15:48:28 -05:00
orignal
af2a5b93f2 SU3 file parser 2014-11-20 14:29:22 -05:00
orignal
f9ec60265a support of RI with EcDSA 2014-11-20 12:21:27 -05:00
orignal
a449dc1377 read RI from buffer 2014-11-19 16:21:40 -05:00
orignal
f6849442a9 encrypted delivery status 2014-11-19 14:56:47 -05:00
orignal
c3e329e406 fixed misalignment 2014-11-19 11:01:04 -05:00
orignal
69cbd71fe0 receive buffers aligned to 16 2014-11-18 12:37:04 -05:00
orignal
dd1a798128 AES buffer aligned to 16 bytes 2014-11-18 12:11:45 -05:00
orignal
96387aecbd store RIs as shared pointers 2014-11-18 11:08:10 -05:00
orignal
77b7fff5ed streaming api functions added 2014-11-18 09:33:58 -05:00
orignal
72c0d8443a create local destination through API 2014-11-17 17:00:30 -05:00
orignal
2988395b81 specify application name 2014-11-17 15:28:52 -05:00
orignal
439b8798c9 start/stop/init through api 2014-11-17 14:42:45 -05:00
orignal
7cf19f5784 api added 2014-11-17 12:37:40 -05:00
orignal
b9e2b7bf64 delete single message routing session immediately 2014-11-16 15:41:54 -05:00
Mikal
cc14b526cd Adding Icon for windows
Adding icon for windows executable.
2014-11-11 02:46:35 +01:00
Mikal
5e67bc62c1 Adding windows installer 2014-11-11 02:21:05 +01:00
692cde5151 Remove "DLL-Output" from README's example
This is not required to build i2pd.
2014-11-09 22:32:17 +00:00
4fbb823391 Merge branch 'master' into proposed_build_fixes
Conflicts:
	Win32/i2pd.vcxproj
2014-11-08 13:45:10 +00:00
d636d55cc8 Adding Windows build instructions 2014-11-08 13:29:15 +00:00
apekatten
901eb10125 Removed required admin-level for running 2014-11-08 13:44:18 +01:00
4b0001b442 innosetup: Add i2pd to PATH 2014-11-07 15:04:33 +00:00
3cd1281167 updates to innosetup script
* support installation of both x64 and x86 binaries
* add readme
* add function to convert line endings. The README.md file currently has
  Windows line endings, but if that ever changes this function will
  automatically make the file "Windows friendly".
2014-11-07 15:04:33 +00:00
33f9918854 minor updates to readme
* grammar fixes
* note working VS2013 version
* change tabs to spaces to ensure proper viewing on systems with
* different tab settings
2014-11-07 15:04:33 +00:00
a518d6063c update vcxproj.filters 2014-11-07 15:04:33 +00:00
1232278c46 Various changes to Windows build
- No hard coded dependency paths. These are better set with environment
  variables or property pages.
- Don't require admin rights to run
- Add support for running on Windows XP (note that Boost and Crypto++
  *also* need to be built targetting XP).
- Explicitly set type to console application
- Initial stubs for building x64 version
- Turn off sending error reports to Microsoft
- spaces, not tabs (matches the default when saved by VS2013 itself)
- disable multibyte
- re-enable SDL checks
2014-11-07 15:04:33 +00:00
dd4283b7c1 Revert "fixed typo" and "fixed crash for Windows"
This reverts commits c8a80a497d and f7791e5289

This didn't actually fix Windows crash problem. In fact, it still
crashed without --log=0 being set. Changes to the i2pd.vcxproj file
fixed the crashes with VS 2013 > update 1.  i2pd now works with VS 2013
Update 3 and Update 4 RC.

Since these changes didn't have the intended effect, let's remove them.
2014-11-07 14:58:27 +00:00
3e826cd6dc easier static builds (STATIC=yes)
This will also disable AESNI
2014-11-07 14:49:16 +00:00
2f8c37b132 Allow setting the LIBDIR at build time 2014-11-07 14:48:49 +00:00
e914d1640c allow AESNI to be disabled 2014-11-07 14:17:24 +00:00
25b5068f5e allow LDFLAGS to be set by user
Move old LDFLAGS variable to LDLIBS. By doing ths, a user can set their
own LDFLAGS without breaking the build. A case in which this can be
useful is hardening with debian, e.g.

    $ dpkg-buildflags --get LDFLAGS
    -Wl,-z,relro
2014-11-04 02:37:12 +00:00
orignal
38b901484a check for SSU packet size 2014-11-03 10:15:01 -05:00
orignal
c8a80a497d fixed typo 2014-11-02 08:28:28 -05:00
orignal
faf1fe7a7c aligned AES keys 2014-11-01 21:53:45 -04:00
orignal
f7791e5289 fixed crash for Windows 2014-11-01 21:48:26 -04:00
orignal
a8acb8ebb4 fixed double deletion 2014-11-01 17:15:59 -04:00
orignal
4334007688 aligned AES and MAC keys 2014-11-01 14:56:13 -04:00
orignal
7a976dd5f2 specify actual mtu in our RI for i2pv6 2014-10-31 18:27:51 -04:00
orignal
f28376bf71 DATAGRAM RECEIVED message 2014-10-31 16:44:44 -04:00
orignal
3b435f1e1d get local MTU for Linux 2014-10-31 14:34:08 -04:00
orignal
a314feff33 get local MTU for Linux 2014-10-31 14:17:52 -04:00
orignal
0b77c9651b 0.2.0 2014-10-30 18:04:17 -04:00
orignal
6b860c8c88 split SSU to SSU and SSUSession 2014-10-30 15:13:29 -04:00
orignal
0cd04c23c5 split SSU to SSU and SSUSession 2014-10-30 15:13:12 -04:00
222 changed files with 23679 additions and 26581 deletions

4
.gitignore vendored
View File

@@ -3,6 +3,7 @@ obj/*.o
router.info
router.keys
i2p
libi2pd.so
netDb
# Autotools
@@ -229,3 +230,6 @@ pip-log.txt
#Mr Developer
.mr.developer.cfg
# Sphinx
docs/_build

31
.travis.yml Normal file
View File

@@ -0,0 +1,31 @@
language: cpp
cache:
apt: true
os:
- linux
sudo: required
dist: trusty
addons:
apt:
packages:
- build-essential
- cmake
- g++
- clang
- libboost-chrono-dev
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-program-options-dev
- libboost-regex-dev
- libboost-system-dev
- libboost-thread-dev
- libminiupnpc-dev
- libssl-dev
compiler:
- gcc
env:
matrix:
- BUILD_TYPE=Release UPNP=ON
- BUILD_TYPE=Release UPNP=OFF
script:
- cd build && cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DWITH_UPNP=${UPNP} && make

View File

@@ -1,23 +1,204 @@
#include <string.h>
#include <inttypes.h>
#include <string>
#include <map>
#include "base64.h"
#include <fstream>
#include <chrono>
#include <condition_variable>
#include <boost/lexical_cast.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
#include "FS.h"
#include "Log.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
#include <boost/algorithm/string.hpp>
namespace i2p
{
namespace client
{
// TODO: this is actually proxy class
class AddressBookFilesystemStorage: public AddressBookStorage
{
private:
i2p::fs::HashedStorage storage;
std::string indexPath;
AddressBook::AddressBook (): m_IsLoaded (false), m_IsDowloading (false)
public:
AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") {};
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const;
void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void RemoveAddress (const i2p::data::IdentHash& ident);
bool Init ();
int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
};
bool AddressBookFilesystemStorage::Init()
{
storage.SetPlace(i2p::fs::GetDataDir());
indexPath = storage.GetRoot() + i2p::fs::dirSep + "addresses.csv";
return storage.Init(i2p::data::GetBase32SubstitutionTable(), 32);
}
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
{
std::string filename = storage.Path(ident.ToBase32());
std::ifstream f(filename, std::ifstream::binary);
if (!f.is_open ()) {
LogPrint(eLogDebug, "Addressbook: Requested, but not found: ", filename);
return nullptr;
}
f.seekg (0,std::ios::end);
size_t len = f.tellg ();
if (len < i2p::data::DEFAULT_IDENTITY_SIZE) {
LogPrint (eLogError, "Addresbook: File ", filename, " is too short: ", len);
return nullptr;
}
f.seekg(0, std::ios::beg);
uint8_t * buf = new uint8_t[len];
f.read((char *)buf, len);
auto address = std::make_shared<i2p::data::IdentityEx>(buf, len);
delete[] buf;
return address;
}
void AddressBookFilesystemStorage::AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{
std::string path = storage.Path( address->GetIdentHash().ToBase32() );
std::ofstream f (path, std::ofstream::binary | std::ofstream::out);
if (!f.is_open ()) {
LogPrint (eLogError, "Addresbook: can't open file ", path);
return;
}
size_t len = address->GetFullLen ();
uint8_t * buf = new uint8_t[len];
address->ToBuffer (buf, len);
f.write ((char *)buf, len);
delete[] buf;
}
void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident)
{
storage.Remove( ident.ToBase32() );
}
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = 0;
std::string s;
std::ifstream f (indexPath, std::ifstream::in); // in text mode
if (f.is_open ()) {
LogPrint(eLogInfo, "Addressbook: using index file ", indexPath);
} else {
LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath);
return 0;
}
addresses.clear ();
while (!f.eof ()) {
getline(f, s);
if (!s.length())
continue; // skip empty line
std::size_t pos = s.find(',');
if (pos != std::string::npos)
{
std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos);
i2p::data::IdentHash ident;
ident.FromBase32 (addr);
addresses[name] = ident;
num++;
}
}
LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage");
return num;
}
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
{
if (addresses.size() == 0) {
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
return 0;
}
int num = 0;
std::ofstream f (indexPath, std::ofstream::out); // in text mode
if (!f.is_open ()) {
LogPrint (eLogWarning, "Addressbook: Can't open ", indexPath);
return 0;
}
for (auto it: addresses) {
f << it.first << "," << it.second.ToBase32 () << std::endl;
num++;
}
LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved");
return num;
}
//---------------------------------------------------------------------
AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false),
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{
}
AddressBook::~AddressBook ()
{
Stop ();
}
void AddressBook::Start ()
{
m_Storage->Init();
LoadHosts (); /* try storage, then hosts.txt, then download */
StartSubscriptions ();
}
void AddressBook::Stop ()
{
StopSubscriptions ();
if (m_SubscriptionsUpdateTimer)
{
delete m_SubscriptionsUpdateTimer;
m_SubscriptionsUpdateTimer = nullptr;
}
if (m_IsDownloading)
{
LogPrint (eLogInfo, "Addresbook: subscriptions is downloading, abort");
for (int i = 0; i < 30; i++)
{
if (!m_IsDownloading)
{
LogPrint (eLogInfo, "Addresbook: subscriptions download complete");
break;
}
std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds
}
LogPrint (eLogError, "Addresbook: subscription download timeout");
m_IsDownloading = false;
}
if (m_Storage)
{
m_Storage->Save (m_Addresses);
delete m_Storage;
m_Storage = nullptr;
}
m_DefaultSubscription = nullptr;
for (auto it: m_Subscriptions)
delete it;
m_Subscriptions.clear ();
}
bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident)
{
auto pos = address.find(".b32.i2p");
@@ -37,73 +218,69 @@ namespace client
ident = *identHash;
return true;
}
else
return false;
}
}
return false;
// if not .b32 we assume full base64 address
i2p::data::IdentityEx dest;
if (!dest.FromBase64 (address))
return false;
ident = dest.GetIdentHash ();
return true;
}
const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address)
{
if (!m_IsLoaded)
LoadHosts ();
if (m_IsLoaded)
{
auto it = m_Addresses.find (address);
if (it != m_Addresses.end ())
return &it->second;
}
auto it = m_Addresses.find (address);
if (it != m_Addresses.end ())
return &it->second;
return nullptr;
}
void AddressBook::InsertAddress (const std::string& address, const std::string& base64)
{
i2p::data::IdentityEx ident;
ident.FromBase64 (base64);
m_Addresses[address] = ident.GetIdentHash ();
LogPrint (address,"->",ident.GetIdentHash ().ToBase32 (), ".b32.i2p added");
auto ident = std::make_shared<i2p::data::IdentityEx>();
ident->FromBase64 (base64);
m_Storage->AddAddress (ident);
m_Addresses[address] = ident->GetIdentHash ();
LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ()));
}
void AddressBook::LoadHostsFromI2P ()
void AddressBook::InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{
std::string content;
int http_code = i2p::util::http::httpRequestViaI2pProxy("http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt", content);
if (http_code == 200)
{
std::ofstream f_save(i2p::util::filesystem::GetFullPath("hosts.txt").c_str(), std::ofstream::out);
if (f_save.is_open())
{
f_save << content;
f_save.close();
}
else
LogPrint("Can't write hosts.txt");
m_IsLoaded = false;
}
else
LogPrint ("Failed to download hosts.txt");
m_IsDowloading = false;
return;
m_Storage->AddAddress (address);
}
std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetAddress (const std::string& address)
{
i2p::data::IdentHash ident;
if (!GetIdentHash (address, ident)) return nullptr;
return m_Storage->GetAddress (ident);
}
void AddressBook::LoadHosts ()
{
std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ofstream::in); // in text mode
if (!f.is_open ())
if (m_Storage->Load (m_Addresses) > 0)
{
LogPrint ("hosts.txt not found. Try to load...");
if (!m_IsDowloading)
{
m_IsDowloading = true;
std::thread load_hosts(&AddressBook::LoadHostsFromI2P, this);
load_hosts.detach();
}
m_IsLoaded = true;
return;
}
// then try hosts.txt
std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode
if (f.is_open ())
{
LoadHostsFromStream (f);
m_IsLoaded = true;
}
}
void AddressBook::LoadHostsFromStream (std::istream& f)
{
std::unique_lock<std::mutex> l(m_AddressBookMutex);
int numAddresses = 0;
std::string s;
while (!f.eof ())
{
getline(f, s);
@@ -118,16 +295,288 @@ namespace client
std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos);
i2p::data::IdentityEx ident;
ident.FromBase64(addr);
m_Addresses[name] = ident.GetIdentHash ();
numAddresses++;
auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (ident->FromBase64(addr))
{
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
numAddresses++;
}
else
LogPrint (eLogError, "Addresbook: malformed address ", addr, " for ", name);
}
}
LogPrint (numAddresses, " addresses loaded");
m_IsLoaded = true;
LogPrint (eLogInfo, "Addresbook: ", numAddresses, " addresses processed");
if (numAddresses > 0)
{
m_IsLoaded = true;
m_Storage->Save (m_Addresses);
}
}
void AddressBook::LoadSubscriptions ()
{
if (!m_Subscriptions.size ())
{
std::ifstream f (i2p::fs::DataDirPath ("subscriptions.txt"), std::ifstream::in); // in text mode
if (f.is_open ())
{
std::string s;
while (!f.eof ())
{
getline(f, s);
if (!s.length()) continue; // skip empty line
m_Subscriptions.push_back (new AddressBookSubscription (*this, s));
}
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
}
else
LogPrint (eLogWarning, "Addresbook: subscriptions.txt not found in datadir");
}
else
LogPrint (eLogError, "Addressbook: subscriptions already loaded");
}
void AddressBook::DownloadComplete (bool success)
{
m_IsDownloading = false;
if (success && m_DefaultSubscription)
{
m_DefaultSubscription.reset (nullptr);
m_IsLoaded = true;
}
if (m_SubscriptionsUpdateTimer)
{
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(
success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
}
void AddressBook::StartSubscriptions ()
{
LoadSubscriptions ();
if (m_IsLoaded && m_Subscriptions.empty ()) return;
auto dest = i2p::client::context.GetSharedLocalDestination ();
if (dest)
{
m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ());
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
else
LogPrint (eLogError, "Addresbook: can't start subscriptions: missing shared local destination");
}
void AddressBook::StopSubscriptions ()
{
if (m_SubscriptionsUpdateTimer)
m_SubscriptionsUpdateTimer->cancel ();
}
void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto dest = i2p::client::context.GetSharedLocalDestination ();
if (!dest) {
LogPrint(eLogWarning, "Addressbook: missing local destination, skip subscription update");
return;
}
if (!m_IsDownloading && dest->IsReady ())
{
if (!m_IsLoaded)
{
// download it from http://i2p-projekt.i2p/hosts.txt
LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription.");
if (!m_DefaultSubscription)
m_DefaultSubscription.reset (new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS));
m_IsDownloading = true;
m_DefaultSubscription->CheckSubscription ();
}
else if (!m_Subscriptions.empty ())
{
// pick random subscription
auto ind = rand () % m_Subscriptions.size();
m_IsDownloading = true;
m_Subscriptions[ind]->CheckSubscription ();
}
}
else
{
// try it again later
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
}
}
AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link):
m_Book (book), m_Link (link)
{
}
void AddressBookSubscription::CheckSubscription ()
{
std::thread load_hosts(&AddressBookSubscription::Request, this);
load_hosts.detach(); // TODO: use join
}
void AddressBookSubscription::Request ()
{
// must be run in separate thread
LogPrint (eLogInfo, "Addresbook: 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))
{
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, "Addresbook: 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;
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);
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: ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
if (!response.eof ())
{
success = true;
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");
}
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);
}
bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip)
{
if (isGzip)
{
std::stringstream uncompressed;
i2p::data::GzipInflator inflator;
inflator.Inflate (s, uncompressed);
if (!uncompressed.fail ())
m_Book.LoadHostsFromStream (uncompressed);
else
return false;
}
else
m_Book.LoadHostsFromStream (s);
return true;
}
}
}

View File

@@ -4,8 +4,12 @@
#include <string.h>
#include <string>
#include <map>
#include "base64.h"
#include "util.h"
#include <vector>
#include <iostream>
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "Base.h"
#include "Identity.h"
#include "Log.h"
@@ -13,22 +17,86 @@ namespace i2p
{
namespace client
{
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/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)
const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes
const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
class AddressBookStorage // interface for storage
{
public:
virtual ~AddressBookStorage () {};
virtual std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const = 0;
virtual void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address) = 0;
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
virtual bool Init () = 0;
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
};
class AddressBookSubscription;
class AddressBook
{
public:
AddressBook ();
~AddressBook ();
void Start ();
void Stop ();
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address);
const i2p::data::IdentHash * FindAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success);
//This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); }
private:
void StartSubscriptions ();
void StopSubscriptions ();
void LoadHosts ();
void LoadSubscriptions ();
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
private:
std::mutex m_AddressBookMutex;
std::map<std::string, i2p::data::IdentHash> m_Addresses;
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
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
};
class AddressBookSubscription
{
public:
AddressBookSubscription (AddressBook& book, const std::string& link);
void CheckSubscription ();
private:
void Request ();
bool ProcessResponse (std::stringstream& s, bool isGzip = false);
private:
void LoadHosts ();
void LoadHostsFromI2P ();
std::map<std::string, i2p::data::IdentHash> m_Addresses;
bool m_IsLoaded, m_IsDowloading;
AddressBook& m_Book;
std::string m_Link, m_Etag, m_LastModified;
};
}
}

643
BOB.cpp Normal file
View File

@@ -0,0 +1,643 @@
#include <string.h>
#include <boost/lexical_cast.hpp>
#include "Log.h"
#include "ClientContext.h"
#include "BOB.h"
namespace i2p
{
namespace client
{
BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, std::shared_ptr<ClientDestination> localDestination):
BOBI2PTunnel (localDestination),
m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port))
{
}
BOBI2PInboundTunnel::~BOBI2PInboundTunnel ()
{
Stop ();
}
void BOBI2PInboundTunnel::Start ()
{
m_Acceptor.listen ();
Accept ();
}
void BOBI2PInboundTunnel::Stop ()
{
m_Acceptor.close();
ClearHandlers ();
}
void BOBI2PInboundTunnel::Accept ()
{
auto receiver = std::make_shared<AddressReceiver> ();
receiver->socket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this,
std::placeholders::_1, receiver));
}
void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver)
{
if (!ecode)
{
Accept ();
ReceiveAddress (receiver);
}
}
void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr<AddressReceiver> receiver)
{
receiver->socket->async_read_some (boost::asio::buffer(
receiver->buffer + receiver->bufferOffset,
BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset),
std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this,
std::placeholders::_1, std::placeholders::_2, receiver));
}
void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<AddressReceiver> receiver)
{
if (ecode)
LogPrint (eLogError, "BOB: inbound tunnel read error: ", ecode.message ());
else
{
receiver->bufferOffset += bytes_transferred;
receiver->buffer[receiver->bufferOffset] = 0;
char * eol = strchr (receiver->buffer, '\n');
if (eol)
{
*eol = 0;
receiver->data = (uint8_t *)eol + 1;
receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
i2p::data::IdentHash ident;
if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident))
{
LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found");
return;
}
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
if (leaseSet)
CreateConnection (receiver, leaseSet);
else
GetLocalDestination ()->RequestDestination (ident,
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
this, std::placeholders::_1, receiver));
}
else
{
if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE)
ReceiveAddress (receiver);
else
LogPrint (eLogError, "BOB: missing inbound address");
}
}
}
void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver)
{
if (leaseSet)
CreateConnection (receiver, leaseSet);
else
LogPrint (eLogError, "BOB: LeaseSet for inbound destination not found");
}
void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
LogPrint (eLogDebug, "BOB: New inbound connection");
auto connection = std::make_shared<I2PTunnelConnection>(this, receiver->socket, leaseSet);
AddHandler (connection);
connection->I2PConnect (receiver->data, receiver->dataLen);
}
BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, bool quiet): BOBI2PTunnel (localDestination),
m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet)
{
}
void BOBI2POutboundTunnel::Start ()
{
Accept ();
}
void BOBI2POutboundTunnel::Stop ()
{
ClearHandlers ();
}
void BOBI2POutboundTunnel::Accept ()
{
auto localDestination = GetLocalDestination ();
if (localDestination)
localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1));
else
LogPrint (eLogError, "BOB: Local destination not set for server tunnel");
}
void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet);
AddHandler (conn);
conn->Connect ();
}
}
BOBDestination::BOBDestination (std::shared_ptr<ClientDestination> localDestination):
m_LocalDestination (localDestination),
m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr)
{
}
BOBDestination::~BOBDestination ()
{
delete m_OutboundTunnel;
delete m_InboundTunnel;
i2p::client::context.DeleteLocalDestination (m_LocalDestination);
}
void BOBDestination::Start ()
{
if (m_OutboundTunnel) m_OutboundTunnel->Start ();
if (m_InboundTunnel) m_InboundTunnel->Start ();
}
void BOBDestination::Stop ()
{
StopTunnels ();
m_LocalDestination->Stop ();
}
void BOBDestination::StopTunnels ()
{
if (m_OutboundTunnel)
{
m_OutboundTunnel->Stop ();
delete m_OutboundTunnel;
m_OutboundTunnel = nullptr;
}
if (m_InboundTunnel)
{
m_InboundTunnel->Stop ();
delete m_InboundTunnel;
m_InboundTunnel = nullptr;
}
}
void BOBDestination::CreateInboundTunnel (int port)
{
if (!m_InboundTunnel)
m_InboundTunnel = new BOBI2PInboundTunnel (port, m_LocalDestination);
}
void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet)
{
if (!m_OutboundTunnel)
m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, m_LocalDestination, quiet);
}
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)
{
}
BOBCommandSession::~BOBCommandSession ()
{
}
void BOBCommandSession::Terminate ()
{
m_Socket.close ();
m_IsOpen = false;
}
void BOBCommandSession::Receive ()
{
m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, BOB_COMMAND_BUFFER_SIZE - m_ReceiveBufferOffset),
std::bind(&BOBCommandSession::HandleReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void BOBCommandSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "BOB: command channel read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
size_t size = m_ReceiveBufferOffset + bytes_transferred;
m_ReceiveBuffer[size] = 0;
char * eol = strchr (m_ReceiveBuffer, '\n');
if (eol)
{
*eol = 0;
char * operand = strchr (m_ReceiveBuffer, ' ');
if (operand)
{
*operand = 0;
operand++;
}
else
operand = eol;
// process command
auto& handlers = m_Owner.GetCommandHandlers ();
auto it = handlers.find (m_ReceiveBuffer);
if (it != handlers.end ())
(this->*(it->second))(operand, eol - operand);
else
{
LogPrint (eLogError, "BOB: unknown command ", m_ReceiveBuffer);
SendReplyError ("unknown command");
}
m_ReceiveBufferOffset = size - (eol - m_ReceiveBuffer) - 1;
memmove (m_ReceiveBuffer, eol + 1, m_ReceiveBufferOffset);
}
else
{
if (size < BOB_COMMAND_BUFFER_SIZE)
m_ReceiveBufferOffset = size;
else
{
LogPrint (eLogError, "BOB: Malformed input of the command channel");
Terminate ();
}
}
}
}
void BOBCommandSession::Send (size_t len)
{
boost::asio::async_write (m_Socket, boost::asio::buffer (m_SendBuffer, len),
boost::asio::transfer_all (),
std::bind(&BOBCommandSession::HandleSent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "BOB: command channel send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (m_IsOpen)
Receive ();
else
Terminate ();
}
}
void BOBCommandSession::SendReplyOK (const char * msg)
{
#ifdef _MSC_VER
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg);
#else
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg);
#endif
Send (len);
}
void BOBCommandSession::SendReplyError (const char * msg)
{
#ifdef _MSC_VER
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg);
#else
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg);
#endif
Send (len);
}
void BOBCommandSession::SendVersion ()
{
size_t len = strlen (BOB_VERSION);
memcpy (m_SendBuffer, BOB_VERSION, len);
Send (len);
}
void BOBCommandSession::SendData (const char * nickname)
{
#ifdef _MSC_VER
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname);
#else
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname);
#endif
Send (len);
}
void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: zap");
Terminate ();
}
void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: quit");
m_IsOpen = false;
SendReplyOK ("Bye!");
}
void BOBCommandSession::StartCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: start ", m_Nickname);
if (!m_CurrentDestination)
{
m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
m_Owner.AddDestination (m_Nickname, m_CurrentDestination);
}
if (m_InPort)
m_CurrentDestination->CreateInboundTunnel (m_InPort);
if (m_OutPort && !m_Address.empty ())
m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet);
m_CurrentDestination->Start ();
SendReplyOK ("tunnel starting");
}
void BOBCommandSession::StopCommandHandler (const char * operand, size_t len)
{
auto dest = m_Owner.FindDestination (m_Nickname);
if (dest)
{
dest->StopTunnels ();
SendReplyOK ("tunnel stopping");
}
else
SendReplyError ("tunnel not found");
}
void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: setnick ", operand);
m_Nickname = operand;
std::string msg ("Nickname set to ");
msg += operand;
SendReplyOK (msg.c_str ());
}
void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: getnick ", operand);
m_CurrentDestination = m_Owner.FindDestination (operand);
if (m_CurrentDestination)
{
m_Keys = m_CurrentDestination->GetKeys ();
m_Nickname = operand;
std::string msg ("Nickname set to ");
msg += operand;
SendReplyOK (msg.c_str ());
}
else
SendReplyError ("tunnel not found");
}
void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: newkeys");
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys ();
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
}
void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: setkeys ", operand);
m_Keys.FromBase64 (operand);
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
}
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: getkeys");
SendReplyOK (m_Keys.ToBase64 ().c_str ());
}
void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: getdest");
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
}
void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: outhost ", operand);
m_Address = operand;
SendReplyOK ("outhost set");
}
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");
}
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: inhost ", operand);
m_Address = operand;
SendReplyOK ("inhost set");
}
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");
}
void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: quiet");
m_IsQuiet = true;
SendReplyOK ("quiet");
}
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)
{
SendReplyError ("Address Not found");
return;
}
auto localDestination = m_CurrentDestination->GetLocalDestination ();
auto leaseSet = localDestination->FindLeaseSet (ident);
if (leaseSet)
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
else
{
auto s = shared_from_this ();
localDestination->RequestDestination (ident,
[s](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
s->SendReplyError ("LeaseSet Not found");
}
);
}
}
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: clear");
m_Owner.DeleteDestination (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)
SendData (it.first.c_str ());
SendReplyOK ("Listing done");
}
void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: option ", operand);
const char * value = strchr (operand, '=');
if (value)
{
*(const_cast<char *>(value)) = 0;
m_Options[operand] = value + 1;
*(const_cast<char *>(value)) = '=';
SendReplyOK ("option");
}
else
SendReplyError ("malformed");
}
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
{
// command -> handler
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler;
m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler;
m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler;
m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler;
m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler;
m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler;
m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler;
m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler;
m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler;
m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler;
m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler;
m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler;
m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler;
m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler;
m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler;
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
}
BOBCommandChannel::~BOBCommandChannel ()
{
Stop ();
for (auto it: m_Destinations)
delete it.second;
}
void BOBCommandChannel::Start ()
{
Accept ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this));
}
void BOBCommandChannel::Stop ()
{
m_IsRunning = false;
for (auto it: m_Destinations)
it.second->Stop ();
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void BOBCommandChannel::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "BOB: runtime exception: ", ex.what ());
}
}
}
void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest)
{
m_Destinations[name] = dest;
}
void BOBCommandChannel::DeleteDestination (const std::string& name)
{
auto it = m_Destinations.find (name);
if (it != m_Destinations.end ())
{
it->second->Stop ();
delete it->second;
m_Destinations.erase (it);
}
}
BOBDestination * BOBCommandChannel::FindDestination (const std::string& name)
{
auto it = m_Destinations.find (name);
if (it != m_Destinations.end ())
return it->second;
return nullptr;
}
void BOBCommandChannel::Accept ()
{
auto newSession = std::make_shared<BOBCommandSession> (*this);
m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this,
std::placeholders::_1, newSession));
}
void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session)
{
if (ecode != boost::asio::error::operation_aborted)
Accept ();
if (!ecode)
{
LogPrint (eLogInfo, "BOB: New command connection from ", session->GetSocket ().remote_endpoint ());
session->SendVersion ();
}
else
LogPrint (eLogError, "BOB: accept error: ", ecode.message ());
}
}
}

237
BOB.h Normal file
View File

@@ -0,0 +1,237 @@
#ifndef BOB_H__
#define BOB_H__
#include <inttypes.h>
#include <thread>
#include <memory>
#include <map>
#include <string>
#include <boost/asio.hpp>
#include "I2PTunnel.h"
#include "I2PService.h"
#include "Identity.h"
#include "LeaseSet.h"
namespace i2p
{
namespace client
{
const size_t BOB_COMMAND_BUFFER_SIZE = 1024;
const char BOB_COMMAND_ZAP[] = "zap";
const char BOB_COMMAND_QUIT[] = "quit";
const char BOB_COMMAND_START[] = "start";
const char BOB_COMMAND_STOP[] = "stop";
const char BOB_COMMAND_SETNICK[] = "setnick";
const char BOB_COMMAND_GETNICK[] = "getnick";
const char BOB_COMMAND_NEWKEYS[] = "newkeys";
const char BOB_COMMAND_GETKEYS[] = "getkeys";
const char BOB_COMMAND_SETKEYS[] = "setkeys";
const char BOB_COMMAND_GETDEST[] = "getdest";
const char BOB_COMMAND_OUTHOST[] = "outhost";
const char BOB_COMMAND_OUTPORT[] = "outport";
const char BOB_COMMAND_INHOST[] = "inhost";
const char BOB_COMMAND_INPORT[] = "inport";
const char BOB_COMMAND_QUIET[] = "quiet";
const char BOB_COMMAND_LOOKUP[] = "lookup";
const char BOB_COMMAND_CLEAR[] = "clear";
const char BOB_COMMAND_LIST[] = "list";
const char BOB_COMMAND_OPTION[] = "option";
const char BOB_VERSION[] = "BOB 00.00.10\nOK\n";
const char BOB_REPLY_OK[] = "OK %s\n";
const char BOB_REPLY_ERROR[] = "ERROR %s\n";
const char BOB_DATA[] = "NICKNAME %s\n";
class BOBI2PTunnel: public I2PService
{
public:
BOBI2PTunnel (std::shared_ptr<ClientDestination> localDestination):
I2PService (localDestination) {};
virtual void Start () {};
virtual void Stop () {};
};
class BOBI2PInboundTunnel: public BOBI2PTunnel
{
struct AddressReceiver
{
std::shared_ptr<boost::asio::ip::tcp::socket> socket;
char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address
uint8_t * data; // pointer to buffer
size_t dataLen, bufferOffset;
AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {};
};
public:
BOBI2PInboundTunnel (int port, std::shared_ptr<ClientDestination> localDestination);
~BOBI2PInboundTunnel ();
void Start ();
void Stop ();
private:
void Accept ();
void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver);
void ReceiveAddress (std::shared_ptr<AddressReceiver> receiver);
void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<AddressReceiver> receiver);
void HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver);
void CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet);
private:
boost::asio::ip::tcp::acceptor m_Acceptor;
};
class BOBI2POutboundTunnel: public BOBI2PTunnel
{
public:
BOBI2POutboundTunnel (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, bool quiet);
void Start ();
void Stop ();
void SetQuiet () { m_IsQuiet = true; };
private:
void Accept ();
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
private:
boost::asio::ip::tcp::endpoint m_Endpoint;
bool m_IsQuiet;
};
class BOBDestination
{
public:
BOBDestination (std::shared_ptr<ClientDestination> localDestination);
~BOBDestination ();
void Start ();
void Stop ();
void StopTunnels ();
void CreateInboundTunnel (int port);
void CreateOutboundTunnel (const std::string& address, int port, bool quiet);
const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); };
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDestination; };
private:
std::shared_ptr<ClientDestination> m_LocalDestination;
BOBI2POutboundTunnel * m_OutboundTunnel;
BOBI2PInboundTunnel * m_InboundTunnel;
};
class BOBCommandChannel;
class BOBCommandSession: public std::enable_shared_from_this<BOBCommandSession>
{
public:
BOBCommandSession (BOBCommandChannel& owner);
~BOBCommandSession ();
void Terminate ();
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
void SendVersion ();
// command handlers
void ZapCommandHandler (const char * operand, size_t len);
void QuitCommandHandler (const char * operand, size_t len);
void StartCommandHandler (const char * operand, size_t len);
void StopCommandHandler (const char * operand, size_t len);
void SetNickCommandHandler (const char * operand, size_t len);
void GetNickCommandHandler (const char * operand, size_t len);
void NewkeysCommandHandler (const char * operand, size_t len);
void SetkeysCommandHandler (const char * operand, size_t len);
void GetkeysCommandHandler (const char * operand, size_t len);
void GetdestCommandHandler (const char * operand, size_t len);
void OuthostCommandHandler (const char * operand, size_t len);
void OutportCommandHandler (const char * operand, size_t len);
void InhostCommandHandler (const char * operand, size_t len);
void InportCommandHandler (const char * operand, size_t len);
void QuietCommandHandler (const char * operand, size_t len);
void LookupCommandHandler (const char * operand, size_t len);
void ClearCommandHandler (const char * operand, size_t len);
void ListCommandHandler (const char * operand, size_t len);
void OptionCommandHandler (const char * operand, size_t len);
private:
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void Send (size_t len);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void SendReplyOK (const char * msg);
void SendReplyError (const char * msg);
void SendData (const char * nickname);
private:
BOBCommandChannel& m_Owner;
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;
std::string m_Nickname, m_Address;
int m_InPort, m_OutPort;
i2p::data::PrivateKeys m_Keys;
std::map<std::string, std::string> m_Options;
BOBDestination * m_CurrentDestination;
};
typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len);
class BOBCommandChannel
{
public:
BOBCommandChannel (const std::string& address, int port);
~BOBCommandChannel ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
void AddDestination (const std::string& name, BOBDestination * dest);
void DeleteDestination (const std::string& name);
BOBDestination * FindDestination (const std::string& name);
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
std::map<std::string, BOBDestination *> m_Destinations;
std::map<std::string, BOBCommandHandler> m_CommandHandlers;
public:
const decltype(m_CommandHandlers)& GetCommandHandlers () const { return m_CommandHandlers; };
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
};
}
}
#endif

View File

@@ -1,10 +1,22 @@
#include <stdlib.h>
#include "base64.h"
#include "Log.h"
#include "Base.h"
namespace i2p
{
namespace data
{
static const char T32[32] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'k', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 't', 't', 'u', 'v', 'w', 'x',
'y', 'z', '2', '3', '4', '5', '6', '7',
};
const char * GetBase32SubstitutionTable ()
{
return T32;
}
static void iT64Build(void);
@@ -16,7 +28,7 @@ namespace data
* Direct Substitution Table
*/
static char T64[64] = {
static const char T64[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
@@ -79,7 +91,7 @@ namespace data
outCount = 4*n;
else
outCount = 4*(n+1);
if (outCount > len) return -1;
if (outCount > len) return 0;
pd = (unsigned char *)OutBuffer;
for ( i = 0; i<n; i++ ){
acc_1 = *ps++;
@@ -154,11 +166,11 @@ namespace data
if (isFirstTime) iT64Build();
n = InCount/4;
m = InCount%4;
if (!m)
if (InCount && !m)
outCount = 3*n;
else {
outCount = 0;
return -1;
return 0;
}
ps = (unsigned char *)(InBuffer + InCount - 1);
@@ -190,6 +202,13 @@ namespace data
return outCount;
}
size_t Base64EncodingBufferSize (const size_t input_size)
{
auto d = div (input_size, 3);
if (d.rem) d.quot++;
return 4*d.quot;
}
/*
*
* iT64
@@ -265,5 +284,107 @@ 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;
}
}
}
}

134
Base.h Normal file
View File

@@ -0,0 +1,134 @@
#ifndef BASE_H__
#define BASE_H__
#include <inttypes.h>
#include <string.h>
#include <string>
#include <zlib.h>
#include <iostream>
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 ();
const char * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
/**
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;
};
}
}
#endif

View File

@@ -1,5 +1,11 @@
#include "util.h"
#include <fstream>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "Config.h"
#include "FS.h"
#include "Log.h"
#include "Identity.h"
#include "ClientContext.h"
namespace i2p
@@ -9,8 +15,8 @@ namespace client
ClientContext context;
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_IrcTunnel (nullptr),
m_ServerTunnel (nullptr), m_SamBridge (nullptr)
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
m_BOBCommandChannel (nullptr)
{
}
@@ -18,141 +24,178 @@ namespace client
{
delete m_HttpProxy;
delete m_SocksProxy;
delete m_IrcTunnel;
delete m_ServerTunnel;
delete m_SamBridge;
delete m_BOBCommandChannel;
}
void ClientContext::Start ()
{
if (!m_SharedLocalDestination)
{
m_SharedLocalDestination = new ClientDestination (false, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1); // non-public, DSA
m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination;
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination;
m_SharedLocalDestination->Start ();
}
m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446));
m_HttpProxy->Start();
LogPrint("HTTP Proxy started");
m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447));
m_SocksProxy->Start();
LogPrint("SOCKS Proxy Started");
std::string ircDestination = i2p::util::config::GetArg("-ircdest", "");
if (ircDestination.length () > 0) // ircdest is presented
{
ClientDestination * localDestination = nullptr;
std::string ircKeys = i2p::util::config::GetArg("-irckeys", "");
if (ircKeys.length () > 0)
localDestination = i2p::client::context.LoadLocalDestination (ircKeys, false);
m_IrcTunnel = new I2PClientTunnel (m_SocksProxy->GetService (), ircDestination,
i2p::util::config::GetArg("-ircport", 6668), localDestination);
m_IrcTunnel->Start ();
LogPrint("IRC tunnel started");
}
std::string eepKeys = i2p::util::config::GetArg("-eepkeys", "");
if (eepKeys.length () > 0) // eepkeys file is presented
{
auto localDestination = i2p::client::context.LoadLocalDestination (eepKeys, true);
m_ServerTunnel = new I2PServerTunnel (m_SocksProxy->GetService (),
i2p::util::config::GetArg("-eephost", "127.0.0.1"), i2p::util::config::GetArg("-eepport", 80),
localDestination);
m_ServerTunnel->Start ();
LogPrint("Server tunnel started");
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);
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
if (httpProxyKeys.length () > 0)
{
i2p::data::PrivateKeys keys;
LoadPrivateKeys (keys, httpProxyKeys);
localDestination = CreateNewLocalDestination (keys, false);
}
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
m_HttpProxy->Start();
}
int samPort = i2p::util::config::GetArg("-samport", 0);
if (samPort)
{
m_SamBridge = new SAMBridge (samPort);
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", 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);
LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort);
if (socksProxyKeys.length () > 0)
{
i2p::data::PrivateKeys keys;
LoadPrivateKeys (keys, socksProxyKeys);
localDestination = CreateNewLocalDestination (keys, false);
}
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
m_SocksProxy->Start();
}
// I2P tunnels
ReadTunnels ();
// SAM
bool sam; i2p::config::GetOption("sam.enabled", sam);
if (sam) {
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
m_SamBridge = new SAMBridge (samAddr, samPort);
m_SamBridge->Start ();
LogPrint("SAM bridge started");
}
// BOB
bool bob; i2p::config::GetOption("bob.enabled", bob);
if (bob) {
std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr);
uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort);
LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort);
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
m_BOBCommandChannel->Start ();
}
}
void ClientContext::Stop ()
{
m_HttpProxy->Stop();
delete m_HttpProxy;
m_HttpProxy = nullptr;
LogPrint("HTTP Proxy stoped");
m_SocksProxy->Stop();
delete m_SocksProxy;
m_SocksProxy = nullptr;
LogPrint("SOCKS Proxy stoped");
if (m_IrcTunnel)
if (m_HttpProxy)
{
m_IrcTunnel->Stop ();
delete m_IrcTunnel;
m_IrcTunnel = nullptr;
LogPrint("IRC tunnel stoped");
LogPrint(eLogInfo, "Clients: stopping HTTP Proxy");
m_HttpProxy->Stop();
delete m_HttpProxy;
m_HttpProxy = nullptr;
}
if (m_ServerTunnel)
if (m_SocksProxy)
{
m_ServerTunnel->Stop ();
delete m_ServerTunnel;
m_ServerTunnel = nullptr;
LogPrint("Server tunnel stoped");
}
LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy");
m_SocksProxy->Stop();
delete m_SocksProxy;
m_SocksProxy = nullptr;
}
for (auto& it: m_ClientTunnels)
{
LogPrint(eLogInfo, "Clients: stopping I2P client tunnel on port ", it.first);
it.second->Stop ();
}
m_ClientTunnels.clear ();
for (auto& it: m_ServerTunnels)
{
LogPrint(eLogInfo, "Clients: stopping I2P server tunnel");
it.second->Stop ();
}
m_ServerTunnels.clear ();
if (m_SamBridge)
{
LogPrint(eLogInfo, "Clients: stopping SAM bridge");
m_SamBridge->Stop ();
delete m_SamBridge;
m_SamBridge = nullptr;
LogPrint("SAM brdige stoped");
}
for (auto it: m_Destinations)
{
it.second->Stop ();
delete it.second;
}
m_Destinations.clear ();
m_SharedLocalDestination = 0; // deleted through m_Destination
}
void ClientContext::LoadLocalDestinations ()
{
int numDestinations = 0;
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
if (m_BOBCommandChannel)
{
if (boost::filesystem::is_regular_file (*it) && it->path ().extension () == ".dat")
{
auto fullPath =
#if BOOST_VERSION > 10500
it->path().string();
#else
it->path();
#endif
auto localDestination = new ClientDestination (fullPath, true);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
numDestinations++;
}
}
if (numDestinations > 0)
LogPrint (numDestinations, " local destinations loaded");
LogPrint(eLogInfo, "Clients: stopping BOB command channel");
m_BOBCommandChannel->Stop ();
delete m_BOBCommandChannel;
m_BOBCommandChannel = nullptr;
}
LogPrint(eLogInfo, "Clients: stopping AddressBook");
m_AddressBook.Stop ();
for (auto it: m_Destinations)
it.second->Stop ();
m_Destinations.clear ();
m_SharedLocalDestination = nullptr;
}
ClientDestination * ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic)
void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
{
auto localDestination = new ClientDestination (i2p::util::filesystem::GetFullPath (filename), isPublic);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
std::string fullPath = i2p::fs::DataDirPath (filename);
std::ifstream s(fullPath, std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0, std::ios::end);
size_t len = s.tellg();
s.seekg (0, std::ios::beg);
uint8_t * buf = new uint8_t[len];
s.read ((char *)buf, len);
keys.FromBuffer (buf, len);
delete[] buf;
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
}
else
{
LogPrint (eLogError, "Clients: can't open file ", fullPath, " Creating new one with signature type ", sigType);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
size_t len = keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
len = keys.ToBuffer (buf, len);
f.write ((char *)buf, len);
delete[] buf;
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
}
}
ClientDestination * ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType)
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
const std::map<std::string, std::string> * params)
{
auto localDestination = new ClientDestination (isPublic, sigType);
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
}
void ClientContext::DeleteLocalDestination (ClientDestination * destination)
void ClientContext::DeleteLocalDestination (std::shared_ptr<ClientDestination> destination)
{
if (!destination) return;
auto it = m_Destinations.find (destination->GetIdentHash ());
@@ -164,16 +207,16 @@ namespace client
m_Destinations.erase (it);
}
d->Stop ();
delete d;
}
}
ClientDestination * ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic)
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params)
{
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint ("Local destination ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p exists");
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
if (!it->second->IsRunning ())
{
it->second->Start ();
@@ -181,19 +224,157 @@ namespace client
}
return nullptr;
}
auto localDestination = new ClientDestination (keys, isPublic);
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination;
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
}
ClientDestination * ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const
std::shared_ptr<ClientDestination> ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const
{
auto it = m_Destinations.find (destination);
if (it != m_Destinations.end ())
return it->second;
return nullptr;
}
template<typename Section, typename Type>
std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const
{
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
}
template<typename Section>
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
{
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH);
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH);
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);
}
void ClientContext::ReadTunnels ()
{
boost::property_tree::ptree pt;
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
if (tunConf == "")
tunConf = i2p::fs::DataDirPath ("tunnels.cfg");
LogPrint(eLogDebug, "FS: tunnels config file: ", tunConf);
try
{
boost::property_tree::read_ini (tunConf, pt);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ());
return;
}
int numClientTunnels = 0, numServerTunnels = 0;
for (auto& section: pt)
{
std::string name = section.first;
try
{
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT)
{
// mandatory params
std::string 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, "");
std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1");
int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0);
i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
// I2CP
std::map<std::string, std::string> options;
ReadI2CPOptions (section, options);
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (keys.length () > 0)
{
i2p::data::PrivateKeys k;
LoadPrivateKeys (k, keys, sigType);
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
localDestination = CreateNewLocalDestination (k, false, &options);
}
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
clientTunnel->Start ();
else
LogPrint (eLogError, "Clients: I2P client tunnel with port ", port, " already exists");
numClientTunnels++;
}
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC)
{
// mandatory params
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
int port = section.second.get<int> (I2P_SERVER_TUNNEL_PORT);
std::string keys = section.second.get<std::string> (I2P_SERVER_TUNNEL_KEYS);
// optional params
int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0);
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, "");
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);
// 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);
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
localDestination = CreateNewLocalDestination (k, true, &options);
I2PServerTunnel * serverTunnel;
if (type == I2P_TUNNELS_SECTION_TYPE_HTTP)
serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip);
else if (type == I2P_TUNNELS_SECTION_TYPE_IRC)
serverTunnel = new I2PServerTunnelIRC (name, host, port, localDestination, webircpass, inPort, gzip);
else // regular server tunnel by default
serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip);
if (accessList.length () > 0)
{
std::set<i2p::data::IdentHash> idents;
size_t pos = 0, comma;
do
{
comma = accessList.find (',', pos);
i2p::data::IdentHash ident;
ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
idents.insert (ident);
pos = comma + 1;
}
while (comma != std::string::npos);
serverTunnel->SetAccessList (idents);
}
if (m_ServerTunnels.insert (std::make_pair (
std::make_tuple (localDestination->GetIdentHash (), inPort),
std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
serverTunnel->Start ();
else
LogPrint (eLogError, "Clients: I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
numServerTunnels++;
}
else
LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ());
}
}
LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created");
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
}
}
}

View File

@@ -1,18 +1,43 @@
#ifndef CLIENT_CONTEXT_H__
#define CLIENT_CONTEXT_H__
#include <map>
#include <tuple>
#include <mutex>
#include <memory>
#include "Destination.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
#include "I2PTunnel.h"
#include "SAM.h"
#include "BOB.h"
#include "AddressBook.h"
namespace i2p
{
namespace client
{
const char I2P_TUNNELS_SECTION_TYPE[] = "type";
const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "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_CLIENT_TUNNEL_PORT[] = "port";
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
const char I2P_CLIENT_TUNNEL_KEYS[] = "keys";
const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
const char I2P_SERVER_TUNNEL_HOST[] = "host";
const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride";
const char I2P_SERVER_TUNNEL_PORT[] = "port";
const char I2P_SERVER_TUNNEL_KEYS[] = "keys";
const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
const char I2P_SERVER_TUNNEL_INPORT[] = "inport";
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
class ClientContext
{
public:
@@ -23,36 +48,46 @@ namespace client
void Start ();
void Stop ();
ClientDestination * GetSharedLocalDestination () const { return m_SharedLocalDestination; };
ClientDestination * CreateNewLocalDestination (bool isPublic = true, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1); // transient
ClientDestination * CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true);
void DeleteLocalDestination (ClientDestination * destination);
ClientDestination * FindLocalDestination (const i2p::data::IdentHash& destination) const;
ClientDestination * LoadLocalDestination (const std::string& filename, bool isPublic);
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
std::shared_ptr<ClientDestination> CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
const std::map<std::string, std::string> * params = nullptr); // transient
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
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);
AddressBook& GetAddressBook () { return m_AddressBook; };
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
private:
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 LoadLocalDestinations ();
private:
std::mutex m_DestinationsMutex;
std::map<i2p::data::IdentHash, ClientDestination *> m_Destinations;
ClientDestination * m_SharedLocalDestination;
std::map<i2p::data::IdentHash, std::shared_ptr<ClientDestination> > m_Destinations;
std::shared_ptr<ClientDestination> m_SharedLocalDestination;
AddressBook m_AddressBook;
i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy;
I2PClientTunnel * m_IrcTunnel;
I2PServerTunnel * m_ServerTunnel;
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
std::map<std::tuple<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel;
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; };
};
extern ClientContext context;

244
Config.cpp Normal file
View File

@@ -0,0 +1,244 @@
/*
* 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 <cstdlib>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include "Config.h"
#include "version.h"
using namespace boost::program_options;
namespace i2p {
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")
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)")
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.cfg or /var/lib/i2pd/tunnels.cfg)")
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file (stdout if not set, file - otherwise, for compatibility)")
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
("loglevel", value<std::string>()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error)")
("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")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
("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<char>()->default_value('-'), "Bandwidth limiting: L - 32kbps, O - 256Kbps, P - unlimited")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
#endif
;
options_description httpserver("HTTP Server options");
httpserver.add_options()
("http.enabled", value<bool>()->default_value(true), "Enable or disable webconsole")
("http.address", value<std::string>()->default_value("127.0.0.1"), "Webconsole listen address")
("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port")
;
options_description httpproxy("HTTP Proxy options");
httpproxy.add_options()
("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy")
("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")
;
options_description socksproxy("SOCKS Proxy options");
socksproxy.add_options()
("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy")
("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.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
;
options_description sam("SAM bridge options");
sam.add_options()
("sam.enabled", value<bool>()->default_value(false), "Enable or disable SAM Application bridge")
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
;
options_description bob("BOB options");
bob.add_options()
("bob.enabled", value<bool>()->default_value(false), "Enable or disable BOB command channel")
("bob.address", value<std::string>()->default_value("127.0.0.1"), "BOB listen address")
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
;
options_description i2pcontrol("I2PControl options");
i2pcontrol.add_options()
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
;
m_OptionsDesc
.add(general)
.add(httpserver)
.add(httpproxy)
.add(socksproxy)
.add(sam)
.add(bob)
.add(i2pcontrol)
;
}
void ParseCmdline(int argc, char* argv[]) {
try {
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);
} catch (boost::program_options::error& e) {
std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
if (m_Options.count("help") || m_Options.count("h")) {
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
std::cout << m_OptionsDesc;
exit(EXIT_SUCCESS);
}
}
void ParseConfig(const std::string& path) {
if (path == "") return;
std::ifstream config(path, std::ios::in);
if (!config.is_open())
{
std::cerr << "missing/unreadable config file: " << path << std::endl;
exit(EXIT_FAILURE);
}
try
{
store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options);
}
catch (boost::program_options::error& e)
{
std::cerr << e.what() << std::endl;
exit(EXIT_FAILURE);
};
}
void Finalize() {
notify(m_Options);
}
bool IsDefault(const char *name) {
if (!m_Options.count(name))
throw "try to check non-existent option";
if (m_Options[name].defaulted())
return true;
return false;
}
} // namespace config
} // namespace i2p

107
Config.h Normal file
View File

@@ -0,0 +1,107 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <string>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
/**
* Functions to parse and store i2pd parameters
*
* General usage flow:
* Init() -- early as possible
* ParseCmdline() -- somewhere close to main()
* ParseConfig() -- after detecting path to config
* Finalize() -- right after all Parse*() functions called
* GetOption() -- may be called after Finalize()
*/
namespace i2p {
namespace config {
extern boost::program_options::variables_map m_Options;
/**
* @brief Initialize list of acceptable parameters
*
* Should be called before any Parse* functions.
*/
void Init();
/**
* @brief Parse cmdline parameters, and show help if requested
* @param argc Cmdline arguments count, should be passed from main().
* @param argv Cmdline parameters array, should be passed from main()
*
* If --help is given in parameters, shows it's list with description
* terminates the program with exitcode 0.
*
* In case of parameter misuse boost throws an exception.
* We internally handle type boost::program_options::unknown_option,
* and then terminate program with exitcode 1.
*
* Other exceptions will be passed to higher level.
*/
void ParseCmdline(int argc, char* argv[]);
/**
* @brief Load and parse given config file
* @param path Path to config file
*
* If error occured when opening file path is points to,
* we show the error message and terminate program.
*
* In case of parameter misuse boost throws an exception.
* We internally handle type boost::program_options::unknown_option,
* and then terminate program with exitcode 1.
*
* Other exceptions will be passed to higher level.
*/
void ParseConfig(const std::string& path);
/**
* @brief Used to combine options from cmdline, config and default values
*/
void Finalize();
/* @brief Accessor to parameters by name
* @param name Name of the requested parameter
* @param value Variable where to store option
* @return this function returns false if parameter not found
*
* @example uint16_t port; GetOption("sam.port", port);
*/
template<typename T>
bool GetOption(const char *name, T& value) {
if (!m_Options.count(name))
return false;
value = m_Options[name].as<T>();
return true;
}
/**
* @brief Set value of given parameter
* @param name Name of settable parameter
* @param value New parameter value
* @return true if value set up successful, false otherwise
*
* @example uint16_t port = 2827; SetOption("bob.port", port);
*/
template<typename T>
bool SetOption(const char *name, const T& value) {
if (!m_Options.count(name))
return false;
m_Options.at(name).value() = value;
notify(m_Options);
return true;
}
/**
* @brief Check is value explicitly given or default
* @param name Name of checked parameter
* @return true if value set to default, false othervise
*/
bool IsDefault(const char *name);
}
}
#endif // CONFIG_H

714
Crypto.cpp Normal file
View File

@@ -0,0 +1,714 @@
#include <string.h>
#include <string>
#include <vector>
#include <mutex>
#include <memory>
#include <openssl/sha.h>
#include <openssl/dh.h>
#include <openssl/md5.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
#include "TunnelBase.h"
#include <openssl/ssl.h>
#include "Log.h"
#include "Crypto.h"
namespace i2p
{
namespace crypto
{
const uint8_t elgp_[256]=
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
const int elgg_ = 2;
const uint8_t dsap_[128]=
{
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
};
const uint8_t dsaq_[20]=
{
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
0x68, 0x40, 0x46, 0xb7
};
const uint8_t dsag_[128]=
{
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
};
const int rsae_ = 65537;
struct CryptoConstants
{
// DH/ElGamal
BIGNUM * elgp;
BIGNUM * elgg;
// DSA
BIGNUM * dsap;
BIGNUM * dsaq;
BIGNUM * dsag;
// RSA
BIGNUM * rsae;
CryptoConstants (const uint8_t * elgp_, int elgg_, const uint8_t * dsap_,
const uint8_t * dsaq_, const uint8_t * dsag_, int rsae_)
{
elgp = BN_new ();
BN_bin2bn (elgp_, 256, elgp);
elgg = BN_new ();
BN_set_word (elgg, elgg_);
dsap = BN_new ();
BN_bin2bn (dsap_, 128, dsap);
dsaq = BN_new ();
BN_bin2bn (dsaq_, 20, dsaq);
dsag = BN_new ();
BN_bin2bn (dsag_, 128, dsag);
rsae = BN_new ();
BN_set_word (rsae, rsae_);
}
~CryptoConstants ()
{
BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae);
}
};
static const CryptoConstants& GetCryptoConstants ()
{
static CryptoConstants cryptoConstants (elgp_, elgg_, dsap_, dsaq_, dsag_, rsae_);
return cryptoConstants;
}
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len)
{
int offset = len - BN_num_bytes (bn);
if (offset < 0) return false;
BN_bn2bin (bn, buf + offset);
memset (buf, 0, offset);
return true;
}
// RSA
#define rsae GetCryptoConstants ().rsae
const BIGNUM * GetRSAE ()
{
return rsae;
}
// DSA
#define dsap GetCryptoConstants ().dsap
#define dsaq GetCryptoConstants ().dsaq
#define dsag GetCryptoConstants ().dsag
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;
return dsa;
}
// DH/ElGamal
#define elgp GetCryptoConstants ().elgp
#define elgg GetCryptoConstants ().elgg
// DH
DHKeys::DHKeys (): m_IsUpdated (true)
{
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;
}
DHKeys::~DHKeys ()
{
DH_free (m_DH);
}
void DHKeys::GenerateKeys (uint8_t * priv, uint8_t * pub)
{
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; };
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;
}
return m_PublicKey;
}
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
{
BIGNUM * pk = BN_bin2bn (pub, 256, NULL);
DH_compute_key (shared, pk, m_DH);
BN_free (pk);
}
// ElGamal
ElGamalEncryption::ElGamalEncryption (const uint8_t * key)
{
ctx = BN_CTX_new ();
// select random k
BIGNUM * k = BN_new ();
BN_rand_range (k, elgp);
if (BN_is_zero (k)) BN_one (k);
// caulculate a
a = BN_new ();
BN_mod_exp (a, elgg, k, elgp, ctx);
BIGNUM * y = BN_new ();
BN_bin2bn (key, 256, y);
// calculate b1
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);
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);
// copy a and b
if (zeroPadding)
{
encrypted[0] = 0;
bn2buf (a, encrypted + 1, 256);
encrypted[257] = 0;
bn2buf (b, encrypted + 258, 256);
}
else
{
bn2buf (a, encrypted, 256);
bn2buf (b, encrypted + 256, 256);
}
BN_free (b);
}
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
uint8_t * data, bool zeroPadding)
{
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * x = BN_new (), * a = BN_new (), * b = BN_new ();
BN_bin2bn (key, 256, x);
BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1
BN_bin2bn (zeroPadding ? encrypted + 1 : encrypted, 256, a);
BN_bin2bn (zeroPadding ? encrypted + 258 : encrypted + 256, 256, b);
// m = b*(a^x mod p) mod p
BN_mod_exp (x, a, x, elgp, ctx);
BN_mod_mul (b, b, x, elgp, ctx);
uint8_t m[255];
bn2buf (b, m, 255);
BN_free (x); BN_free (a); BN_free (b);
BN_CTX_free (ctx);
uint8_t hash[32];
SHA256 (m + 33, 222, hash);
if (memcmp (m + 1, hash, 32))
{
LogPrint (eLogError, "ElGamal decrypt hash doesn't match");
return false;
}
memcpy (data, m + 33, 222);
return true;
}
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub)
{
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
RAND_bytes (priv, 256);
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * p = BN_new ();
BN_bin2bn (priv, 256, p);
BN_mod_exp (p, elgg, p, elgp, ctx);
bn2buf (p, pub, 256);
BN_free (p);
BN_CTX_free (ctx);
#else
DHKeys dh;
dh.GenerateKeys (priv, pub);
#endif
}
// HMAC
const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
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];
// 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;
// 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);
// calculate digest
MD5((uint8_t *)buf, 96, digest);
}
// AES
#ifdef AESNI
#define KeyExpansion256(round0,round1) \
"pshufd $0xff, %%xmm2, %%xmm2 \n" \
"movaps %%xmm1, %%xmm4 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \
"pxor %%xmm2, %%xmm1 \n" \
"movaps %%xmm1, "#round0"(%[sched]) \n" \
"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
"pshufd $0xaa, %%xmm4, %%xmm2 \n" \
"movaps %%xmm3, %%xmm4 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \
"pxor %%xmm2, %%xmm3 \n" \
"movaps %%xmm3, "#round1"(%[sched]) \n"
void ECBCryptoAESNI::ExpandKey (const AESKey& key)
{
__asm__
(
"movups (%[key]), %%xmm1 \n"
"movups 16(%[key]), %%xmm3 \n"
"movaps %%xmm1, (%[sched]) \n"
"movaps %%xmm3, 16(%[sched]) \n"
"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
KeyExpansion256(32,48)
"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
KeyExpansion256(64,80)
"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
KeyExpansion256(96,112)
"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
KeyExpansion256(128,144)
"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
KeyExpansion256(160,176)
"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
KeyExpansion256(192,208)
"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
// key expansion final
"pshufd $0xff, %%xmm2, %%xmm2 \n"
"movaps %%xmm1, %%xmm4 \n"
"pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n"
"pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n"
"pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n"
"pxor %%xmm2, %%xmm1 \n"
"movups %%xmm1, 224(%[sched]) \n"
: // output
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
);
}
#define EncryptAES256(sched) \
"pxor (%["#sched"]), %%xmm0 \n" \
"aesenc 16(%["#sched"]), %%xmm0 \n" \
"aesenc 32(%["#sched"]), %%xmm0 \n" \
"aesenc 48(%["#sched"]), %%xmm0 \n" \
"aesenc 64(%["#sched"]), %%xmm0 \n" \
"aesenc 80(%["#sched"]), %%xmm0 \n" \
"aesenc 96(%["#sched"]), %%xmm0 \n" \
"aesenc 112(%["#sched"]), %%xmm0 \n" \
"aesenc 128(%["#sched"]), %%xmm0 \n" \
"aesenc 144(%["#sched"]), %%xmm0 \n" \
"aesenc 160(%["#sched"]), %%xmm0 \n" \
"aesenc 176(%["#sched"]), %%xmm0 \n" \
"aesenc 192(%["#sched"]), %%xmm0 \n" \
"aesenc 208(%["#sched"]), %%xmm0 \n" \
"aesenclast 224(%["#sched"]), %%xmm0 \n"
void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out)
{
__asm__
(
"movups (%[in]), %%xmm0 \n"
EncryptAES256(sched)
"movups %%xmm0, (%[out]) \n"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
);
}
#define DecryptAES256(sched) \
"pxor 224(%["#sched"]), %%xmm0 \n" \
"aesdec 208(%["#sched"]), %%xmm0 \n" \
"aesdec 192(%["#sched"]), %%xmm0 \n" \
"aesdec 176(%["#sched"]), %%xmm0 \n" \
"aesdec 160(%["#sched"]), %%xmm0 \n" \
"aesdec 144(%["#sched"]), %%xmm0 \n" \
"aesdec 128(%["#sched"]), %%xmm0 \n" \
"aesdec 112(%["#sched"]), %%xmm0 \n" \
"aesdec 96(%["#sched"]), %%xmm0 \n" \
"aesdec 80(%["#sched"]), %%xmm0 \n" \
"aesdec 64(%["#sched"]), %%xmm0 \n" \
"aesdec 48(%["#sched"]), %%xmm0 \n" \
"aesdec 32(%["#sched"]), %%xmm0 \n" \
"aesdec 16(%["#sched"]), %%xmm0 \n" \
"aesdeclast (%["#sched"]), %%xmm0 \n"
void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out)
{
__asm__
(
"movups (%[in]), %%xmm0 \n"
DecryptAES256(sched)
"movups %%xmm0, (%[out]) \n"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
);
}
#define CallAESIMC(offset) \
"movaps "#offset"(%[shed]), %%xmm0 \n" \
"aesimc %%xmm0, %%xmm0 \n" \
"movaps %%xmm0, "#offset"(%[shed]) \n"
void ECBDecryptionAESNI::SetKey (const AESKey& key)
{
ExpandKey (key); // expand encryption key first
// then invert it using aesimc
__asm__
(
CallAESIMC(16)
CallAESIMC(32)
CallAESIMC(48)
CallAESIMC(64)
CallAESIMC(80)
CallAESIMC(96)
CallAESIMC(112)
CallAESIMC(128)
CallAESIMC(144)
CallAESIMC(160)
CallAESIMC(176)
CallAESIMC(192)
CallAESIMC(208)
: : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory"
);
}
#endif
void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
{
#ifdef AESNI
__asm__
(
"movups (%[iv]), %%xmm1 \n"
"1: \n"
"movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched)
"movaps %%xmm0, %%xmm1 \n"
"movups %%xmm0, (%[out]) \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"dec %[num] \n"
"jnz 1b \n"
"movups %%xmm1, (%[iv]) \n"
:
: [iv]"r"(&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;
}
#endif
}
void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
{
// len/16
int numBlocks = len >> 4;
if (numBlocks > 0)
Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
}
void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out)
{
#ifdef AESNI
__asm__
(
"movups (%[iv]), %%xmm1 \n"
"movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched)
"movups %%xmm0, (%[out]) \n"
"movups %%xmm0, (%[iv]) \n"
:
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory"
);
#else
Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
#endif
}
void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
{
#ifdef AESNI
__asm__
(
"movups (%[iv]), %%xmm1 \n"
"1: \n"
"movups (%[in]), %%xmm0 \n"
"movaps %%xmm0, %%xmm2 \n"
DecryptAES256(sched)
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n"
"movaps %%xmm2, %%xmm1 \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"dec %[num] \n"
"jnz 1b \n"
"movups %%xmm1, (%[iv]) \n"
:
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
);
#else
for (int i = 0; i < numBlocks; i++)
{
ChipherBlock tmp = in[i];
m_ECBDecryption.Decrypt (in + i, out + i);
out[i] ^= m_IV;
m_IV = tmp;
}
#endif
}
void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
{
int numBlocks = len >> 4;
if (numBlocks > 0)
Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
}
void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out)
{
#ifdef AESNI
__asm__
(
"movups (%[iv]), %%xmm1 \n"
"movups (%[in]), %%xmm0 \n"
"movups %%xmm0, (%[iv]) \n"
DecryptAES256(sched)
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n"
:
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory"
);
#else
Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
#endif
}
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
{
#ifdef AESNI
__asm__
(
// encrypt IV
"movups (%[in]), %%xmm0 \n"
EncryptAES256(sched_iv)
"movaps %%xmm0, %%xmm1 \n"
// double IV encryption
EncryptAES256(sched_iv)
"movups %%xmm0, (%[out]) \n"
// encrypt data, IV is xmm1
"1: \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched_l)
"movaps %%xmm0, %%xmm1 \n"
"movups %%xmm0, (%[out]) \n"
"dec %[num] \n"
"jnz 1b \n"
:
: [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
: "%xmm0", "%xmm1", "cc", "memory"
);
#else
m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
m_LayerEncryption.SetIV (out);
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
#endif
}
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
{
#ifdef AESNI
__asm__
(
// decrypt IV
"movups (%[in]), %%xmm0 \n"
DecryptAES256(sched_iv)
"movaps %%xmm0, %%xmm1 \n"
// double IV encryption
DecryptAES256(sched_iv)
"movups %%xmm0, (%[out]) \n"
// decrypt data, IV is xmm1
"1: \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"movups (%[in]), %%xmm0 \n"
"movaps %%xmm0, %%xmm2 \n"
DecryptAES256(sched_l)
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n"
"movaps %%xmm2, %%xmm1 \n"
"dec %[num] \n"
"jnz 1b \n"
:
: [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
);
#else
m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
m_LayerDecryption.SetIV (out);
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
#endif
}
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;
static void OpensslLockingCallback(int mode, int type, const char * file, int line)
{
if (type > 0 && (size_t)type < m_OpenSSLMutexes.size ())
{
if (mode & CRYPTO_LOCK)
m_OpenSSLMutexes[type]->lock ();
else
m_OpenSSLMutexes[type]->unlock ();
}
}*/
void InitCrypto ()
{
SSL_library_init ();
/* auto numLocks = CRYPTO_num_locks();
for (int i = 0; i < numLocks; i++)
m_OpenSSLMutexes.emplace_back (new std::mutex);
CRYPTO_set_locking_callback (OpensslLockingCallback);*/
}
void TerminateCrypto ()
{
/* CRYPTO_set_locking_callback (nullptr);
m_OpenSSLMutexes.clear ();*/
}
}
}

281
Crypto.h Normal file
View File

@@ -0,0 +1,281 @@
#ifndef CRYPTO_H__
#define CRYPTO_H__
#include <inttypes.h>
#include <string>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/aes.h>
#include <openssl/dsa.h>
#include "Base.h"
namespace i2p
{
namespace crypto
{
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
// DSA
DSA * CreateDSA ();
// RSA
const BIGNUM * GetRSAE ();
// DH
class DHKeys
{
public:
DHKeys ();
~DHKeys ();
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
const uint8_t * GetPublicKey ();
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;
};
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false);
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
// HMAC
typedef i2p::data::Tag<32> MACKey;
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
// AES
struct ChipherBlock
{
uint8_t buf[16];
void operator^=(const ChipherBlock& other) // XOR
{
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE
__asm__
(
"movups (%[buf]), %%xmm0 \n"
"movups (%[other]), %%xmm1 \n"
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[buf]) \n"
:
: [buf]"r"(buf), [other]"r"(other.buf)
: "%xmm0", "%xmm1", "memory"
);
#else
// TODO: implement it better
for (int i = 0; i < 16; i++)
buf[i] ^= other.buf[i];
#endif
}
};
typedef i2p::data::Tag<32> AESKey;
template<size_t sz>
class AESAlignedBuffer // 16 bytes alignment
{
public:
AESAlignedBuffer ()
{
m_Buf = m_UnalignedBuffer;
uint8_t rem = ((size_t)m_Buf) & 0x0f;
if (rem)
m_Buf += (16 - rem);
}
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
private:
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
uint8_t * m_Buf;
};
#ifdef AESNI
class ECBCryptoAESNI
{
public:
uint8_t * GetKeySchedule () { return m_KeySchedule; };
protected:
void ExpandKey (const AESKey& key);
private:
AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes
};
class ECBEncryptionAESNI: public ECBCryptoAESNI
{
public:
void SetKey (const AESKey& key) { ExpandKey (key); };
void Encrypt (const ChipherBlock * in, ChipherBlock * out);
};
class ECBDecryptionAESNI: public ECBCryptoAESNI
{
public:
void SetKey (const AESKey& key);
void Decrypt (const ChipherBlock * in, ChipherBlock * out);
};
typedef ECBEncryptionAESNI ECBEncryption;
typedef ECBDecryptionAESNI ECBDecryption;
#else // use openssl
class ECBEncryption
{
public:
void SetKey (const AESKey& key)
{
AES_set_encrypt_key (key, 256, &m_Key);
}
void Encrypt (const ChipherBlock * in, ChipherBlock * out)
{
AES_encrypt (in->buf, out->buf, &m_Key);
}
private:
AES_KEY m_Key;
};
class ECBDecryption
{
public:
void SetKey (const AESKey& key)
{
AES_set_decrypt_key (key, 256, &m_Key);
}
void Decrypt (const ChipherBlock * in, ChipherBlock * out)
{
AES_decrypt (in->buf, out->buf, &m_Key);
}
private:
AES_KEY m_Key;
};
#endif
class CBCEncryption
{
public:
CBCEncryption () { memset (m_LastBlock.buf, 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 Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
void Encrypt (const uint8_t * in, uint8_t * out); // one block
private:
ChipherBlock m_LastBlock;
ECBEncryption m_ECBEncryption;
};
class CBCDecryption
{
public:
CBCDecryption () { memset (m_IV.buf, 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 Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
void Decrypt (const uint8_t * in, uint8_t * out); // one block
private:
ChipherBlock m_IV;
ECBDecryption m_ECBDecryption;
};
class TunnelEncryption // with double IV encryption
{
public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
{
m_LayerEncryption.SetKey (layerKey);
m_IVEncryption.SetKey (ivKey);
}
void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
private:
ECBEncryption m_IVEncryption;
#ifdef AESNI
ECBEncryption m_LayerEncryption;
#else
CBCEncryption m_LayerEncryption;
#endif
};
class TunnelDecryption // with double IV encryption
{
public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
{
m_LayerDecryption.SetKey (layerKey);
m_IVDecryption.SetKey (ivKey);
}
void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
private:
ECBDecryption m_IVDecryption;
#ifdef AESNI
ECBDecryption m_LayerDecryption;
#else
CBCDecryption m_LayerDecryption;
#endif
};
void InitCrypto ();
void TerminateCrypto ();
}
}
#endif

View File

@@ -28,7 +28,10 @@ namespace crypto
// DSA
#define dsap GetCryptoConstants ().dsap
#define dsaq GetCryptoConstants ().dsaq
#define dsag GetCryptoConstants ().dsag
#define dsag GetCryptoConstants ().dsag
// RSA
const int rsae = 65537;
}
}

View File

@@ -1,9 +1,12 @@
#include <thread>
#include <memory>
#include "Daemon.h"
#include "Config.h"
#include "Log.h"
#include "base64.h"
#include "FS.h"
#include "Base.h"
#include "version.h"
#include "Transports.h"
#include "NTCPSession.h"
@@ -12,11 +15,16 @@
#include "Tunnel.h"
#include "NetDb.h"
#include "Garlic.h"
#include "util.h"
#include "Streaming.h"
#include "Destination.h"
#include "HTTPServer.h"
#include "I2PControl.h"
#include "ClientContext.h"
#include "Crypto.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
namespace i2p
{
@@ -25,14 +33,15 @@ namespace i2p
class Daemon_Singleton::Daemon_Singleton_Private
{
public:
Daemon_Singleton_Private() : httpServer(nullptr)
{};
~Daemon_Singleton_Private()
{
delete httpServer;
};
Daemon_Singleton_Private() {};
~Daemon_Singleton_Private() {};
i2p::util::HTTPServer *httpServer;
std::unique_ptr<i2p::util::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
#ifdef USE_UPNP
i2p::transport::UPnP m_UPnP;
#endif
};
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
@@ -42,96 +51,188 @@ namespace i2p
bool Daemon_Singleton::IsService () const
{
bool service = false;
#ifndef _WIN32
return i2p::util::config::GetArg("-service", 0);
#else
return false;
i2p::config::GetOption("service", service);
#endif
return service;
}
bool Daemon_Singleton::init(int argc, char* argv[])
{
i2p::util::config::OptionParser(argc, argv);
i2p::config::Init();
i2p::config::ParseCmdline(argc, argv);
std::string config; i2p::config::GetOption("conf", config);
std::string datadir; i2p::config::GetOption("datadir", datadir);
i2p::fs::DetectDataDir(datadir, IsService());
i2p::fs::Init();
datadir = i2p::fs::GetDataDir();
if (config == "")
{
config = i2p::fs::DataDirPath("i2p.conf");
// use i2p.cong only if exists
if (!i2p::fs::Exists (config)) config = ""; /* reset */
}
i2p::config::ParseConfig(config);
i2p::config::Finalize();
i2p::crypto::InitCrypto ();
i2p::context.Init ();
LogPrint("\n\n\n\ni2pd starting\n");
LogPrint("Version ", VERSION);
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
i2p::config::GetOption("daemon", isDaemon);
isDaemon = i2p::util::config::GetArg("-daemon", 0);
isLogging = i2p::util::config::GetArg("-log", 1);
// TODO: move log init here
int port = i2p::util::config::GetArg("-port", 0);
if (port)
i2p::context.UpdatePort (port);
const char * host = i2p::util::config::GetCharArg("-host", "");
if (host && host[0])
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir);
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));
}
if (i2p::util::config::GetArg("-unreachable", 0))
i2p::context.SetUnreachable ();
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetAcceptsTunnels (!transit);
i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0));
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
char bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
if (isFloodfill)
{
LogPrint(eLogInfo, "Daemon: router will be floodfill");
i2p::context.SetFloodfill (true);
}
else
i2p::context.SetFloodfill (false);
if (bandwidth != '-')
{
LogPrint(eLogInfo, "Daemon: bandwidth set to ", bandwidth);
if (bandwidth > 'O')
i2p::context.SetExtraBandwidth ();
else if (bandwidth > 'L')
i2p::context.SetHighBandwidth ();
else
i2p::context.SetLowBandwidth ();
}
else if (isFloodfill)
{
LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'");
i2p::context.SetExtraBandwidth ();
}
else
{
LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'");
i2p::context.SetLowBandwidth ();
}
std::string family; i2p::config::GetOption("family", family);
i2p::context.SetFamily (family);
if (family.length () > 0)
LogPrint(eLogInfo, "Daemon: family set to ", family);
LogPrint("CMD parameters:");
for (int i = 0; i < argc; ++i)
LogPrint(i, " ", argv[i]);
return true;
}
bool Daemon_Singleton::start()
{
// initialize log
if (isLogging)
{
if (isDaemon)
{
std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string();
#ifndef _WIN32
logfile_path.append("/i2pd.log");
#else
logfile_path.append("\\i2pd.log");
#endif
StartLog (logfile_path);
}
else
StartLog (""); // write to stdout
std::string logs = ""; i2p::config::GetOption("log", logs);
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
if (isDaemon && (logs == "" || logs == "stdout"))
logs = "file";
if (logs == "file") {
if (logfile == "")
logfile = i2p::fs::DataDirPath("i2pd.log");
StartLog (logfile);
} else {
// use stdout
StartLog ("");
}
SetLogLevel(loglevel);
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);
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
d.httpServer = std::unique_ptr<i2p::util::HTTPServer>(new i2p::util::HTTPServer(httpAddr, httpPort));
d.httpServer->Start();
}
d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070));
d.httpServer->Start();
LogPrint("HTTP Server started");
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
LogPrint("NetDB started");
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: starting UPnP");
d.m_UPnP.Start ();
#endif
LogPrint(eLogInfo, "Daemon: starting Transports");
i2p::transport::transports.Start();
LogPrint("Transports started");
LogPrint(eLogInfo, "Daemon: starting Tunnels");
i2p::tunnel::tunnels.Start();
LogPrint("Tunnels started");
LogPrint(eLogInfo, "Daemon: starting Client");
i2p::client::context.Start ();
LogPrint("Client started");
// I2P Control Protocol
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
if (i2pcontrol) {
std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr);
uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort);
LogPrint(eLogInfo, "Daemon: starting I2PControl at ", i2pcpAddr, ":", i2pcpPort);
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
d.m_I2PControlService->Start ();
}
return true;
}
bool Daemon_Singleton::stop()
{
LogPrint("Shutdown started.");
LogPrint(eLogInfo, "Daemon: shutting down");
LogPrint(eLogInfo, "Daemon: stopping Client");
i2p::client::context.Stop();
LogPrint("Client stoped");
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
i2p::tunnel::tunnels.Stop();
LogPrint("Tunnels stoped");
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: stopping UPnP");
d.m_UPnP.Stop ();
#endif
LogPrint(eLogInfo, "Daemon: stopping Transports");
i2p::transport::transports.Stop();
LogPrint("Transports stoped");
LogPrint(eLogInfo, "Daemon: stopping NetDB");
i2p::data::netdb.Stop();
LogPrint("NetDB stoped");
d.httpServer->Stop();
LogPrint("HTTP Server stoped");
if (d.httpServer) {
LogPrint(eLogInfo, "Daemon: stopping HTTP Server");
d.httpServer->Stop();
d.httpServer = nullptr;
}
if (d.m_I2PControlService)
{
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
d.m_I2PControlService->Stop ();
d.m_I2PControlService = nullptr;
}
i2p::crypto::TerminateCrypto ();
StopLog ();
delete d.httpServer; d.httpServer = nullptr;
return true;
}
}

View File

@@ -1,4 +1,6 @@
#pragma once
#ifndef DAEMON_H__
#define DAEMON_H__
#include <string>
#ifdef _WIN32
@@ -18,17 +20,18 @@ namespace i2p
virtual bool init(int argc, char* argv[]);
virtual bool start();
virtual bool stop();
virtual void run () {};
int isLogging;
int isDaemon;
int running;
bool isLogging;
bool isDaemon;
bool running;
protected:
Daemon_Singleton();
virtual ~Daemon_Singleton();
bool IsService () const;
bool IsService () const;
// d-pointer for httpServer, httpProxy, etc.
class Daemon_Singleton_Private;
@@ -45,9 +48,10 @@ namespace i2p
return instance;
}
virtual bool init(int argc, char* argv[]);
virtual bool start();
virtual bool stop();
bool init(int argc, char* argv[]);
bool start();
bool stop();
void run ();
};
#else
class DaemonLinux : public Daemon_Singleton
@@ -59,13 +63,17 @@ namespace i2p
return instance;
}
virtual bool start();
virtual bool stop();
bool start();
bool stop();
; void run ();
private:
std::string pidfile;
int pidFilehandle;
int pidFH;
};
#endif
}
}
#endif // DAEMON_H__

View File

@@ -4,31 +4,23 @@
#include <signal.h>
#include <stdlib.h>
#include <thread>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "Config.h"
#include "FS.h"
#include "Log.h"
#include "util.h"
void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
if (i2p::util::config::GetArg("daemon", 0) == 1)
{
static bool first=true;
if (first)
{
first=false;
return;
}
}
LogPrint("Reloading config.");
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
break;
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
ReopenLogFile ();
break;
case SIGABRT:
case SIGTERM:
case SIGINT:
@@ -37,7 +29,6 @@ void handle_signal(int sig)
}
}
namespace i2p
{
namespace util
@@ -52,17 +43,25 @@ namespace i2p
::exit (EXIT_SUCCESS);
if (pid < 0) // error
{
LogPrint(eLogError, "Daemon: could not fork: ", strerror(errno));
return false;
}
// child
umask(0);
umask(S_IWGRP | S_IRWXO); // 0027
int sid = setsid();
if (sid < 0)
{
LogPrint("Error, could not create process group.");
LogPrint(eLogError, "Daemon: could not create process group.");
return false;
}
std::string d = i2p::fs::GetDataDir();
if (chdir(d.c_str()) != 0)
{
LogPrint(eLogError, "Daemon: could not chdir: ", strerror(errno));
return false;
}
chdir(i2p::util::filesystem::GetDataDir().string().c_str());
// close stdin/stdout/stderr descriptors
::close (0);
@@ -74,22 +73,32 @@ namespace i2p
}
// Pidfile
pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string();
pidfile.append("/i2pd.pid");
pidFilehandle = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
if (pidFilehandle == -1)
{
LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?");
return false;
// this code is c-styled and a bit ugly, but we need fd for locking pidfile
std::string pidfile; i2p::config::GetOption("pidfile", pidfile);
if (pidfile == "") {
pidfile = i2p::fs::DataDirPath("i2pd.pid");
}
if (lockf(pidFilehandle, F_TLOCK, 0) == -1)
{
LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?");
return false;
if (pidfile != "") {
pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
if (pidFH < 0)
{
LogPrint(eLogError, "Daemon: could not create pid file ", pidfile, ": ", strerror(errno));
return false;
}
if (lockf(pidFH, F_TLOCK, 0) != 0)
{
LogPrint(eLogError, "Daemon: could not lock pid file ", pidfile, ": ", strerror(errno));
return false;
}
char pid[10];
sprintf(pid, "%d\n", getpid());
ftruncate(pidFH, 0);
if (write(pidFH, pid, strlen(pid)) < 0)
{
LogPrint(eLogError, "Daemon: could not write pidfile: ", strerror(errno));
return false;
}
}
char pid[10];
sprintf(pid, "%d\n", getpid());
write(pidFilehandle, pid, strlen(pid));
// Signal handler
struct sigaction sa;
@@ -106,12 +115,18 @@ namespace i2p
bool DaemonLinux::stop()
{
close(pidFilehandle);
unlink(pidfile.c_str());
i2p::fs::Remove(pidfile);
return Daemon_Singleton::stop();
}
void DaemonLinux::run ()
{
while (running)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
}
}
}
}

View File

@@ -1,10 +1,11 @@
#include "Config.h"
#include "Daemon.h"
#include "util.h"
#include "Log.h"
#ifdef _WIN32
#include "./Win32/Win32Service.h"
#include "Win32/Win32App.h"
namespace i2p
{
@@ -15,69 +16,41 @@ namespace i2p
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
setlocale(LC_ALL, "Russian");
return Daemon_Singleton::init(argc, argv);
}
if (!Daemon_Singleton::init(argc, argv)) return false;
if (I2PService::isService())
isDaemon = 1;
else
isDaemon = 0;
std::string serviceControl = i2p::util::config::GetArg("-service", "none");
if (serviceControl == "install")
{
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
);
exit(0);
}
else if (serviceControl == "remove")
{
UninstallService(SERVICE_NAME);
exit(0);
}
else if (serviceControl != "none")
{
printf(" --service=install to install the service.\n");
printf(" --service=remove to remove the service.\n");
}
if (isDaemon == 1)
{
LogPrint("Service session");
I2PService service(SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError());
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else
LogPrint("User session");
return true;
}
bool DaemonWin32::start()
{
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
return Daemon_Singleton::start();
if (!i2p::win32::StartWin32App ()) return false;
// override log
i2p::config::SetOption("log", std::string ("file"));
bool ret = Daemon_Singleton::start();
if (ret && IsLogToFile ())
{
// TODO: find out where this garbage to console comes from
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
}
return ret;
}
bool DaemonWin32::stop()
{
{
i2p::win32::StopWin32App ();
return Daemon_Singleton::stop();
}
}
void DaemonWin32::run ()
{
i2p::win32::RunWin32App ();
}
}
}
#endif
#endif

View File

@@ -1,7 +1,7 @@
#include <string.h>
#include <vector>
#include <cryptopp/sha.h>
#include <cryptopp/gzip.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Log.h"
#include "TunnelBase.h"
#include "RouterContext.h"
@@ -12,62 +12,75 @@ namespace i2p
{
namespace datagram
{
DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner):
m_Owner (owner)
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner), m_Receiver (nullptr)
{
}
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote)
DatagramDestination::~DatagramDestination ()
{
}
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);
auto identityLen = m_Owner->GetIdentity ()->ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen;
auto signatureLen = m_Owner.GetIdentity ().GetSignatureLen ();
auto signatureLen = m_Owner->GetIdentity ()->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_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, buf1, len);
m_Owner.Sign (hash, 32, signature);
SHA256(buf1, len, hash);
m_Owner->Sign (hash, 32, signature);
}
else
m_Owner.Sign (buf1, len, signature);
auto service = m_Owner.GetService ();
if (service)
service->post (boost::bind (&DatagramDestination::SendMsg, this,
CreateDataMessage (buf, len + headerLen), remote));
m_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
LogPrint ("Failed to send datagram. Destination is not running");
m_Owner->RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg));
}
void DatagramDestination::SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote)
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, std::shared_ptr<I2NPMessage> msg)
{
auto leases = remote.GetNonExpiredLeases ();
if (!leases.empty ())
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 = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
auto garlic = m_Owner.WrapMessage (remote, msg, true);
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,
leases[i]->tunnelGateway, leases[i]->tunnelID,
garlic
});
m_Owner.SendTunnelDataMsgs (msgs);
outboundTunnel->SendTunnelDataMsg (msgs);
}
else
{
LogPrint ("Failed to send datagram. All leases expired");
DeleteI2NPMessage (msg);
if (outboundTunnel)
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
else
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
}
}
void DatagramDestination::HandleDatagram (const uint8_t * buf, size_t len)
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
i2p::data::IdentityEx identity;
size_t identityLen = identity.FromBuffer (buf, len);
@@ -77,54 +90,53 @@ namespace datagram
bool verified = false;
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, buf + headerLen, len - headerLen);
uint8_t hash[32];
SHA256(buf + headerLen, len - headerLen, hash);
verified = identity.Verify (hash, 32, signature);
}
}
else
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
if (verified)
{
// TODO: invoke datagram handler
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);
else
LogPrint (eLogWarning, "Receiver for datagram is not set");
}
else
LogPrint ("Datagram signature verification failed");
LogPrint (eLogWarning, "Datagram signature verification failed");
}
void DatagramDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
// unzip it
CryptoPP::Gunzip decompressor;
decompressor.Put (buf, len);
decompressor.MessageEnd();
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
auto uncompressedLen = decompressor.MaxRetrievable ();
if (uncompressedLen <= MAX_DATAGRAM_SIZE)
{
decompressor.Get (uncompressed, uncompressedLen);
HandleDatagram (uncompressed, uncompressedLen);
}
else
LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size");
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen)
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len)
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{
I2NPMessage * msg = NewI2NPMessage ();
CryptoPP::Gzip compressor; // default level
compressor.Put (payload, len);
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
auto msg = NewI2NPMessage ();
uint8_t * buf = msg->GetPayload ();
*(uint32_t *)buf = htobe32 (size); // length
buf += 4;
compressor.Get (buf, size);
memset (buf + 4, 0, 4); // source and destination are zeroes
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
msg->len += size + 4;
FillI2NPMessageHeader (msg, eI2NPData);
buf += 4; // reserve for length
size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
if (size)
{
htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, fromPort); // source port
htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData);
}
else
msg = nullptr;
return msg;
}
}

View File

@@ -2,6 +2,11 @@
#define DATAGRAM_H__
#include <inttypes.h>
#include <memory>
#include <functional>
#include <map>
#include "Base.h"
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
@@ -16,23 +21,38 @@ namespace datagram
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 (i2p::client::ClientDestination& owner);
~DatagramDestination () {};
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote);
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void 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); };
private:
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
void SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote);
void HandleDatagram (const uint8_t * buf, size_t len);
void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<I2NPMessage> msg);
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);
private:
i2p::client::ClientDestination& m_Owner;
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default
std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
};
}
}

View File

@@ -1,8 +1,12 @@
#include <fstream>
#include <algorithm>
#include <cryptopp/dh.h>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <openssl/rand.h>
#include "Log.h"
#include "util.h"
#include "FS.h"
#include "Crypto.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "Destination.h"
@@ -10,137 +14,194 @@ namespace i2p
{
namespace client
{
ClientDestination::ClientDestination (bool isPublic, i2p::data::SigningKeyType sigType):
m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr),
m_CurrentOutboundTunnel (nullptr), m_LeaseSet (nullptr), m_IsPublic (isPublic),
m_DatagramDestination (nullptr)
{
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel
if (m_IsPublic)
LogPrint ("Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p created");
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
}
ClientDestination::ClientDestination (const std::string& fullPath, bool isPublic):
m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr),
m_CurrentOutboundTunnel (nullptr), m_LeaseSet (nullptr), m_IsPublic (isPublic),
m_DatagramDestination (nullptr)
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
{
std::ifstream s(fullPath.c_str (), std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0, std::ios::end);
size_t len = s.tellg();
s.seekg (0, std::ios::beg);
uint8_t * buf = new uint8_t[len];
s.read ((char *)buf, len);
m_Keys.FromBuffer (buf, len);
delete[] buf;
LogPrint ("Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p loaded");
}
if (m_IsPublic)
PersistTemporaryKeys ();
else
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
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 numTags = DEFAULT_TAGS_TO_SEND;
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
if (params)
{
LogPrint ("Can't open file ", fullPath, " Creating new one");
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
size_t len = m_Keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
len = m_Keys.ToBuffer (buf, len);
f.write ((char *)buf, len);
delete[] buf;
LogPrint ("New private keys file ", fullPath, " for ", m_Keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p created");
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
if (len > 0)
{
inboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", len);
}
}
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
if (len > 0)
{
outboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", len);
}
}
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = boost::lexical_cast<int>(it->second);
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 = boost::lexical_cast<int>(it->second);
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 = boost::lexical_cast<int>(it->second);
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);
}
}
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
}
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic):
m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr),
m_Keys (keys), m_CurrentOutboundTunnel (nullptr), m_LeaseSet (nullptr), m_IsPublic (isPublic),
m_DatagramDestination (nullptr)
{
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel
SetNumTags (numTags);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers);
if (m_IsPublic)
LogPrint ("Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p created");
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
}
ClientDestination::~ClientDestination ()
{
Stop ();
for (auto it: m_RemoteLeaseSets)
delete it.second;
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);
delete m_LeaseSet;
delete m_Work;
delete m_Service;
delete m_StreamingDestination;
delete m_DatagramDestination;
if (m_DatagramDestination)
delete m_DatagramDestination;
}
void ClientDestination::Run ()
{
if (m_Service)
m_Service->run ();
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Destination: runtime exception: ", ex.what ());
}
}
}
void ClientDestination::Start ()
{
m_Service = new boost::asio::io_service;
m_Work = new boost::asio::io_service::work (*m_Service);
m_Pool->SetActive (true);
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
m_StreamingDestination->Start ();
if (!m_IsRunning)
{
m_IsRunning = true;
m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&ClientDestination::Run, shared_from_this ()));
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this ()); // TODO:
m_StreamingDestination->Start ();
for (auto it: m_StreamingDestinationsByPorts)
it.second->Start ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
}
}
void ClientDestination::Stop ()
{
m_StreamingDestination->Stop ();
if (m_Pool)
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
m_IsRunning = false;
if (m_Service)
m_Service->stop ();
if (m_Thread)
if (m_IsRunning)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
m_CleanupTimer.cancel ();
m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
m_IsRunning = false;
m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto it: m_StreamingDestinationsByPorts)
it.second->Stop ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
if (m_Pool)
{
m_Pool->SetLocalDestination (nullptr);
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
}
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
}
delete m_Work; m_Work = nullptr;
delete m_Service; m_Service = nullptr;
}
const i2p::data::LeaseSet * ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
{
if (it->second->HasNonExpiredLeases ())
if (!it->second->IsExpired ())
return it->second;
else
{
LogPrint ("All leases of remote LeaseSet expired. Request it");
i2p::data::netdb.RequestDestination (ident, true, m_Pool);
}
LogPrint (eLogWarning, "Destination: remote LeaseSet expired");
}
else
{
auto ls = i2p::data::netdb.FindLeaseSet (ident);
if (ls)
if (ls && !ls->IsExpired ())
{
ls = new i2p::data::LeaseSet (*ls);
ls->PopulateLeases (); // since we don't store them in netdb
m_RemoteLeaseSets[ident] = ls;
return ls;
}
@@ -148,7 +209,7 @@ namespace client
return nullptr;
}
const i2p::data::LeaseSet * ClientDestination::GetLeaseSet ()
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::GetLeaseSet ()
{
if (!m_Pool) return nullptr;
if (!m_LeaseSet)
@@ -158,51 +219,52 @@ namespace client
void ClientDestination::UpdateLeaseSet ()
{
auto newLeaseSet = new i2p::data::LeaseSet (*m_Pool);
if (!m_LeaseSet)
m_LeaseSet = newLeaseSet;
else
{
// TODO: implement it better
*m_LeaseSet = *newLeaseSet;
delete newLeaseSet;
}
m_LeaseSet.reset (new i2p::data::LeaseSet (m_Pool));
}
void ClientDestination::SendTunnelDataMsgs (const std::vector<i2p::tunnel::TunnelMessageBlock>& msgs)
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{
m_CurrentOutboundTunnel = m_Pool->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
if (m_CurrentOutboundTunnel)
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
else
struct
{
LogPrint ("No outbound tunnels in the pool");
for (auto it: msgs)
DeleteI2NPMessage (it.data);
}
uint8_t k[32], t[32];
} data;
memcpy (data.k, key, 32);
memcpy (data.t, tag, 32);
auto s = shared_from_this ();
m_Service.post ([s,data](void)
{
s->AddSessionKey (data.k, data.t);
});
return true;
}
void ClientDestination::ProcessGarlicMessage (I2NPMessage * msg)
void ClientDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service->post (boost::bind (&ClientDestination::HandleGarlicMessage, this, msg));
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, shared_from_this (), msg));
}
void ClientDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg)
void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service->post (boost::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg));
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
}
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
I2NPHeader * header = (I2NPHeader *)buf;
switch (header->typeID)
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID)
{
case eI2NPData:
HandleDataMessage (buf + sizeof (I2NPHeader), be16toh (header->size));
HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
case eI2NPDeliveryStatus:
// we assume tunnel tests non-encrypted
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
break;
case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (buf + sizeof (I2NPHeader), be16toh (header->size));
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); // TODO: remove
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
default:
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
@@ -211,27 +273,126 @@ namespace client
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
{
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
size_t offset = sizeof (I2NPDatabaseStoreMsg);
if (msg->replyToken) // TODO:
offset += 36;
if (msg->type == 1) // LeaseSet
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
LogPrint ("Remote LeaseSet");
auto it = m_RemoteLeaseSets.find (msg->key);
LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
offset += 36;
}
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);
if (it != m_RemoteLeaseSets.end ())
{
it->second->Update (buf + offset, len - offset);
LogPrint ("Remote LeaseSet updated");
leaseSet = it->second;
if (leaseSet->IsNewer (buf + offset, len - offset))
{
leaseSet->Update (buf + offset, len - offset);
if (leaseSet->IsValid ())
LogPrint (eLogDebug, "Remote LeaseSet updated");
else
{
LogPrint (eLogDebug, "Remote LeaseSet update failed");
m_RemoteLeaseSets.erase (it);
leaseSet = nullptr;
}
}
else
LogPrint (eLogDebug, "Remote LeaseSet is older. Not updated");
}
else
{
LogPrint ("New remote LeaseSet added");
m_RemoteLeaseSets[msg->key] = new i2p::data::LeaseSet (buf + offset, len - offset);
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
if (leaseSet->IsValid ())
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
{
LogPrint (eLogDebug, "New remote LeaseSet added");
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet;
}
else
LogPrint (eLogDebug, "Own remote LeaseSet dropped");
}
else
{
LogPrint (eLogError, "New remote LeaseSet failed");
leaseSet = nullptr;
}
}
}
else
LogPrint ("Unexpected client's DatabaseStore type ", msg->type, ". Dropped");
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET);
if (it1 != m_LeaseSetRequests.end ())
{
it1->second->requestTimeoutTimer.cancel ();
if (it1->second->requestComplete) it1->second->requestComplete (leaseSet);
m_LeaseSetRequests.erase (it1);
}
}
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
{
i2p::data::IdentHash key (buf);
int num = buf[32]; // num
LogPrint (eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64 (), " num=", num);
auto it = m_LeaseSetRequests.find (key);
if (it != m_LeaseSetRequests.end ())
{
auto request = it->second;
bool found = false;
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);
m_LeaseSetRequests.erase (key);
}
}
else
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
}
void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed");
m_ExcludedFloodfills.clear ();
m_PublishReplyToken = 0;
// schedule verification
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1));
}
else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
}
void ClientDestination::SetLeaseSetUpdated ()
@@ -239,43 +400,164 @@ namespace client
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
UpdateLeaseSet ();
if (m_IsPublic)
i2p::data::netdb.PublishLeaseSet (m_LeaseSet, m_Pool);
{
m_PublishVerificationTimer.cancel ();
Publish ();
}
}
void ClientDestination::Publish ()
{
if (!m_LeaseSet || !m_Pool)
{
LogPrint (eLogError, "Destination: Can't publish non-existing LeaseSet");
return;
}
if (m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending");
return;
}
auto outbound = m_Pool->GetNextOutboundTunnel ();
if (!outbound)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
return;
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
if (!floodfill)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
m_ExcludedFloodfills.clear ();
return;
}
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1));
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
}
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_PublishReplyToken)
{
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again");
m_PublishReplyToken = 0;
Publish ();
}
}
}
void ClientDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto s = shared_from_this ();
RequestLeaseSet (GetIdentHash (),
// "this" added due to bug in gcc 4.7-4.8
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet)
{
if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet)
{
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified");
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return;
}
}
else
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet");
// we have to publish again
s->Publish ();
});
}
}
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = be32toh (*(uint32_t *)buf);
uint32_t length = bufbe32toh (buf);
buf += 4;
// we assume I2CP payload
uint16_t fromPort = bufbe16toh (buf + 4), // source
toPort = bufbe16toh (buf + 6); // destination
switch (buf[9])
{
case PROTOCOL_TYPE_STREAMING:
{
// streaming protocol
if (m_StreamingDestination)
m_StreamingDestination->HandleDataMessagePayload (buf, length);
auto dest = GetStreamingDestination (toPort);
if (dest)
dest->HandleDataMessagePayload (buf, length);
else
LogPrint ("Missing streaming destination");
LogPrint (eLogError, "Destination: Missing streaming destination");
}
break;
case PROTOCOL_TYPE_DATAGRAM:
// datagram protocol
if (m_DatagramDestination)
m_DatagramDestination->HandleDataMessagePayload (buf, length);
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
else
LogPrint ("Missing streaming destination");
LogPrint (eLogError, "Destination: Missing datagram destination");
break;
default:
LogPrint ("Data: unexpected protocol ", buf[9]);
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
}
}
i2p::stream::Stream * ClientDestination::CreateStream (const i2p::data::LeaseSet& remote, int port)
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
{
if (!streamRequestComplete)
{
LogPrint (eLogError, "Destination: request callback is not specified in CreateStream");
return;
}
auto leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (leaseSet, port));
else
{
auto s = shared_from_this ();
RequestDestination (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
streamRequestComplete(s->CreateStream (ls, port));
else
streamRequestComplete (nullptr);
});
}
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
if (m_StreamingDestination)
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
return nullptr;
}
else
return nullptr;
}
void ClientDestination::AcceptStreams (const std::function<void (i2p::stream::Stream *)>& acceptor)
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const
{
if (port)
{
auto it = m_StreamingDestinationsByPorts.find (port);
if (it != m_StreamingDestinationsByPorts.end ())
return it->second;
}
// if port is zero or not found, use default destination
return m_StreamingDestination;
}
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
{
if (m_StreamingDestination)
m_StreamingDestination->SetAcceptor (acceptor);
@@ -286,7 +568,7 @@ namespace client
if (m_StreamingDestination)
m_StreamingDestination->ResetAcceptor ();
}
bool ClientDestination::IsAcceptingStreams () const
{
if (m_StreamingDestination)
@@ -294,10 +576,209 @@ namespace client
return false;
}
void ClientDestination::CreateDatagramDestination ()
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
{
auto dest = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this (), port, gzip);
if (port)
m_StreamingDestinationsByPorts[port] = dest;
else // update default
m_StreamingDestination = dest;
return dest;
}
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{
if (!m_DatagramDestination)
m_DatagramDestination = new i2p::datagram::DatagramDestination (*this);
m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
return m_DatagramDestination;
}
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
if (!m_Pool || !IsReady ())
{
if (requestComplete) requestComplete (nullptr);
return false;
}
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true;
}
void ClientDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
{
auto s = shared_from_this ();
m_Service.post ([dest, s](void)
{
auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ())
{
auto requestComplete = it->second->requestComplete;
s->m_LeaseSetRequests.erase (it);
if (requestComplete) requestComplete (nullptr);
}
});
}
void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill)
{
auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestComplete = requestComplete;
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
if (ret.second) // inserted
{
if (!SendLeaseSetRequest (dest, floodfill, request))
{
// request failed
m_LeaseSetRequests.erase (dest);
if (request->requestComplete) request->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);
}
}
else
{
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
if (requestComplete) requestComplete (nullptr);
}
}
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
{
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
if (request->replyTunnel && request->outboundTunnel)
{
request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
request->requestTimeoutTimer.cancel ();
uint8_t replyKey[32], replyTag[32];
RAND_bytes (replyKey, 32); // random session key
RAND_bytes (replyTag, 32); // random session tag
AddSessionKey (replyKey, replyTag);
auto msg = WrapMessage (nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
request->replyTunnel, replyKey, replyTag));
request->outboundTunnel->SendTunnelDataMsg (
{
i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0, msg
}
});
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer,
shared_from_this (), std::placeholders::_1, dest));
}
else
return false;
return true;
}
void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto it = m_LeaseSetRequests.find (dest);
if (it != m_LeaseSetRequests.end ())
{
bool done = false;
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
{
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
if (floodfill)
{
// reset tunnels, because one them might fail
it->second->outboundTunnel = nullptr;
it->second->replyTunnel = nullptr;
done = !SendLeaseSetRequest (dest, floodfill, it->second);
}
else
done = true;
}
else
{
LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
done = true;
}
if (done)
{
auto requestComplete = it->second->requestComplete;
m_LeaseSetRequests.erase (it);
if (requestComplete) requestComplete (nullptr);
}
}
}
}
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
CleanupExpiredTags ();
CleanupRemoteLeaseSets ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
}
}
void ClientDestination::CleanupRemoteLeaseSets ()
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
{
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
{
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_RemoteLeaseSets.erase (it);
}
else
it++;
}
}
void ClientDestination::PersistTemporaryKeys ()
{
std::string ident = GetIdentHash().ToBase32();
std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat"));
std::ifstream f(path, std::ifstream::binary);
if (f) {
f.read ((char *)m_EncryptionPublicKey, 256);
f.read ((char *)m_EncryptionPrivateKey, 256);
return;
}
LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p");
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
if (f1) {
f1.write ((char *)m_EncryptionPublicKey, 256);
f1.write ((char *)m_EncryptionPrivateKey, 256);
return;
}
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
}
}
}

View File

@@ -3,11 +3,18 @@
#include <thread>
#include <mutex>
#include <memory>
#include <map>
#include <set>
#include <string>
#include <functional>
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
#include "CryptoConst.h"
#include "Crypto.h"
#include "LeaseSet.h"
#include "Garlic.h"
#include "NetDb.h"
#include "Streaming.h"
#include "Datagram.h"
@@ -18,37 +25,75 @@ namespace client
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
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_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
// I2CP
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
const int DEFAULT_TAGS_TO_SEND = 40;
class ClientDestination: public i2p::garlic::GarlicDestination
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class ClientDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<ClientDestination>
{
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found
struct LeaseSetRequest
{
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
RequestComplete requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
};
public:
ClientDestination (bool isPublic, i2p::data::SigningKeyType sigType);
ClientDestination (const std::string& fullPath, bool isPublic);
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic);
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
virtual void Start ();
virtual void Stop ();
bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service * GetService () { return m_Service; };
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); };
void ResetCurrentOutboundTunnel () { m_CurrentOutboundTunnel = nullptr; };
const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident);
void SendTunnelDataMsgs (const std::vector<i2p::tunnel::TunnelMessageBlock>& msgs);
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<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);
// streaming
i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; };
i2p::stream::Stream * CreateStream (const i2p::data::LeaseSet& remote, int port = 0);
void AcceptStreams (const std::function<void (i2p::stream::Stream *)>& acceptor);
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
// datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
void CreateDatagramDestination ();
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
// implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
@@ -56,12 +101,14 @@ namespace client
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
// implements GarlicDestination
const i2p::data::LeaseSet * GetLeaseSet ();
void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet ();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
void ProcessGarlicMessage (I2NPMessage * msg);
void ProcessDeliveryStatusMessage (I2NPMessage * msg);
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated ();
// I2CP
@@ -71,26 +118,43 @@ namespace client
void Run ();
void UpdateLeaseSet ();
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets ();
void PersistTemporaryKeys ();
private:
bool m_IsRunning;
volatile bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service * m_Service;
boost::asio::io_service::work * m_Work;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
i2p::data::PrivateKeys m_Keys;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
std::map<i2p::data::IdentHash, i2p::data::LeaseSet *> m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
i2p::tunnel::TunnelPool * m_Pool;
i2p::tunnel::OutboundTunnel * m_CurrentOutboundTunnel;
i2p::data::LeaseSet * m_LeaseSet;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::shared_ptr<i2p::data::LeaseSet> m_LeaseSet;
bool m_IsPublic;
i2p::stream::StreamingDestination * m_StreamingDestination;
uint32_t m_PublishReplyToken;
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
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;
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
public:
// for HTTP only

View File

@@ -1,77 +0,0 @@
#ifndef EL_GAMAL_H__
#define EL_GAMAL_H__
#include <inttypes.h>
#include <cryptopp/integer.h>
#include <cryptopp/osrng.h>
#include <cryptopp/sha.h>
#include "CryptoConst.h"
#include "Log.h"
namespace i2p
{
namespace crypto
{
class ElGamalEncryption
{
public:
ElGamalEncryption (const uint8_t * key):
y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1),
a (a_exp_b_mod_c (elgg, k, elgp)), b1 (a_exp_b_mod_c (y, k, elgp))
{
}
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false)
{
// calculate b = b1*m mod p
uint8_t m[255];
m[0] = 0xFF;
memcpy (m+33, data, len);
CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222);
CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp));
// copy a and b
if (zeroPadding)
{
encrypted[0] = 0;
a.Encode (encrypted + 1, 256);
encrypted[257] = 0;
b.Encode (encrypted + 258, 256);
}
else
{
a.Encode (encrypted, 256);
b.Encode (encrypted + 256, 256);
}
}
private:
CryptoPP::AutoSeededRandomPool rnd;
CryptoPP::Integer y, k, a, b1;
bool m_ZeroPadding;
};
inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
uint8_t * data, bool zeroPadding = false)
{
CryptoPP::Integer x(key, 256), a(zeroPadding? encrypted +1 : encrypted, 256),
b(zeroPadding? encrypted + 258 :encrypted + 256, 256);
uint8_t m[255], hash[32];
a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255);
CryptoPP::SHA256().CalculateDigest(hash, m+33, 222);
for (int i = 0; i < 32; i++)
if (hash[i] != m[i+1])
{
LogPrint ("ElGamal decrypt hash doesn't match");
return false;
}
memcpy (data, m + 33, 222);
return true;
}
}
}
#endif

158
FS.cpp Normal file
View File

@@ -0,0 +1,158 @@
/*
* 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 <algorithm>
#include <boost/filesystem.hpp>
#ifdef WIN32
#include <shlobj.h>
#endif
#include "Base.h"
#include "FS.h"
#include "Log.h"
namespace i2p {
namespace fs {
std::string appName = "i2pd";
std::string dataDir = "";
#ifdef _WIN32
std::string dirSep = "\\";
#else
std::string dirSep = "/";
#endif
const std::string & GetAppName () {
return appName;
}
void SetAppName (const std::string& name) {
appName = name;
}
const std::string & GetDataDir () {
return dataDir;
}
void DetectDataDir(const std::string & cmdline_param, bool isService) {
if (cmdline_param != "") {
dataDir = cmdline_param;
return;
}
#if defined(WIN32) || defined(_WIN32)
char localAppData[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
dataDir = std::string(localAppData) + "\\" + appName;
return;
#elif defined(MAC_OSX)
char *home = getenv("HOME");
dataDir = (home != NULL && strlen(home) > 0) ? home : "";
dataDir += "/Library/Application Support/" + appName;
return;
#else /* other unix */
char *home = getenv("HOME");
if (isService) {
dataDir = "/var/lib/" + appName;
} else if (home != NULL && strlen(home) > 0) {
dataDir = std::string(home) + "/." + appName;
} else {
dataDir = "/tmp/" + appName;
}
return;
#endif
}
bool Init() {
if (!boost::filesystem::exists(dataDir))
boost::filesystem::create_directory(dataDir);
std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations))
boost::filesystem::create_directory(destinations);
return true;
}
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path))
return false;
boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
files.push_back(it->path().string());
}
return true;
}
bool Exists(const std::string & path) {
return boost::filesystem::exists(path);
}
bool Remove(const std::string & path) {
if (!boost::filesystem::exists(path))
return false;
return boost::filesystem::remove(path);
}
void HashedStorage::SetPlace(const std::string &path) {
root = path + i2p::fs::dirSep + name;
}
bool HashedStorage::Init(const char * chars, size_t count) {
if (!boost::filesystem::exists(root)) {
boost::filesystem::create_directory(root);
}
for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p))
continue;
if (boost::filesystem::create_directory(p))
continue; /* ^ throws exception on failure */
return false;
}
return true;
}
std::string HashedStorage::Path(const std::string & ident) const {
std::string safe_ident = ident;
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
std::stringstream t("");
t << this->root << i2p::fs::dirSep;
t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
t << prefix2 << safe_ident << "." << suffix;
return t.str();
}
void HashedStorage::Remove(const std::string & ident) {
std::string path = Path(ident);
if (!boost::filesystem::exists(path))
return;
boost::filesystem::remove(path);
}
void HashedStorage::Traverse(std::vector<std::string> & files) {
boost::filesystem::path p(root);
boost::filesystem::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
files.push_back(t);
}
}
} // fs
} // i2p

142
FS.h Normal file
View File

@@ -0,0 +1,142 @@
/*
* 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
*/
#ifndef FS_H__
#define FS_H__
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
namespace i2p {
namespace fs {
extern std::string dirSep;
/**
* @brief Class to work with NetDb & Router profiles
*
* Usage:
*
* const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
* auto h = HashedStorage("name", "y", "z-", ".txt");
* h.SetPlace("/tmp/hs-test");
* h.GetName() -> gives "name"
* h.GetRoot() -> gives "/tmp/hs-test/name"
* h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet
* h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt
* h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists
* std::vector<std::string> files;
* h.Traverse(files); <- finds all files in storage and saves in given vector
*/
class HashedStorage {
protected:
std::string root; /**< path to storage with it's name included */
std::string name; /**< name of the storage */
std::string prefix1; /**< hashed directory prefix */
std::string prefix2; /**< prefix of file in storage */
std::string suffix; /**< suffix of file in storage (extension) */
public:
HashedStorage(const char *n, const char *p1, const char *p2, const char *s):
name(n), prefix1(p1), prefix2(p2), suffix(s) {};
/** create subdirs in storage */
bool Init(const char* chars, size_t cnt);
const std::string & GetRoot() const { return this->root; }
const std::string & GetName() const { return this->name; }
/** set directory where to place storage directory */
void SetPlace(const std::string & path);
/** path to file with given ident */
std::string Path(const std::string & ident) const;
/** remove file by ident */
void Remove(const std::string & ident);
/** find all files in storage and store list in provided vector */
void Traverse(std::vector<std::string> & files);
};
/** @brief Returns current application name, default 'i2pd' */
const std::string & GetAppName ();
/** @brief Set applicaton name, affects autodetection of datadir */
void SetAppName (const std::string& name);
/** @brief Returns datadir path */
const std::string & GetDataDir();
/**
* @brief Set datadir either from cmdline option or using autodetection
* @param cmdline_param Value of cmdline parameter --datadir=<something>
* @param isService Value of cmdline parameter --service
*
* Examples of autodetected paths:
*
* Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\
* Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
* Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/
*/
void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
/**
* @brief Create subdirectories inside datadir
*/
bool Init();
/**
* @brief Get list of files in directory
* @param path Path to directory
* @param files Vector to store found files
* @return true on success and false if directory not exists
*/
bool ReadDir(const std::string & path, std::vector<std::string> & files);
/**
* @brief Remove file with given path
* @param path Absolute path to file
* @return true on success, false if file not exists, throws exception on error
*/
bool Remove(const std::string & path);
/**
* @brief Check existence of file
* @param path Absolute path to file
* @return true if file exists, false otherwise
*/
bool Exists(const std::string & path);
template<typename T>
void _ExpandPath(std::stringstream & path, T c) {
path << i2p::fs::dirSep << c;
}
template<typename T, typename ... Other>
void _ExpandPath(std::stringstream & path, T c, Other ... other) {
_ExpandPath(path, c);
_ExpandPath(path, other ...);
}
/**
* @brief Get path relative to datadir
*
* Examples (with datadir = "/tmp/i2pd"):
*
* i2p::fs::Path("test") -> '/tmp/i2pd/test'
* i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt'
*/
template<typename ... Other>
std::string DataDirPath(Other ... components) {
std::stringstream s("");
s << i2p::fs::GetDataDir();
_ExpandPath(s, components ...);
return s.str();
}
} // fs
} // i2p
#endif // /* FS_H__ */

175
Family.cpp Normal file
View File

@@ -0,0 +1,175 @@
#include <string.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include "FS.h"
#include "Log.h"
#include "Crypto.h"
#include "Family.h"
namespace i2p
{
namespace data
{
Families::Families ()
{
}
Families::~Families ()
{
}
void Families::LoadCertificate (const std::string& filename)
{
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
SSL * ssl = SSL_new (ctx);
X509 * cert = SSL_get_certificate (ssl);
if (cert)
{
std::shared_ptr<i2p::crypto::Verifier> verifier;
// 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 * family = strstr (cn, ".family");
if (family) family[0] = 0;
}
auto pkey = X509_get_pubkey (cert);
int keyType = EVP_PKEY_type(pkey->type);
switch (keyType)
{
case EVP_PKEY_DSA:
// TODO:
break;
case EVP_PKEY_EC:
{
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
if (ecKey)
{
auto group = EC_KEY_get0_group (ecKey);
if (group)
{
int curve = EC_GROUP_get_curve_name (group);
if (curve == NID_X9_62_prime256v1)
{
uint8_t signingKey[64];
BIGNUM * x = BN_new(), * y = BN_new();
EC_POINT_get_affine_coordinates_GFp (group,
EC_KEY_get0_public_key (ecKey), x, y, NULL);
i2p::crypto::bn2buf (x, signingKey, 32);
i2p::crypto::bn2buf (y, signingKey + 32, 32);
BN_free (x); BN_free (y);
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>(signingKey);
}
else
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
EC_KEY_free (ecKey);
}
break;
}
default:
LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
}
EVP_PKEY_free (pkey);
if (verifier && cn)
m_SigningKeys[cn] = verifier;
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Family: Can't open certificate file ", filename);
SSL_CTX_free (ctx);
}
void Families::LoadCertificates ()
{
std::string certDir = i2p::fs::DataDirPath("certificates", "family");
std::vector<std::string> files;
int numCertificates = 0;
if (!i2p::fs::ReadDir(certDir, files)) {
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
return;
}
for (const std::string & file : files) {
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
LogPrint(eLogWarning, "Family: ignoring file ", file);
continue;
}
LoadCertificate (file);
numCertificates++;
}
LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded");
}
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
const char * signature, const char * key)
{
uint8_t buf[50], signatureBuf[64];
size_t len = family.length (), signatureLen = strlen (signature);
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32;
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
auto it = m_SigningKeys.find (family);
if (it != m_SigningKeys.end ())
return it->second->Verify (buf, len, signatureBuf);
// TODO: process key
return true;
}
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident)
{
auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
std::string sig;
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
SSL * ssl = SSL_new (ctx);
EVP_PKEY * pkey = SSL_get_privatekey (ssl);
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
if (ecKey)
{
auto group = EC_KEY_get0_group (ecKey);
if (group)
{
int curve = EC_GROUP_get_curve_name (group);
if (curve == NID_X9_62_prime256v1)
{
uint8_t signingPrivateKey[32], buf[50], signature[64];
i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32);
i2p::crypto::ECDSAP256Signer signer (signingPrivateKey);
size_t len = family.length ();
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32;
signer.Sign (buf, len, signature);
len = Base64EncodingBufferSize (64);
char * b64 = new char[len+1];
len = ByteStreamToBase64 (signature, 64, b64, len);
b64[len] = 0;
sig = b64;
delete[] b64;
}
else
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Family: Can't open keys file: ", filename);
SSL_CTX_free (ctx);
return sig;
}
}
}

38
Family.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef FAMILY_H__
#define FAMILY_H__
#include <map>
#include <string>
#include <memory>
#include "Signature.h"
#include "Identity.h"
namespace i2p
{
namespace data
{
class Families
{
public:
Families ();
~Families ();
void LoadCertificates ();
bool VerifyFamily (const std::string& family, const IdentHash& ident,
const char * signature, const char * key = nullptr);
private:
void LoadCertificate (const std::string& filename);
private:
std::map<std::string, std::shared_ptr<i2p::crypto::Verifier> > m_SigningKeys;
};
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
// return base64 signature of empty string in case of failure
}
}
#endif

View File

@@ -2,12 +2,14 @@
#include "I2PEndian.h"
#include <map>
#include <string>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Timestamp.h"
#include "Destination.h"
#include "Log.h"
#include "Garlic.h"
namespace i2p
@@ -15,17 +17,18 @@ namespace i2p
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
const i2p::data::RoutingDestination * destination, int numTags):
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdated (numTags > 0)
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
{
// create new session tags and session key
m_Rnd.GenerateBlock (m_SessionKey, 32);
RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
}
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdated (false)
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
@@ -39,25 +42,62 @@ namespace garlic
delete it.second;
m_UnconfirmedTagsMsgs.clear ();
}
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
{
if (!m_SharedRoutingPath) return nullptr;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED ||
!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
ts*1000LL > m_SharedRoutingPath->remoteLease->endDate ||
ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT)
m_SharedRoutingPath = nullptr;
if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++;
return m_SharedRoutingPath;
}
void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path)
{
if (path && path->outboundTunnel && path->remoteLease)
{
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
path->numTimesUsed = 0;
}
else
path = nullptr;
m_SharedRoutingPath = path;
}
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
{
auto tags = new UnconfirmedTags (m_NumTags);
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++)
{
m_Rnd.GenerateBlock (tags->sessionTags[i], 32);
RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
}
return tags;
}
void GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
{
TagsConfirmed (msgID);
if (msgID == m_LeaseSetUpdateMsgID)
{
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
CleanupExpiredTags ();
}
void GarlicRoutingSession::TagsConfirmed (uint32_t 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;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
@@ -67,22 +107,38 @@ namespace garlic
m_UnconfirmedTagsMsgs.erase (it);
delete tags;
}
}
bool GarlicRoutingSession::CleanupExpiredTags ()
{
uint32_t 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++;
}
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (it->first);
delete it->second;
it = m_UnconfirmedTagsMsgs.erase (it);
}
else
it++;
}
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg)
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
I2NPMessage * m = NewI2NPMessage ();
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
size_t len = 0;
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
@@ -108,19 +164,19 @@ namespace garlic
// create message
if (!tagFound) // new session
{
LogPrint ("No garlic tags available. Use ElGamal");
LogPrint (eLogWarning, "Garlic: No tags available, will use ElGamal");
if (!m_Destination)
{
LogPrint ("Can't use ElGamal for unknown destination");
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
return nullptr;
}
// create ElGamal block
ElGamalBlock elGamal;
memcpy (elGamal.sessionKey, m_SessionKey, 32);
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
RAND_bytes (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
SHA256(elGamal.preIV, 32, iv);
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Encryption.SetIV (iv);
buf += 514;
len += 514;
@@ -130,27 +186,25 @@ namespace garlic
// session tag
memcpy (buf, tag, 32);
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, tag, 32);
SHA256(tag, 32, iv);
m_Encryption.SetIV (iv);
buf += 32;
len += 32;
}
// AES block
len += CreateAESBlock (buf, msg);
*(uint32_t *)(m->GetPayload ()) = htobe32 (len);
htobe32buf (m->GetPayload (), len);
m->len += len + 4;
FillI2NPMessageHeader (m, eI2NPGarlic);
if (msg)
DeleteI2NPMessage (msg);
m->FillI2NPMessageHeader (eI2NPGarlic);
return m;
}
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, const I2NPMessage * msg)
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
{
size_t blockSize = 0;
bool createNewTags = m_Owner && ((int)m_SessionTags.size () <= m_NumTags/2);
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
*(uint16_t *)buf = newTags ? htobe16 (newTags->numTags) : 0; // tag count
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
blockSize += 2;
if (newTags) // session tags recreated
{
@@ -167,8 +221,8 @@ namespace garlic
buf[blockSize] = 0; // flag
blockSize++;
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
*payloadSize = htobe32 (len);
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
htobe32buf (payloadSize, len);
SHA256(buf + blockSize, len, payloadHash);
blockSize += len;
size_t rem = blockSize % 16;
if (rem)
@@ -177,10 +231,11 @@ namespace garlic
return blockSize;
}
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags)
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint32_t msgID = m_Rnd.GenerateWord32 ();
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
size_t size = 0;
uint8_t * numCloves = payload + size;
*numCloves = 0;
@@ -188,26 +243,36 @@ namespace garlic
if (m_Owner)
{
if (newTags) // new session
// resubmit non-confirmed LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted &&
i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
// attach DeviveryStatus if necessary
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
{
// clove is DeliveryStatus
size += CreateDeliveryStatusClove (payload + size, msgID);
if (size > 0) // successive?
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
if (cloveSize > 0) // successive?
{
size += cloveSize;
(*numCloves)++;
m_UnconfirmedTagsMsgs[msgID] = newTags;
m_Owner->DeliveryStatusSent (this, msgID);
if (newTags) // new tags created
m_UnconfirmedTagsMsgs[msgID] = newTags;
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
}
else
LogPrint ("DeliveryStatus clove was not created");
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
}
if (m_LeaseSetUpdated)
// attach LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
{
m_LeaseSetUpdated = false;
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
m_LeaseSetUpdateMsgID = msgID;
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
// clove if our leaseSet must be attached
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
size += CreateGarlicClove (payload + size, leaseSet, false);
DeleteI2NPMessage (leaseSet);
size += CreateGarlicClove (payload + size, leaseSet, false);
(*numCloves)++;
}
}
@@ -219,16 +284,16 @@ namespace garlic
memset (payload + size, 0, 3); // certificate of message
size += 3;
*(uint32_t *)(payload + size) = htobe32 (msgID); // MessageID
htobe32buf (payload + size, msgID); // MessageID
size += 4;
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message
htobe64buf (payload + size, ts); // Expiration of message
size += 8;
return size;
}
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination)
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
size_t size = 0;
if (isDestination && m_Destination)
{
@@ -245,9 +310,11 @@ namespace garlic
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4;
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
memset (buf + size, 0, 3); // certificate of clove
size += 3;
@@ -259,131 +326,138 @@ namespace garlic
size_t size = 0;
if (m_Owner)
{
auto leases = m_Owner->GetLeaseSet ()->GetNonExpiredLeases ();
if (!leases.empty ())
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
if (inboundTunnel)
{
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
size++;
uint32_t i = m_Rnd.GenerateWord32 (0, leases.size () - 1);
// hash and tunnelID sequence is reversed for Garlic
memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash
memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash
size += 32;
*(uint32_t *)(buf + size) = htobe32 (leases[i].tunnelID); // tunnelID
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
size += 4;
// create msg
I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
auto msg = CreateDeliveryStatusMsg (msgID);
if (m_Owner)
{
//encrypt
uint8_t key[32], tag[32];
RAND_bytes (key, 32); // random session key
RAND_bytes (tag, 32); // random session tag
m_Owner->SubmitSessionKey (key, tag);
GarlicRoutingSession garlic (key, tag);
msg = garlic.WrapSingleMessage (msg);
}
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
DeleteI2NPMessage (msg);
// fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4;
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
memset (buf + size, 0, 3); // certificate of clove
size += 3;
}
else
LogPrint ("All tunnels of local LeaseSet expired");
LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus");
}
else
LogPrint ("Missing local LeaseSet");
LogPrint (eLogWarning, "Garlic: Missing local LeaseSet");
return size;
}
GarlicDestination::~GarlicDestination ()
{
for (auto it: m_Sessions)
delete it.second;
m_Sessions.clear ();
}
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
{
if (key)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
decryption->SetKey (key);
m_Tags[SessionTag(tag)] = decryption;
m_Tags[SessionTag(tag, ts)] = decryption;
}
}
void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg)
bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{
AddSessionKey (key, tag);
return true;
}
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
uint8_t * buf = msg->GetPayload ();
uint32_t length = be32toh (*(uint32_t *)buf);
buf += 4; // lentgh
uint32_t length = bufbe32toh (buf);
if (length > msg->GetLength ())
{
LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ());
return;
}
buf += 4; // length
auto it = m_Tags.find (SessionTag(buf));
if (it != m_Tags.end ())
{
// tag found. Use AES
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, buf, 32);
it->second->SetIV (iv);
it->second->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
m_Tags.erase (it); // tag might be used only once
if (length >= 32)
{
uint8_t iv[32]; // IV is first 16 bytes
SHA256(buf, 32, iv);
it->second->SetIV (iv);
it->second->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
}
else
LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes");
m_Tags.erase (it); // tag might be used only once
}
else
{
// tag not found. Use ElGamal
ElGamalBlock elGamal;
if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
{
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
decryption->SetKey (elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
SHA256(elGamal.preIV, 32, iv);
decryption->SetIV (iv);
decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
}
else
LogPrint ("Failed to decrypt garlic");
LogPrint (eLogError, "Garlic: Failed to decrypt message");
}
DeleteI2NPMessage (msg);
// cleanup expired tags
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
if (m_LastTagsCleanupTime)
{
int numExpiredTags = 0;
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
{
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
numExpiredTags++;
it = m_Tags.erase (it);
}
else
it++;
}
LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
}
m_LastTagsCleanupTime = ts;
}
}
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
i2p::tunnel::InboundTunnel * from)
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
uint16_t tagCount = be16toh (*(uint16_t *)buf);
buf += 2;
uint16_t tagCount = bufbe16toh (buf);
buf += 2; len -= 2;
if (tagCount > 0)
{
if (tagCount*32 > len)
{
LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len);
return ;
}
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < tagCount; i++)
m_Tags[SessionTag(buf + i*32, ts)] = decryption;
}
buf += tagCount*32;
uint32_t payloadSize = be32toh (*(uint32_t *)buf);
len -= tagCount*32;
uint32_t payloadSize = bufbe32toh (buf);
if (payloadSize > len)
{
LogPrint ("Unexpected payload size ", payloadSize);
LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize);
return;
}
buf += 4;
@@ -394,20 +468,21 @@ namespace garlic
buf++; // flag
// payload
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, buf, payloadSize);
if (memcmp (hash, payloadHash, 32)) // payload hash doesn't match
uint8_t digest[32];
SHA256 (buf, payloadSize, digest);
if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match
{
LogPrint ("Wrong payload hash");
LogPrint (eLogError, "Garlic: wrong payload hash");
return;
}
HandleGarlicPayload (buf, payloadSize, from);
}
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
const uint8_t * buf1 = buf;
int numCloves = buf[0];
LogPrint (numCloves," cloves");
LogPrint (eLogDebug, "Garlic: ", numCloves," cloves");
buf++;
for (int i = 0; i < numCloves; i++)
{
@@ -417,97 +492,142 @@ namespace garlic
if (flag & 0x80) // encrypted?
{
// TODO: implement
LogPrint ("Clove encrypted");
LogPrint (eLogWarning, "Garlic: clove encrypted");
buf += 32;
}
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
switch (deliveryType)
{
case eGarlicDeliveryTypeLocal:
LogPrint ("Garlic type local");
LogPrint (eLogDebug, "Garlic: type local");
HandleI2NPMessage (buf, len, from);
break;
case eGarlicDeliveryTypeDestination:
LogPrint ("Garlic type destination");
LogPrint (eLogDebug, "Garlic: type destination");
buf += 32; // destination. check it later or for multiple destinations
HandleI2NPMessage (buf, len, from);
break;
case eGarlicDeliveryTypeTunnel:
{
LogPrint ("Garlic type tunnel");
LogPrint (eLogDebug, "Garlic: type tunnel");
// gwHash and gwTunnel sequence is reverted
uint8_t * gwHash = buf;
buf += 32;
uint32_t gwTunnel = be32toh (*(uint32_t *)buf);
uint32_t gwTunnel = bufbe32toh (buf);
buf += 4;
i2p::tunnel::OutboundTunnel * tunnel = nullptr;
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
{
I2NPMessage * msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
}
else
LogPrint ("No outbound tunnels available for garlic clove");
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
break;
}
case eGarlicDeliveryTypeRouter:
LogPrint ("Garlic type router not supported");
LogPrint (eLogWarning, "Garlic: type router not supported");
buf += 32;
break;
default:
LogPrint ("Unknow garlic delivery type ", (int)deliveryType);
LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
}
buf += GetI2NPMessageLength (buf); // I2NP
buf += 4; // CloveID
buf += 8; // Date
buf += 3; // Certificate
if (buf - buf1 > (int)len)
{
LogPrint (eLogError, "Garlic: clove is too long");
break;
}
}
}
I2NPMessage * GarlicDestination::WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * msg, bool attachLeaseSet)
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
{
auto session = GetRoutingSession (destination, attachLeaseSet ? 32 : 0); // don't use tag if no LeaseSet
auto session = GetRoutingSession (destination, attachLeaseSet);
return session->WrapSingleMessage (msg);
}
GarlicRoutingSession * GarlicDestination::GetRoutingSession (
const i2p::data::RoutingDestination& destination, int numTags)
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{
auto it = m_Sessions.find (destination.GetIdentHash ());
GarlicRoutingSession * session = nullptr;
if (it != m_Sessions.end ())
session = it->second;
GarlicRoutingSessionPtr session;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (destination->GetIdentHash ());
if (it != m_Sessions.end ())
session = it->second;
}
if (!session)
{
session = new GarlicRoutingSession (this, &destination, numTags);
session = std::make_shared<GarlicRoutingSession> (this, destination,
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination.GetIdentHash ()] = session;
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
}
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID)
void GarlicDestination::CleanupExpiredTags ()
{
m_CreatedSessions[msgID] = session;
// incoming
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numExpiredTags = 0;
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
{
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
numExpiredTags++;
it = m_Tags.erase (it);
}
else
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 ())
{
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
it = m_Sessions.erase (it);
}
else
it++;
}
}
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
{
m_DeliveryStatusSessions.erase (msgID);
}
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
{
m_DeliveryStatusSessions[msgID] = session;
}
void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
uint32_t msgID = be32toh (deliveryStatus->msgID);
uint32_t msgID = bufbe32toh (msg->GetPayload ());
{
auto it = m_CreatedSessions.find (msgID);
if (it != m_CreatedSessions.end ())
auto it = m_DeliveryStatusSessions.find (msgID);
if (it != m_DeliveryStatusSessions.end ())
{
it->second->TagsConfirmed (msgID);
m_CreatedSessions.erase (it);
LogPrint ("Garlic message ", msgID, " acknowledged");
it->second->MessageConfirmed (msgID);
m_DeliveryStatusSessions.erase (it);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
}
DeleteI2NPMessage (msg);
}
void GarlicDestination::SetLeaseSetUpdated ()
@@ -517,12 +637,12 @@ namespace garlic
it.second->SetLeaseSetUpdated ();
}
void GarlicDestination::ProcessGarlicMessage (I2NPMessage * msg)
void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleGarlicMessage (msg);
}
void GarlicDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg)
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleDeliveryStatusMessage (msg);
}

128
Garlic.h
View File

@@ -8,15 +8,19 @@
#include <thread>
#include <mutex>
#include <memory>
#include <cryptopp/osrng.h>
#include "aes.h"
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "LeaseSet.h"
#include "Queue.h"
#include "Identity.h"
namespace i2p
{
{
namespace tunnel
{
class OutboundTunnel;
}
namespace garlic
{
@@ -28,17 +32,19 @@ namespace garlic
eGarlicDeliveryTypeTunnel = 3
};
#pragma pack(1)
struct ElGamalBlock
{
uint8_t sessionKey[32];
uint8_t preIV[32];
uint8_t padding[158];
};
#pragma pack()
const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used
struct SessionTag: public i2p::data::Tag<32>
{
@@ -52,10 +58,27 @@ namespace garlic
#endif
uint32_t creationTime; // seconds since epoch
};
class GarlicDestination;
class GarlicRoutingSession
struct GarlicRoutingPath
{
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease;
int rtt; // RTT
uint32_t updateTime; // seconds since epoch
int numTimesUsed;
};
class GarlicDestination;
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
{
enum LeaseSetUpdateStatus
{
eLeaseSetUpToDate = 0,
eLeaseSetUpdated,
eLeaseSetSubmitted,
eLeaseSetDoNotSend
};
struct UnconfirmedTags
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
@@ -67,79 +90,110 @@ namespace garlic
public:
GarlicRoutingSession (GarlicDestination * owner, const i2p::data::RoutingDestination * destination, int numTags);
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
int numTags, bool attachLeaseSet);
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~GarlicRoutingSession ();
I2NPMessage * WrapSingleMessage (I2NPMessage * msg);
void TagsConfirmed (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
void SetLeaseSetUpdated () { m_LeaseSetUpdated = true; };
void SetLeaseSetUpdated ()
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
private:
size_t CreateAESBlock (uint8_t * buf, const I2NPMessage * msg);
size_t CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags);
size_t CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination);
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
void TagsConfirmed (uint32_t msgID);
UnconfirmedTags * GenerateSessionTags ();
private:
GarlicDestination * m_Owner;
const i2p::data::RoutingDestination * m_Destination;
uint8_t m_SessionKey[32];
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;
bool m_LeaseSetUpdated;
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
i2p::crypto::CBCEncryption m_Encryption;
CryptoPP::AutoSeededRandomPool m_Rnd;
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
public:
// for HTTP only
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
};
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
class GarlicDestination: public i2p::data::LocalDestination
{
public:
GarlicDestination (): m_LastTagsCleanupTime (0) {};
GarlicDestination (): m_NumTags (32) {}; // 32 tags by default
~GarlicDestination ();
GarlicRoutingSession * GetRoutingSession (const i2p::data::RoutingDestination& destination, int numTags);
I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination,
I2NPMessage * msg, bool attachLeaseSet = false);
void SetNumTags (int numTags) { m_NumTags = numTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID);
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
virtual void ProcessGarlicMessage (I2NPMessage * msg);
virtual void ProcessDeliveryStatusMessage (I2NPMessage * msg);
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated ();
virtual const i2p::data::LeaseSet * GetLeaseSet () = 0; // TODO
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) = 0;
virtual std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
protected:
void HandleGarlicMessage (I2NPMessage * msg);
void HandleDeliveryStatusMessage (I2NPMessage * msg);
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
private:
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
i2p::tunnel::InboundTunnel * from);
void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private:
// outgoing sessions
int m_NumTags;
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions;
std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions;
// incoming
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
uint32_t m_LastTagsCleanupTime;
// DeliveryStatus
std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public:
// for HTTP only
size_t GetNumIncomingTags () const { return m_Tags.size (); }
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
};
}
}

View File

@@ -1,87 +1,321 @@
#include <cstring>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include "ClientContext.h"
#include <string>
#include <atomic>
#include "HTTPProxy.h"
#include "util.h"
#include "Identity.h"
#include "Streaming.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include "I2PTunnel.h"
namespace i2p
{
namespace proxy
{
void HTTPProxyConnection::parseHeaders(const std::string& h, std::vector<header>& hm) {
std::string str (h);
std::string::size_type idx;
std::string t;
int i = 0;
while( (idx=str.find ("\r\n")) != std::string::npos) {
t=str.substr (0,idx);
str.erase (0,idx+2);
if (t == "")
break;
idx=t.find(": ");
if (idx == std::string::npos)
static const size_t http_buffer_size = 8192;
class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler>
{
private:
enum state
{
std::cout << "Bad header line: " << t << std::endl;
break;
}
LogPrint ("Name: ", t.substr (0,idx), " Value: ", t.substr (idx+2));
hm[i].name = t.substr (0,idx);
hm[i].value = t.substr (idx+2);
i++;
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);
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void AsyncSockRead();
void HTTPRequestFailed(/*std::string message*/);
void ExtractRequest();
bool ValidateHTTPRequest();
void HandleJumpServices();
bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
void SentHTTPFailed(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
uint8_t m_http_buff[http_buffer_size];
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
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(); }
};
void HTTPProxyHandler::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 {
LogPrint(eLogError, "HTTPProxy: no socket for read");
}
}
void HTTPProxyConnection::ExtractRequest(request& r)
void HTTPProxyHandler::Terminate() {
if (Kill()) return;
if (m_sock)
{
LogPrint(eLogDebug, "HTTPProxy: close sock");
m_sock->close();
m_sock = nullptr;
}
Done(shared_from_this());
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
{
std::string requestString = m_Buffer;
int idx=requestString.find(" ");
std::string method = requestString.substr(0,idx);
requestString = requestString.substr(idx+1);
idx=requestString.find(" ");
std::string requestUrl = requestString.substr(0,idx);
LogPrint("method is: ", method, "\nRequest is: ", requestUrl);
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\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 HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate)
{
m_state = nstate;
}
void HTTPProxyHandler::ExtractRequest()
{
LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url);
std::string server="";
std::string port="80";
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
boost::smatch m;
std::string path;
if(boost::regex_search(requestUrl, m, rHTTP, boost::match_extra)) {
if(boost::regex_search(m_url, m, rHTTP, boost::match_extra))
{
server=m[1].str();
if(m[2].str() != "") {
port=m[3].str();
}
if (m[2].str() != "") port=m[3].str();
path=m[4].str();
}
LogPrint("server is: ",server, " port is: ", port, "\n path is: ",path);
r.uri = path;
r.method = method;
r.host = server;
r.port = boost::lexical_cast<int>(port);
LogPrint(eLogDebug, "HTTPProxy: server: ", server, ", port: ", port, ", path: ", path);
m_address = server;
m_port = boost::lexical_cast<int>(port);
m_path = path;
}
void HTTPProxyConnection::RunRequest()
bool HTTPProxyHandler::ValidateHTTPRequest()
{
request r;
ExtractRequest(r);
parseHeaders(m_Buffer, r.headers);
size_t addressHelperPos = r.uri.find ("i2paddresshelper");
if (addressHelperPos != std::string::npos)
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
{
// jump service
size_t addressPos = r.uri.find ("=", addressHelperPos);
if (addressPos != std::string::npos)
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
HTTPRequestFailed(); //TODO: send right stuff
return false;
}
return true;
}
void HTTPProxyHandler::HandleJumpServices()
{
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;
}
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::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
{
ExtractRequest(); //TODO: parse earlier
if (!ValidateHTTPRequest()) return false;
HandleJumpServices();
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)
{
LogPrint ("Jump service for ", r.host, " found. Inserting to address book");
auto base64 = r.uri.substr (addressPos + 1);
i2p::client::context.GetAddressBook ().InsertAddress (r.host, base64);
*eol = 0; eol++;
if (strncmp ((const char *)http_buff, "Referer", 7)) // strip out referer
{
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--;
}
}
}
}
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;
}
http_buff++;
len--;
if (m_state == DONE)
return CreateHTTPRequest(http_buff,len);
}
return true;
}
void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes");
if(ecode)
{
LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
Terminate();
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();
}
}
void HTTPProxyHandler::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)
{
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
{
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
}
}
HTTPProxyServer::HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
{
}
LogPrint("Requesting ", r.host, ":", r.port, " with path ", r.uri, " and method ", r.method);
SendToAddress (r.host, r.port, m_Buffer, m_BufferLen);
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
return std::make_shared<HTTPProxyHandler> (this, socket);
}
}
}

View File

@@ -1,42 +1,32 @@
#ifndef HTTP_PROXY_H__
#define HTTP_PROXY_H__
#include <sstream>
#include <thread>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include "HTTPServer.h"
#include <mutex>
#include "I2PService.h"
#include "Destination.h"
namespace i2p
{
namespace proxy
{
class HTTPProxyConnection : public i2p::util::HTTPConnection
class HTTPProxyServer: public i2p::client::TCPIPAcceptor
{
public:
HTTPProxyConnection (boost::asio::ip::tcp::socket * socket): HTTPConnection(socket) { };
HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
~HTTPProxyServer() {};
protected:
void RunRequest();
void parseHeaders(const std::string& h, std::vector<header>& hm);
void ExtractRequest(request& r);
// Implements TCPIPAcceptor
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
const char* GetName() { return "HTTP Proxy"; }
};
class HTTPProxy : public i2p::util::HTTPServer
{
public:
HTTPProxy (int port): HTTPServer(port) {};
private:
void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket)
{
new HTTPProxyConnection(m_NewSocket);
}
};
typedef HTTPProxyServer HTTPProxy;
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
#include <sstream>
#include <thread>
#include <memory>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include "LeaseSet.h"
@@ -14,7 +15,7 @@ namespace util
{
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
class HTTPConnection
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{
protected:
@@ -38,22 +39,20 @@ namespace util
struct reply
{
std::vector<header> headers;
std::string content;
std::string status_string, content;
std::vector<boost::asio::const_buffer> to_buffers (int status);
};
public:
HTTPConnection (boost::asio::ip::tcp::socket * socket):
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_Timer (socket->get_io_service ()),
m_Stream (nullptr), m_BufferLen (0) { Receive (); };
virtual ~HTTPConnection() { delete m_Socket; }
m_Stream (nullptr), m_BufferLen (0) {};
void Receive ();
private:
void Terminate ();
void Receive ();
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void AsyncStreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
@@ -68,8 +67,12 @@ namespace util
void ShowTransitTunnels (std::stringstream& s);
void ShowLocalDestinations (std::stringstream& s);
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
void ShowSAMSessions (std::stringstream& s);
void ShowSAMSession (const std::string& id, std::stringstream& s);
void ShowI2PTunnels (std::stringstream& s);
void StartAcceptingTunnels (std::stringstream& s);
void StopAcceptingTunnels (std::stringstream& s);
void RunPeerTest (std::stringstream& s);
void FillContent (std::stringstream& s);
std::string ExtractAddress ();
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
@@ -77,9 +80,9 @@ namespace util
protected:
boost::asio::ip::tcp::socket * m_Socket;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
boost::asio::deadline_timer m_Timer;
i2p::stream::Stream * m_Stream;
std::shared_ptr<i2p::stream::Stream> m_Stream;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen;
request m_Request;
@@ -92,7 +95,7 @@ namespace util
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
void SendToDestination (const i2p::data::LeaseSet * remote, int port, const char * buf, size_t len);
void SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len);
public:
@@ -104,7 +107,7 @@ namespace util
{
public:
HTTPServer (int port);
HTTPServer (const std::string& address, int port);
virtual ~HTTPServer ();
void Start ();
@@ -114,18 +117,18 @@ namespace util
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode);
void HandleAccept(const boost::system::error_code& ecode,
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
private:
std::thread * m_Thread;
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ip::tcp::socket * m_NewSocket;
protected:
virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket);
virtual void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
};
}
}

View File

@@ -1,14 +1,15 @@
#include <string.h>
#include <atomic>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Base.h"
#include "Log.h"
#include "Crypto.h"
#include "I2PEndian.h"
#include <cryptopp/sha.h>
#include <cryptopp/gzip.h>
#include "ElGamal.h"
#include "Timestamp.h"
#include "RouterContext.h"
#include "NetDb.h"
#include "Tunnel.h"
#include "base64.h"
#include "Transports.h"
#include "Garlic.h"
#include "I2NPProtocol.h"
@@ -17,305 +18,314 @@ using namespace i2p::transport;
namespace i2p
{
I2NPMessage * NewI2NPMessage ()
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
{
return new I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE>();
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> >();
}
I2NPMessage * NewI2NPShortMessage ()
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ()
{
return new I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE>();
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
}
I2NPMessage * NewI2NPMessage (size_t len)
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
{
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
}
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID)
{
SetTypeID (msgType);
if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4);
SetMsgID (replyMsgID);
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
UpdateSize ();
UpdateChks ();
}
void DeleteI2NPMessage (I2NPMessage * msg)
void I2NPMessage::RenewI2NPMessageHeader ()
{
delete msg;
}
static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID)
{
I2NPHeader * header = msg->GetHeader ();
header->typeID = msgType;
if (replyMsgID) // for tunnel creation
header->msgID = htobe32 (replyMsgID);
else
{
header->msgID = htobe32 (I2NPmsgID);
I2NPmsgID++;
}
header->expiration = htobe64 (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
int len = msg->GetLength () - sizeof (I2NPHeader);
header->size = htobe16 (len);
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, msg->GetPayload (), len);
header->chks = hash[0];
}
void RenewI2NPMessageHeader (I2NPMessage * msg)
{
if (msg)
{
I2NPHeader * header = msg->GetHeader ();
header->msgID = htobe32 (I2NPmsgID);
I2NPmsgID++;
header->expiration = htobe64 (i2p::util::GetMillisecondsSinceEpoch () + 5000);
}
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
SetMsgID (msgID);
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
}
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID)
bool I2NPMessage::IsExpired () const
{
I2NPMessage * msg = NewI2NPMessage (len);
memcpy (msg->GetPayload (), buf, len);
msg->len += len;
FillI2NPMessageHeader (msg, msgType, replyMsgID);
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
auto exp = GetExpiration ();
return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
}
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
auto msg = NewI2NPMessage (len);
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length ", msg->maxLen);
msg->FillI2NPMessageHeader (msgType, replyMsgID);
return msg;
}
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from)
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
I2NPMessage * msg = NewI2NPMessage ();
memcpy (msg->GetBuffer (), buf, len);
msg->len = msg->offset + len;
msg->from = from;
auto msg = NewI2NPMessage ();
if (msg->offset + len < msg->maxLen)
{
memcpy (msg->GetBuffer (), buf, len);
msg->len = msg->offset + len;
msg->from = from;
}
else
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length");
return msg;
}
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
I2NPDeliveryStatusMsg msg;
if (!msg) return nullptr;
auto newMsg = NewI2NPMessage (msg->len);
newMsg->offset = msg->offset;
*newMsg = *msg;
return newMsg;
}
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
{
auto m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
if (msgID)
{
msg.msgID = htobe32 (msgID);
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ());
}
else // for SSU establishment
{
msg.msgID = htobe32 (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
msg.timestamp = htobe64 (2); // netID = 2
}
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
}
m->len += DELIVERY_STATUS_SIZE;
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
return m;
}
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers,
bool encryption, i2p::tunnel::TunnelPool * pool)
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{
I2NPMessage * m = NewI2NPMessage ();
auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, key, 32); // key
buf += 32;
memcpy (buf, from, 32); // from
buf += 32;
uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP;
if (replyTunnelID)
{
*buf = encryption ? 0x03: 0x01; // set delivery flag
*(uint32_t *)(buf+1) = htobe32 (replyTunnelID);
*buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag
htobe32buf (buf+1, replyTunnelID);
buf += 5;
}
else
{
encryption = false; // encryption can we set for tunnels only
*buf = 0; // flag
*buf = flag; // flag
buf++;
}
if (exploratory)
if (excludedPeers)
{
*(uint16_t *)buf = htobe16 (1); // one exlude record
int cnt = excludedPeers->size ();
htobe16buf (buf, cnt);
buf += 2;
// reply with non-floodfill routers only
memset (buf, 0, 32);
buf += 32;
for (auto& it: *excludedPeers)
{
memcpy (buf, it, 32);
buf += 32;
}
}
else
{
if (excludedPeers)
{
int cnt = excludedPeers->size ();
*(uint16_t *)buf = htobe16 (cnt);
buf += 2;
for (auto& it: *excludedPeers)
{
memcpy (buf, it, 32);
buf += 32;
}
}
else
{
// nothing to exclude
*(uint16_t *)buf = htobe16 (0);
buf += 2;
}
}
if (encryption)
{
// session key and tag for reply
auto& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (buf, 32); // key
buf[32] = 1; // 1 tag
rnd.GenerateBlock (buf + 33, 32); // tag
if (pool)
pool->GetGarlicDestination ().AddSessionKey (buf, buf + 33); // introduce new key-tag to garlic engine
else
LogPrint ("Destination for encrypteed reply not specified");
buf += 65;
}
{
// nothing to exclude
htobuf16 (buf, 0);
buf += 2;
}
m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
return m;
}
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
const i2p::data::RouterInfo * floodfill)
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
{
I2NPMessage * m = NewI2NPShortMessage ();
int cnt = excludedFloodfills.size ();
auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, dest, 32); // key
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;
// excluded
htobe16buf (buf, cnt);
buf += 2;
if (cnt > 0)
{
for (auto& it: excludedFloodfills)
{
memcpy (buf, it, 32);
buf += 32;
}
}
// encryption
memcpy (buf, replyKey, 32);
buf[32] = 1; // 1 tag
memcpy (buf + 33, replyTag, 32);
buf += 65;
m->len += (buf - m->GetPayload ());
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
std::vector<i2p::data::IdentHash> routers)
{
auto m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
size_t len = 0;
memcpy (buf, ident, 32);
len += 32;
buf[len] = floodfill ? 1 : 0; // 1 router for now
buf[len] = routers.size ();
len++;
if (floodfill)
for (auto it: routers)
{
memcpy (buf + len, floodfill->GetIdentHash (), 32);
memcpy (buf + len, it, 32);
len += 32;
}
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
len += 32;
m->len += len;
FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply);
return m;
}
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
{
if (!router) // we send own RouterInfo
router = &context.GetRouterInfo ();
router = context.GetSharedRouterInfo ();
I2NPMessage * m = NewI2NPShortMessage ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (msg->key, router->GetIdentHash (), 32);
msg->type = 0;
msg->replyToken = 0;
CryptoPP::Gzip compressor;
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
compressor.MessageEnd();
auto size = compressor.MaxRetrievable ();
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
*(uint16_t *)buf = htobe16 (size); // size
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
memset (buf, 0, 4); // zero tunnelID means direct reply
buf += 4;
memcpy (buf, router->GetIdentHash (), 32);
buf += 32;
}
uint8_t * sizePtr = buf;
buf += 2;
// TODO: check if size doesn't exceed buffer
compressor.Get (buf, size);
m->len += sizeof (I2NPDatabaseStoreMsg) + 2 + size; // payload size
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
m->len += (buf - payload); // payload size
i2p::data::GzipDeflator deflator;
size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
if (size)
{
htobe16buf (sizePtr, size); // size
m->len += size;
}
else
m = nullptr;
if (m)
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken)
{
if (!leaseSet) return nullptr;
I2NPMessage * m = NewI2NPShortMessage ();
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)payload;
memcpy (msg->key, leaseSet->GetIdentHash (), 32);
msg->type = 1; // LeaseSet
msg->replyToken = htobe32 (replyToken);
size_t size = sizeof (I2NPDatabaseStoreMsg);
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
auto leases = leaseSet->GetNonExpiredLeases ();
if (leases.size () > 0)
{
*(uint32_t *)(payload + size) = htobe32 (leases[0].tunnelID);
htobe32buf (payload + size, leases[0]->tunnelID);
size += 4; // reply tunnelID
memcpy (payload + size, leases[0].tunnelGateway, 32);
memcpy (payload + size, leases[0]->tunnelGateway, 32);
size += 32; // reply tunnel gateway
}
else
msg->replyToken = 0;
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
}
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen ();
m->len += size;
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
const uint8_t * ourIdent, uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
const uint8_t * replyKey, const uint8_t * replyIV, uint32_t nextMessageID,
bool isGateway, bool isEndpoint)
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg)
{
I2NPBuildRequestRecordClearText clearText;
clearText.receiveTunnel = htobe32 (receiveTunnelID);
clearText.nextTunnel = htobe32(nextTunnelID);
memcpy (clearText.layerKey, layerKey, 32);
memcpy (clearText.ivKey, ivKey, 32);
memcpy (clearText.replyKey, replyKey, 32);
memcpy (clearText.replyIV, replyIV, 16);
clearText.flag = 0;
if (isGateway) clearText.flag |= 0x80;
if (isEndpoint) clearText.flag |= 0x40;
memcpy (clearText.ourIdent, ourIdent, 32);
memcpy (clearText.nextIdent, nextIdent, 32);
clearText.requestTime = htobe32 (i2p::util::GetHoursSinceEpoch ());
clearText.nextMessageID = htobe32(nextMessageID);
return clearText;
}
void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router,
const I2NPBuildRequestRecordClearText& clearText,
I2NPBuildRequestRecordElGamalEncrypted& record)
{
router.GetElGamalEncryption ()->Encrypt ((uint8_t *)&clearText, sizeof(clearText), record.encrypted);
memcpy (record.toPeer, (const uint8_t *)router.GetIdentHash (), 16);
if (!msg || msg->GetTypeID () != eI2NPDatabaseStore) return false;
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
}
bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText)
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
{
for (int i = 0; i < num; i++)
{
if (!memcmp (records[i].toPeer, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint ("Record ",i," is ours");
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), records[i].encrypted, (uint8_t *)&clearText);
// replace record to reply
I2NPBuildResponseRecord * reply = (I2NPBuildResponseRecord *)(records + i);
if (i2p::context.AcceptsTunnels ())
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
// replace record to reply
if (i2p::context.AcceptsTunnels () &&
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS &&
!i2p::transport::transports.IsBandwidthExceeded ())
{
i2p::tunnel::TransitTunnel * transitTunnel =
i2p::tunnel::CreateTransitTunnel (
be32toh (clearText.receiveTunnel),
clearText.nextIdent, be32toh (clearText.nextTunnel),
clearText.layerKey, clearText.ivKey,
clearText.flag & 0x80, clearText.flag & 0x40);
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
reply->ret = 0;
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
}
else
reply->ret = 30; // always reject with bandwidth reason (30)
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
//TODO: fill filler
CryptoPP::SHA256().CalculateDigest(reply->hash, reply->padding, sizeof (reply->padding) + 1); // + 1 byte of ret
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
// encrypt reply
i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++)
{
encryption.SetKey (clearText.replyKey);
encryption.SetIV (clearText.replyIV);
encryption.Encrypt((uint8_t *)(records + j), sizeof (records[j]), (uint8_t *)(records + j));
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
return true;
}
@@ -326,247 +336,241 @@ namespace i2p
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
int num = buf[0];
LogPrint ("VariableTunnelBuild ", num, " records");
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
{
LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len);
return;
}
i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID);
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
if (tunnel)
{
// endpoint of inbound tunnel
LogPrint ("VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel (static_cast<i2p::tunnel::InboundTunnel *>(tunnel));
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
}
else
{
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
{
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(buf+1);
I2NPBuildRequestRecordClearText clearText;
if (HandleBuildRequestRecords (num, records, clearText))
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText.flag & 0x40) // we are endpoint of outboud tunnel
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText.nextIdent,
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel),
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
be32toh (clearText.nextMessageID)));
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText.nextIdent,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, be32toh (clearText.nextMessageID)));
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
}
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
I2NPBuildRequestRecordClearText clearText;
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, (I2NPBuildRequestRecordElGamalEncrypted *)buf, clearText))
if (len < NUM_TUNNEL_BUILD_RECORDS*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE)
{
if (clearText.flag & 0x40) // we are endpoint of outbound tunnel
LogPrint (eLogError, "TunnelBuild message is too short ", len);
return;
}
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
{
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText.nextIdent,
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel),
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPTunnelBuildReply, buf, len,
be32toh (clearText.nextMessageID)));
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText.nextIdent,
CreateI2NPMessage (eI2NPTunnelBuild, buf, len, be32toh (clearText.nextMessageID)));
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID);
int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
{
LogPrint (eLogError, "VaribleTunnelBuildReply message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
if (tunnel)
{
// reply for outbound tunnel
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddOutboundTunnel (static_cast<i2p::tunnel::OutboundTunnel *>(tunnel));
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
}
else
{
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
LogPrint ("Pending tunnel for message ", replyMsgID, " not found");
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
}
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf)
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{
I2NPMessage * msg = NewI2NPMessage ();
memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
FillI2NPMessageHeader (msg, eI2NPTunnelData);
auto msg = NewI2NPShortMessage ();
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
}
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
{
I2NPMessage * msg = NewI2NPMessage ();
memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
*(uint32_t *)(msg->GetPayload ()) = htobe32 (tunnelID);
auto msg = NewI2NPShortMessage ();
htobe32buf (msg->GetPayload (), tunnelID);
msg->len += 4; // tunnelID
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
}
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
{
auto msg = NewI2NPShortMessage ();
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
FillI2NPMessageHeader (msg, eI2NPTunnelData);
return msg;
}
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
{
I2NPMessage * msg = NewI2NPMessage (len);
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
header->tunnelID = htobe32 (tunnelID);
header->length = htobe16 (len);
memcpy (msg->GetPayload () + sizeof (TunnelGatewayHeader), buf, len);
msg->len += sizeof (TunnelGatewayHeader) + len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
auto msg = NewI2NPMessage (len);
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->len += TUNNEL_GATEWAY_HEADER_SIZE;
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
return msg;
}
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg)
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
{
if (msg->offset >= sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader))
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
{
// message is capable to be used without copying
TunnelGatewayHeader * header = (TunnelGatewayHeader *)(msg->GetBuffer () - sizeof (TunnelGatewayHeader));
header->tunnelID = htobe32 (tunnelID);
uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE;
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
int len = msg->GetLength ();
header->length = htobe16 (len);
msg->offset -= (sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader));
msg->len = msg->offset + sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader) +len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
return msg;
}
else
{
I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
DeleteI2NPMessage (msg);
return msg1;
}
return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
}
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
I2NPMessage * msg = NewI2NPMessage (len);
size_t gatewayMsgOffset = sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
auto msg = NewI2NPMessage (len);
size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->offset += gatewayMsgOffset;
msg->len += gatewayMsgOffset;
memcpy (msg->GetPayload (), buf, len);
msg->len += len;
FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message
len = msg->GetLength ();
msg->offset -= gatewayMsgOffset;
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
header->tunnelID = htobe32 (tunnelID);
header->length = htobe16 (len);
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message
return msg;
}
void HandleTunnelGatewayMsg (I2NPMessage * msg)
{
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
uint32_t tunnelID = be32toh(header->tunnelID);
uint16_t len = be16toh(header->length);
// we make payload as new I2NP message to send
msg->offset += sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
msg->len = msg->offset + len;
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID);
if (msg->GetHeader()->typeID == eI2NPDatabaseStore ||
msg->GetHeader()->typeID == eI2NPDatabaseSearchReply)
{
// transit DatabaseStore my contain new/updated RI
// or DatabaseSearchReply with new routers
auto ds = NewI2NPMessage ();
*ds = *msg;
i2p::data::netdb.PostI2NPMsg (ds);
}
i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
if (tunnel)
tunnel->SendTunnelDataMsg (msg);
else
{
LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found");
i2p::DeleteI2NPMessage (msg);
}
}
size_t GetI2NPMessageLength (const uint8_t * msg)
{
I2NPHeader * header = (I2NPHeader *)msg;
return be16toh (header->size) + sizeof (I2NPHeader);
return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
}
void HandleI2NPMessage (uint8_t * msg, size_t len)
{
I2NPHeader * header = (I2NPHeader *)msg;
uint32_t msgID = be32toh (header->msgID);
LogPrint ("I2NP msg received len=", len,", type=", (int)header->typeID, ", msgID=", (unsigned int)msgID);
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 + sizeof (I2NPHeader);
int size = be16toh (header->size);
switch (header->typeID)
uint8_t * buf = msg + I2NP_HEADER_SIZE;
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
switch (typeID)
{
case eI2NPVariableTunnelBuild:
LogPrint ("VariableTunnelBuild");
HandleVariableTunnelBuildMsg (msgID, buf, size);
break;
case eI2NPVariableTunnelBuildReply:
LogPrint ("VariableTunnelBuildReply");
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break;
case eI2NPTunnelBuild:
LogPrint ("TunnelBuild");
HandleTunnelBuildMsg (buf, size);
break;
case eI2NPTunnelBuildReply:
LogPrint ("TunnelBuildReply");
// TODO:
break;
default:
LogPrint ("Unexpected message ", (int)header->typeID);
LogPrint (eLogWarning, "I2NP: Unexpected message ", (int)typeID);
}
}
void HandleI2NPMessage (I2NPMessage * msg)
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{
switch (msg->GetHeader ()->typeID)
uint8_t typeID = msg->GetTypeID ();
LogPrint (eLogDebug, "I2NP: Handling message with type ", (int)typeID);
switch (typeID)
{
case eI2NPTunnelData:
LogPrint ("TunnelData");
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
case eI2NPTunnelGateway:
LogPrint ("TunnelGateway");
HandleTunnelGatewayMsg (msg);
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
case eI2NPGarlic:
LogPrint ("Garlic");
if (msg->from && msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->GetGarlicDestination ().ProcessGarlicMessage (msg);
{
if (msg->from)
{
if (msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
else
LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore");
}
else
i2p::context.ProcessGarlicMessage (msg);
break;
break;
}
case eI2NPDatabaseStore:
case eI2NPDatabaseSearchReply:
case eI2NPDatabaseLookup:
@@ -574,16 +578,60 @@ namespace i2p
i2p::data::netdb.PostI2NPMsg (msg);
break;
case eI2NPDeliveryStatus:
LogPrint ("DeliveryStatus");
{
if (msg->from && msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg);
else
i2p::context.ProcessDeliveryStatusMessage (msg);
break;
}
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply:
// forward to tunnel thread
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
default:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
DeleteI2NPMessage (msg);
}
}
}
I2NPMessagesHandler::~I2NPMessagesHandler ()
{
Flush ();
}
void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{
switch (msg->GetTypeID ())
{
case eI2NPTunnelData:
m_TunnelMsgs.push_back (msg);
break;
case eI2NPTunnelGateway:
m_TunnelGatewayMsgs.push_back (msg);
break;
default:
HandleI2NPMessage (msg);
}
}
}
void I2NPMessagesHandler::Flush ()
{
if (!m_TunnelMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
m_TunnelMsgs.clear ();
}
if (!m_TunnelGatewayMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
m_TunnelGatewayMsgs.clear ();
}
}
}

View File

@@ -2,81 +2,73 @@
#define I2NP_PROTOCOL_H__
#include <inttypes.h>
#include <set>
#include <string.h>
#include <set>
#include <memory>
#include <openssl/sha.h>
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "LeaseSet.h"
namespace i2p
{
#pragma pack (1)
{
// I2NP header
const size_t I2NP_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4;
const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8;
const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2;
const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1;
struct I2NPHeader
{
uint8_t typeID;
uint32_t msgID;
uint64_t expiration;
uint16_t size;
uint8_t chks;
};
struct I2NPHeaderShort
{
uint8_t typeID;
uint32_t shortExpiration;
};
struct I2NPDatabaseStoreMsg
{
uint8_t key[32];
uint8_t type;
uint32_t replyToken;
};
struct I2NPDeliveryStatusMsg
{
uint32_t msgID;
uint64_t timestamp;
};
// I2NP short header
const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4;
struct I2NPBuildRequestRecordClearText
{
uint32_t receiveTunnel;
uint8_t ourIdent[32];
uint32_t nextTunnel;
uint8_t nextIdent[32];
uint8_t layerKey[32];
uint8_t ivKey[32];
uint8_t replyKey[32];
uint8_t replyIV[16];
uint8_t flag;
uint32_t requestTime;
uint32_t nextMessageID;
uint8_t filler[29];
};
// Tunnel Gateway header
const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0;
const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4;
const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2;
struct I2NPBuildResponseRecord
{
uint8_t hash[32];
uint8_t padding[495];
uint8_t ret;
};
// DeliveryStatus
const size_t DELIVERY_STATUS_MSGID_OFFSET = 0;
const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4;
const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8;
// DatabaseStore
const size_t DATABASE_STORE_KEY_OFFSET = 0;
const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32;
const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1;
const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4;
// TunnelBuild
const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
//BuildRequestRecordClearText
const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222;
struct I2NPBuildRequestRecordElGamalEncrypted
{
uint8_t toPeer[16];
uint8_t encrypted[512];
};
struct TunnelGatewayHeader
{
uint32_t tunnelID;
uint16_t length;
};
// BuildRequestRecordEncrypted
const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16;
#pragma pack ()
// BuildResponseRecord
const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0;
const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32;
const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495;
const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE;
enum I2NPMessageType
{
@@ -96,6 +88,17 @@ namespace i2p
const int NUM_TUNNEL_BUILD_RECORDS = 8;
// DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCYPTION_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
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
const unsigned int MAX_NUM_TRANSIT_TUNNELS = 2500;
namespace tunnel
{
class InboundTunnel;
@@ -103,21 +106,66 @@ namespace tunnel
}
const size_t I2NP_MAX_MESSAGE_SIZE = 32768;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 2400;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60*1000; // 1 minute in milliseconds
struct I2NPMessage
{
uint8_t * buf;
size_t len, offset, maxLen;
i2p::tunnel::InboundTunnel * from;
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
I2NPMessage (): buf (nullptr),len (sizeof (I2NPHeader) + 2),
offset(2), maxLen (0), from (nullptr) {};
// reserve 2 bytes for NTCP header
I2NPHeader * GetHeader () { return (I2NPHeader *)GetBuffer (); };
uint8_t * GetPayload () { return GetBuffer () + sizeof(I2NPHeader); };
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
// header accessors
uint8_t * GetHeader () { return GetBuffer (); };
const uint8_t * GetHeader () const { return GetBuffer (); };
void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; };
void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); };
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); };
uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); };
void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); };
uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); };
void UpdateSize () { SetSize (GetPayloadLength ()); };
void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; };
void UpdateChks ()
{
uint8_t hash[32];
SHA256(GetPayload (), GetPayloadLength (), hash);
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
}
// payload
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; };
const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; };
uint8_t * GetBuffer () { return buf + offset; };
const uint8_t * GetBuffer () const { return buf + offset; };
size_t GetLength () const { return len - offset; };
size_t GetLength () const { return len - offset; };
size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; };
void Align (size_t alignment)
{
if (len + alignment > maxLen) return;
size_t rem = ((size_t)GetBuffer ()) % alignment;
if (rem)
{
offset += (alignment - rem);
len += (alignment - rem);
}
}
size_t Concat (const uint8_t * buf1, size_t len1)
{
// make sure with don't write beyond maxLen
if (len + len1 > maxLen) len1 = maxLen - len;
memcpy (buf + len, buf1, len1);
len += len1;
return len1;
}
I2NPMessage& operator=(const I2NPMessage& other)
{
@@ -126,83 +174,91 @@ namespace tunnel
from = other.from;
return *this;
}
// for SSU only
uint8_t * GetSSUHeader () { return buf + offset + sizeof(I2NPHeader) - sizeof(I2NPHeaderShort); };
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
{
I2NPHeaderShort ssu = *(I2NPHeaderShort *)GetSSUHeader ();
I2NPHeader * header = GetHeader ();
header->typeID = ssu.typeID;
header->msgID = htobe32 (msgID);
header->expiration = htobe64 (be32toh (ssu.shortExpiration)*1000LL);
header->size = htobe16 (len - offset - sizeof (I2NPHeader));
header->chks = 0;
const uint8_t * ssu = GetSSUHeader ();
GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID (msgID);
SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL);
SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0);
}
uint32_t ToSSU () // return msgID
{
I2NPHeader header = *GetHeader ();
I2NPHeaderShort * ssu = (I2NPHeaderShort *)GetSSUHeader ();
ssu->typeID = header.typeID;
ssu->shortExpiration = htobe32 (be64toh (header.expiration)/1000LL);
len = offset + sizeof (I2NPHeaderShort) + be16toh (header.size);
return be32toh (header.msgID);
uint8_t header[I2NP_HEADER_SIZE];
memcpy (header, GetHeader (), I2NP_HEADER_SIZE);
uint8_t * ssu = GetSSUHeader ();
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
}
void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0);
void RenewI2NPMessageHeader ();
bool IsExpired () const;
};
template<int sz>
struct I2NPMessageBuffer: public I2NPMessage
{
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
uint8_t m_Buffer[sz];
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
};
I2NPMessage * NewI2NPMessage ();
I2NPMessage * NewI2NPShortMessage ();
I2NPMessage * NewI2NPMessage (size_t len);
void DeleteI2NPMessage (I2NPMessage * msg);
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID = 0);
void RenewI2NPMessageHeader (I2NPMessage * msg);
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from = nullptr);
std::shared_ptr<I2NPMessage> NewI2NPMessage ();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false,
std::set<i2p::data::IdentHash> * excludedPeers = nullptr, bool encryption = false,
i2p::tunnel::TunnelPool * pool = nullptr);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg);
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
const uint8_t * ourIdent, uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
const uint8_t * replyKey, const uint8_t * replyIV, uint32_t nextMessageID,
bool isGateway, bool isEndpoint);
void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router,
const I2NPBuildRequestRecordClearText& clearText,
I2NPBuildRequestRecordElGamalEncrypted& record);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf);
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ();
void HandleTunnelGatewayMsg (I2NPMessage * msg);
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
size_t GetI2NPMessageLength (const uint8_t * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (I2NPMessage * msg);
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler
{
public:
~I2NPMessagesHandler ();
void PutNextMessage (std::shared_ptr<I2NPMessage> msg);
void Flush ();
private:
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
};
}
#endif

548
I2PControl.cpp Normal file
View File

@@ -0,0 +1,548 @@
#include <stdio.h>
#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>
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp>
#endif
#include "FS.h"
#include "Log.h"
#include "Config.h"
#include "NetDb.h"
#include "RouterContext.h"
#include "Daemon.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "Transports.h"
#include "version.h"
#include "I2PControl.h"
namespace i2p
{
namespace client
{
I2PControlService::I2PControlService (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_SSLContext (m_Service, boost::asio::ssl::context::sslv23),
m_ShutdownTimer (m_Service)
{
i2p::config::GetOption("i2pcontrol.password", m_Password);
// certificate / keys
std::string i2pcp_crt; i2p::config::GetOption("i2pcontrol.cert", i2pcp_crt);
std::string i2pcp_key; i2p::config::GetOption("i2pcontrol.key", i2pcp_key);
if (i2pcp_crt.at(0) != '/')
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
if (i2pcp_key.at(0) != '/')
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) {
LogPrint (eLogInfo, "I2PControl: creating new certificate for control connection");
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
} else {
LogPrint(eLogDebug, "I2PControl: using cert from ", i2pcp_crt);
}
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem);
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem);
// handlers
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
// I2PControl
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
// RouterInfo
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
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.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
// RouterManager
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler;
}
I2PControlService::~I2PControlService ()
{
Stop ();
}
void I2PControlService::Start ()
{
if (!m_IsRunning)
{
Accept ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
}
}
void I2PControlService::Stop ()
{
if (m_IsRunning)
{
m_IsRunning = false;
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
void I2PControlService::Run ()
{
while (m_IsRunning)
{
try {
m_Service.run ();
} catch (std::exception& ex) {
LogPrint (eLogError, "I2PControl: runtime exception: ", ex.what ());
}
}
}
void I2PControlService::Accept ()
{
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
{
if (ecode != boost::asio::error::operation_aborted)
Accept ();
if (ecode) {
LogPrint (eLogError, "I2PControl: accept error: ", ecode.message ());
return;
}
LogPrint (eLogDebug, "I2PControl: new request from ", socket->lowest_layer ().remote_endpoint ());
Handshake (socket);
}
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
{
socket->async_handshake(boost::asio::ssl::stream_base::server,
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
}
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
{
if (ecode) {
LogPrint (eLogError, "I2PControl: handshake error: ", ecode.message ());
return;
}
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
ReadRequest (socket);
}
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
{
auto request = std::make_shared<I2PControlBuffer>();
socket->async_read_some (
#if defined(BOOST_ASIO_HAS_STD_ARRAY)
boost::asio::buffer (*request),
#else
boost::asio::buffer (request->data (), request->size ()),
#endif
std::bind(&I2PControlService::HandleRequestReceived, this,
std::placeholders::_1, std::placeholders::_2, socket, request));
}
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode) {
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
return;
} else {
try
{
bool isHtml = !memcmp (buf->data (), "POST", 4);
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");
response << "{\"id\":null,\"error\":";
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
response << "\"jsonrpc\":\"2.0\"}";
#else
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 ())
{
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, isHtml);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
}
catch (...)
{
LogPrint (eLogError, "I2PControl: handle request unknown exception");
}
}
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
{
ss << "\"" << name << "\":" << value;
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const
{
ss << "\"" << name << "\":";
if (value.length () > 0)
ss << "\"" << value << "\"";
else
ss << "null";
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
{
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
{
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);
}
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));
}
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode) {
LogPrint (eLogError, "I2PControl: write error: ", ecode.message ());
}
}
// handlers
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
int api = params.get<int> ("API");
auto password = params.get<std::string> ("Password");
LogPrint (eLogDebug, "I2PControl: Authenticate API=", api, " Password=", password);
if (password != m_Password) {
LogPrint (eLogError, "I2PControl: Authenticate - Invalid password: ", password);
return;
}
InsertParam (results, "API", api);
results << ",";
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
m_Tokens.insert (token);
InsertParam (results, "Token", token);
}
void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
auto echo = params.get<std::string> ("Echo");
LogPrint (eLogDebug, "I2PControl Echo Echo=", echo);
InsertParam (results, "Result", echo);
}
// I2PControl
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto& it: params)
{
LogPrint (eLogDebug, "I2PControl: I2PControl request: ", it.first);
auto it1 = m_I2PControlHandlers.find (it.first);
if (it1 != m_I2PControlHandlers.end ())
{
(this->*(it1->second))(it.second.data ());
InsertParam (results, it.first, "");
}
else
LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first);
}
}
void I2PControlService::PasswordHandler (const std::string& value)
{
LogPrint (eLogWarning, "I2PControl: new password=", value, ", to make it persistent you should update your config!");
m_Password = value;
m_Tokens.clear ();
}
// RouterInfo
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
if (it1 != m_RouterInfoHandlers.end ())
{
if (it != params.begin ()) results << ",";
(this->*(it1->second))(results);
}
else
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
}
}
void I2PControlService::UptimeHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000);
}
void I2PControlService::VersionHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.version", VERSION);
}
void I2PControlService::StatusHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.status", "???"); // TODO:
}
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
}
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
}
void I2PControlService::NetStatusHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
}
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
{
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
}
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetInBandwidth ();
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
}
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetOutBandwidth ();
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
}
void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
}
void I2PControlService::NetTotalSentBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
}
// RouterManager
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
auto it1 = m_RouterManagerHandlers.find (it->first);
if (it1 != m_RouterManagerHandlers.end ()) {
(this->*(it1->second))(results);
} else
LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first);
}
}
void I2PControlService::ShutdownHandler (std::ostringstream& results)
{
LogPrint (eLogInfo, "I2PControl: Shutdown requested");
InsertParam (results, "Shutdown", "");
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
});
}
void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results)
{
i2p::context.SetAcceptsTunnels (false);
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout ();
LogPrint (eLogInfo, "I2PControl: Graceful shutdown requested, ", timeout, " seconds remains");
InsertParam (results, "ShutdownGraceful", "");
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
});
}
void I2PControlService::ReseedHandler (std::ostringstream& results)
{
LogPrint (eLogInfo, "I2PControl: Reseed requested");
InsertParam (results, "Reseed", "");
i2p::data::netdb.Reseed ();
}
// network setting
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
auto it1 = m_NetworkSettingHandlers.find (it->first);
if (it1 != m_NetworkSettingHandlers.end ()) {
(this->*(it1->second))(it->second.data (), results);
} else
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
}
}
// certificate
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
{
FILE *f = NULL;
EVP_PKEY * pkey = EVP_PKEY_new ();
RSA * rsa = RSA_new ();
BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ());
RSA_generate_key_ex (rsa, 4096, e, NULL);
BN_free (e);
if (rsa)
{
EVP_PKEY_assign_RSA (pkey, rsa);
X509 * x509 = X509_new ();
ASN1_INTEGER_set (X509_get_serialNumber (x509), 1);
X509_gmtime_adj (X509_get_notBefore (x509), 0);
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, "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
X509_sign (x509, pkey, EVP_sha1 ()); // sign
// save cert
if ((f = fopen (crt_path, "wb")) != NULL) {
LogPrint (eLogInfo, "I2PControl: saving new cert to ", crt_path);
PEM_write_X509 (f, x509);
fclose (f);
} else {
LogPrint (eLogError, "I2PControl: can't write cert: ", strerror(errno));
}
// save key
if ((f = fopen (key_path, "wb")) != NULL) {
LogPrint (eLogInfo, "I2PControl: saving cert key to ", key_path);
PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL);
fclose (f);
} else {
LogPrint (eLogError, "I2PControl: can't write key: ", strerror(errno));
}
X509_free (x509);
} else {
LogPrint (eLogError, "I2PControl: can't create RSA key for certificate");
}
EVP_PKEY_free (pkey);
}
}
}

120
I2PControl.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef I2P_CONTROL_H__
#define I2P_CONTROL_H__
#include <inttypes.h>
#include <thread>
#include <memory>
#include <array>
#include <string>
#include <sstream>
#include <map>
#include <set>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/property_tree/ptree.hpp>
namespace i2p
{
namespace client
{
const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024;
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
const long I2P_CONTROL_CERTIFICATE_VALIDITY = 365*10; // 10 years
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
class I2PControlService
{
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
public:
I2PControlService (const std::string& address, int port);
~I2PControlService ();
void Start ();
void Stop ();
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
void Handshake (std::shared_ptr<ssl_socket> socket);
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
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 SendResponse (std::shared_ptr<ssl_socket> socket,
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);
void CreateCertificate (const char *crt_path, const char *key_path);
private:
void InsertParam (std::ostringstream& ss, const std::string& name, int value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, double value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const;
// methods
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results);
void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
// I2PControl
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
void PasswordHandler (const std::string& value);
// RouterInfo
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results);
void UptimeHandler (std::ostringstream& results);
void VersionHandler (std::ostringstream& results);
void StatusHandler (std::ostringstream& results);
void NetDbKnownPeersHandler (std::ostringstream& results);
void NetDbActivePeersHandler (std::ostringstream& results);
void NetStatusHandler (std::ostringstream& results);
void TunnelsParticipatingHandler (std::ostringstream& results);
void InboundBandwidth1S (std::ostringstream& results);
void OutboundBandwidth1S (std::ostringstream& results);
void NetTotalReceivedBytes (std::ostringstream& results);
void NetTotalSentBytes (std::ostringstream& results);
// RouterManager
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
void ShutdownHandler (std::ostringstream& results);
void ShutdownGracefulHandler (std::ostringstream& results);
void ReseedHandler (std::ostringstream& results);
// NetworkSetting
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
private:
std::string m_Password;
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ssl::context m_SSLContext;
boost::asio::deadline_timer m_ShutdownTimer;
std::set<std::string> m_Tokens;
std::map<std::string, MethodHandler> m_MethodHandlers;
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers;
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
};
}
}
#endif

View File

@@ -5,6 +5,7 @@
#include "LittleBigEndian.h"
#ifdef NEEDS_LOCAL_ENDIAN
uint16_t htobe16(uint16_t int16)
{
BigEndian<uint16_t> u16(int16);
@@ -40,6 +41,7 @@ uint64_t be64toh(uint64_t big64)
LittleEndian<uint64_t> u64(big64);
return u64.raw_value;
}
#endif
/* it can be used in Windows 8
#include <Winsock2.h>

View File

@@ -1,7 +1,9 @@
#ifndef I2PENDIAN_H__
#define I2PENDIAN_H__
#include <inttypes.h>
#include <string.h>
#if defined(__linux__) || defined(__FreeBSD_kernel__)
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
#include <endian.h>
#elif __FreeBSD__
#include <sys/endian.h>
@@ -25,6 +27,7 @@
#define le64toh(x) OSSwapLittleToHostInt64(x)
#else
#define NEEDS_LOCAL_ENDIAN
#include <cstdint>
uint16_t htobe16(uint16_t int16);
uint32_t htobe32(uint32_t int32);
@@ -34,7 +37,83 @@ uint16_t be16toh(uint16_t big16);
uint32_t be32toh(uint32_t big32);
uint64_t be64toh(uint64_t big64);
// assume LittleEndine
#define htole16
#define htole32
#define htole64
#define le16toh
#define le32toh
#define le64toh
#endif
inline uint16_t buf16toh(const void *buf)
{
uint16_t b16;
memcpy(&b16, buf, sizeof(uint16_t));
return b16;
}
inline uint32_t buf32toh(const void *buf)
{
uint32_t b32;
memcpy(&b32, buf, sizeof(uint32_t));
return b32;
}
inline uint64_t buf64toh(const void *buf)
{
uint64_t b64;
memcpy(&b64, buf, sizeof(uint64_t));
return b64;
}
inline uint16_t bufbe16toh(const void *buf)
{
return be16toh(buf16toh(buf));
}
inline uint32_t bufbe32toh(const void *buf)
{
return be32toh(buf32toh(buf));
}
inline uint64_t bufbe64toh(const void *buf)
{
return be64toh(buf64toh(buf));
}
inline void htobuf16(void *buf, uint16_t b16)
{
memcpy(buf, &b16, sizeof(uint16_t));
}
inline void htobuf32(void *buf, uint32_t b32)
{
memcpy(buf, &b32, sizeof(uint32_t));
}
inline void htobuf64(void *buf, uint64_t b64)
{
memcpy(buf, &b64, sizeof(uint64_t));
}
inline void htobe16buf(void *buf, uint16_t big16)
{
htobuf16(buf, htobe16(big16));
}
inline void htobe32buf(void *buf, uint32_t big32)
{
htobuf32(buf, htobe32(big32));
}
inline void htobe64buf(void *buf, uint64_t big64)
{
htobuf64(buf, htobe64(big64));
}
#endif // I2PENDIAN_H__

211
I2PService.cpp Normal file
View File

@@ -0,0 +1,211 @@
#include "Destination.h"
#include "Identity.h"
#include "ClientContext.h"
#include "I2PService.h"
namespace i2p
{
namespace client
{
static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
m_LocalDestination (localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE))
{
}
I2PService::I2PService (i2p::data::SigningKeyType kt):
m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt))
{
}
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
assert(streamRequestComplete);
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "I2PService: Remote destination ", dest, " not found");
streamRequestComplete (nullptr);
}
}
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()
{
Terminate();
}
void TCPIPPipe::Start()
{
AsyncReceiveUpstream();
AsyncReceiveDownstream();
}
void TCPIPPipe::Terminate()
{
if(Kill()) return;
Done(shared_from_this());
if (m_up) {
if (m_up->is_open()) {
m_up->close();
}
m_up = nullptr;
}
if (m_down) {
if (m_down->is_open()) {
m_down->close();
}
m_down = nullptr;
}
}
void TCPIPPipe::AsyncReceiveUpstream()
{
if (m_up) {
m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: no upstream socket for read");
}
}
void TCPIPPipe::AsyncReceiveDownstream()
{
if (m_down) {
m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: no downstream socket for read");
}
}
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
{
if (m_up) {
LogPrint(eLogDebug, "TCPIPPipe: write upstream ", (int)len);
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleUpstreamWrite,
shared_from_this(),
std::placeholders::_1)
);
} else {
LogPrint(eLogError, "tcpip pipe upstream socket null");
}
}
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
{
if (m_down) {
LogPrint(eLogDebug, "TCPIPPipe: write downstream ", (int)len);
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleDownstreamWrite,
shared_from_this(),
std::placeholders::_1)
);
} else {
LogPrint(eLogError, "tcpip pipe downstream socket null");
}
}
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe downstream got ", (int) bytes_transfered);
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Downstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
UpstreamWrite(m_upstream_buf, bytes_transfered);
}
AsyncReceiveDownstream();
}
}
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Downstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
}
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Upstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
}
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe upstream got ", (int) bytes_transfered);
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Upstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered);
DownstreamWrite(m_upstream_buf, bytes_transfered);
}
AsyncReceiveUpstream();
}
}
void TCPIPAcceptor::Start ()
{
m_Acceptor.listen ();
Accept ();
}
void TCPIPAcceptor::Stop ()
{
m_Acceptor.close();
m_Timer.cancel ();
ClearHandlers();
}
void TCPIPAcceptor::Accept ()
{
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (!ecode)
{
LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted");
auto handler = CreateHandler(socket);
if (handler)
{
AddHandler(handler);
handler->Handle();
}
else
socket->close();
Accept();
}
else
{
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
}
}
}
}

133
I2PService.h Normal file
View File

@@ -0,0 +1,133 @@
#ifndef I2PSERVICE_H__
#define I2PSERVICE_H__
#include <atomic>
#include <mutex>
#include <unordered_set>
#include <memory>
#include <boost/asio.hpp>
#include "Destination.h"
#include "Identity.h"
namespace i2p
{
namespace client
{
class I2PServiceHandler;
class I2PService
{
public:
I2PService (std::shared_ptr<ClientDestination> localDestination = nullptr);
I2PService (i2p::data::SigningKeyType kt);
virtual ~I2PService () { ClearHandlers (); }
inline void AddHandler (std::shared_ptr<I2PServiceHandler> conn)
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.insert(conn);
}
inline void RemoveHandler (std::shared_ptr<I2PServiceHandler> conn)
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.erase(conn);
}
inline void ClearHandlers ()
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.clear();
}
inline std::shared_ptr<ClientDestination> GetLocalDestination () { 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);
inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }
virtual void Start () = 0;
virtual void Stop () = 0;
virtual const char* GetName() { return "Generic I2P Service"; }
private:
std::shared_ptr<ClientDestination> m_LocalDestination;
std::unordered_set<std::shared_ptr<I2PServiceHandler> > m_Handlers;
std::mutex m_HandlersMutex;
};
/*Simple interface for I2PHandlers, allows detection of finalization amongst other things */
class I2PServiceHandler
{
public:
I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { }
virtual ~I2PServiceHandler() { }
//If you override this make sure you call it from the children
virtual void Handle() {}; //Start handling the socket
protected:
// Call when terminating or handing over to avoid race conditions
inline bool Kill () { return m_Dead.exchange(true); }
// Call to know if the handler is dead
inline bool Dead () { return m_Dead; }
// Call when done to clean up (make sure Kill is called first)
inline void Done (std::shared_ptr<I2PServiceHandler> me) { if(m_Service) m_Service->RemoveHandler(me); }
// Call to talk with the owner
inline I2PService * GetOwner() { return m_Service; }
private:
I2PService *m_Service;
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
};
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192;
// bidirectional pipe for 2 tcp/ip sockets
class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> {
public:
TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream);
~TCPIPPipe();
void Start();
protected:
void Terminate();
void AsyncReceiveUpstream();
void AsyncReceiveDownstream();
void HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
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);
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];
std::shared_ptr<boost::asio::ip::tcp::socket> m_up, m_down;
};
/* TODO: support IPv6 too */
//This is a service that listens for connections on the IP network and interacts with I2P
class TCPIPAcceptor: public I2PService
{
public:
TCPIPAcceptor (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
I2PService(localDestination),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
m_Timer (GetService ()) {}
TCPIPAcceptor (const std::string& address, int port, i2p::data::SigningKeyType kt) :
I2PService(kt),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
m_Timer (GetService ()) {}
virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); }
//If you override this make sure you call it from the children
void Start ();
//If you override this make sure you call it from the children
void Stop ();
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);
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
};
}
}
#endif

View File

@@ -1,7 +1,6 @@
#include <boost/bind.hpp>
#include "base64.h"
#include <cassert>
#include "Base.h"
#include "Log.h"
#include "NetDb.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PTunnel.h"
@@ -10,64 +9,93 @@ namespace i2p
{
namespace client
{
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
m_Socket (socket), m_Owner (owner)
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 ()),
m_IsQuiet (true)
{
m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet);
m_Stream->Send (m_Buffer, 0); // connect
StreamReceive ();
Receive ();
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port);
}
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, i2p::stream::Stream * stream,
boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target):
m_Socket (socket), m_Stream (stream), m_Owner (owner)
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
{
}
I2PTunnelConnection::I2PTunnelConnection (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, bool quiet):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (target), m_IsQuiet (quiet)
{
if (m_Socket)
m_Socket->async_connect (target, boost::bind (&I2PTunnelConnection::HandleConnect,
this, boost::asio::placeholders::error));
}
I2PTunnelConnection::~I2PTunnelConnection ()
{
delete m_Socket;
}
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
{
if (m_Stream)
{
if (msg)
m_Stream->Send (msg, len); // connect and send
else
m_Stream->Send (m_Buffer, 0); // connect
}
StreamReceive ();
Receive ();
}
void I2PTunnelConnection::Connect ()
{
if (m_Socket)
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
shared_from_this (), std::placeholders::_1));
}
void I2PTunnelConnection::Terminate ()
{
{
if (Kill()) return;
if (m_Stream)
{
m_Stream->Close ();
i2p::stream::DeleteStream (m_Stream);
m_Stream = nullptr;
m_Stream.reset ();
}
m_Socket->close ();
if (m_Owner)
m_Owner->RemoveConnection (this);
//delete this;
Done(shared_from_this ());
}
void I2PTunnelConnection::Receive ()
{
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
boost::bind(&I2PTunnelConnection::HandleReceived, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint ("I2PTunnel read error: ", ecode.message ());
{
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (m_Stream)
m_Stream->Send (m_Buffer, bytes_transferred);
Receive ();
{
auto s = shared_from_this ();
m_Stream->AsyncSend (m_Buffer, bytes_transferred,
[s](const boost::system::error_code& ecode)
{
if (!ecode)
s->Receive ();
else
s->Terminate ();
});
}
}
}
@@ -75,7 +103,7 @@ namespace client
{
if (ecode)
{
LogPrint ("I2PTunnel write error: ", ecode.message ());
LogPrint (eLogError, "I2PTunnel: write error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -86,199 +114,390 @@ namespace client
void I2PTunnelConnection::StreamReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
I2P_TUNNEL_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, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
I2P_TUNNEL_CONNECTION_MAX_IDLE);
}
else // closed by peer
{
// get remaning data
auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
if (len > 0) // still some data
Write (m_StreamBuffer, len);
else // no more data
Terminate ();
}
}
}
void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint ("I2PTunnel stream read error: ", ecode.message ());
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
{
if (bytes_transferred > 0)
Write (m_StreamBuffer, bytes_transferred); // postpone termination
else
Terminate ();
}
}
else
{
boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
}
Write (m_StreamBuffer, bytes_transferred);
}
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
{
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
}
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
{
if (ecode)
{
LogPrint ("I2PTunnel connect error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
{
if (m_Stream) m_Stream->Close ();
i2p::stream::DeleteStream (m_Stream);
m_Stream = nullptr;
}
LogPrint (eLogError, "I2PTunnel: connect error: ", ecode.message ());
Terminate ();
}
else
{
LogPrint ("I2PTunnel connected");
StreamReceive ();
LogPrint (eLogDebug, "I2PTunnel: connected");
if (m_IsQuiet)
StreamReceive ();
else
{
// send destination first like received from I2P
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
dest += "\n";
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
HandleStreamReceive (boost::system::error_code (), dest.size ());
}
Receive ();
}
}
void I2PTunnel::AddConnection (I2PTunnelConnection * conn)
I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (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 ())
{
m_Connections.insert (conn);
}
void I2PTunnel::RemoveConnection (I2PTunnelConnection * conn)
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
{
m_Connections.erase (conn);
}
void I2PTunnel::ClearConnections ()
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 (line.find ("Host:") != std::string::npos)
m_OutHeader << "Host: " << m_Host << "\r\n";
else
m_OutHeader << line << "\n";
}
}
else
break;
}
// add X-I2P fields
if (m_From)
{
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
}
if (endOfHeader)
{
m_OutHeader << "\r\n"; // end of header
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
}
}
}
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (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& webircpass):
I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()),
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
{
}
void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len)
{
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 ());
}
std::string line;
m_OutPacket.str ("");
m_InPacket.clear ();
m_InPacket.write ((const char *)buf, len);
while (!m_InPacket.eof () && !m_InPacket.fail ())
{
std::getline (m_InPacket, line);
if (line.length () == 0 && m_InPacket.eof ()) {
m_InPacket.str ("");
}
auto pos = line.find ("USER");
if (pos != std::string::npos && pos == 0)
{
pos = line.find (" ");
pos++;
pos = line.find (" ", pos);
pos++;
auto nextpos = line.find (" ", pos);
m_OutPacket << line.substr (0, pos);
m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ());
m_OutPacket << line.substr (nextpos) << '\n';
} else {
m_OutPacket << line << '\n';
}
}
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
}
/* This handler tries to stablish a connection with the desired server and dies if it fails to do so */
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
{
for (auto it: m_Connections)
delete it;
m_Connections.clear ();
}
I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination,
int port, ClientDestination * localDestination):
I2PTunnel (service, localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256)),
m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (service), m_Destination (destination), m_DestinationIdentHash (nullptr),
m_RemoteLeaseSet (nullptr)
public:
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
I2PServiceHandler(parent), m_DestinationIdentHash(destination),
m_DestinationPort (destinationPort), m_Socket(socket) {};
void Handle();
void Terminate();
private:
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
i2p::data::IdentHash m_DestinationIdentHash;
int m_DestinationPort;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
};
void I2PClientTunnelHandler::Handle()
{
GetOwner()->GetLocalDestination ()->CreateStream (
std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1),
m_DestinationIdentHash, m_DestinationPort);
}
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (Kill()) return;
LogPrint (eLogDebug, "I2PTunnel: new connection");
auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect ();
Done(shared_from_this());
}
else
{
LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
Terminate();
}
}
void I2PClientTunnelHandler::Terminate()
{
if (Kill()) return;
if (m_Socket)
{
m_Socket->close();
m_Socket = nullptr;
}
Done(shared_from_this());
}
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort)
{
}
I2PClientTunnel::~I2PClientTunnel ()
{
Stop ();
}
void I2PClientTunnel::Start ()
{
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
if (!m_DestinationIdentHash)
LogPrint ("I2PTunnel unknown destination ", m_Destination);
m_Acceptor.listen ();
Accept ();
TCPIPAcceptor::Start ();
GetIdentHash();
}
void I2PClientTunnel::Stop ()
{
m_Acceptor.close();
m_Timer.cancel ();
ClearConnections ();
TCPIPAcceptor::Stop();
auto *originalIdentHash = m_DestinationIdentHash;
m_DestinationIdentHash = nullptr;
delete originalIdentHash;
}
void I2PClientTunnel::Accept ()
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash ()
{
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
m_Acceptor.async_accept (*newSocket, boost::bind (&I2PClientTunnel::HandleAccept, this,
boost::asio::placeholders::error, newSocket));
}
void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (!ecode)
if (!m_DestinationIdentHash)
{
if (!m_DestinationIdentHash)
{
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
}
if (m_DestinationIdentHash)
{
// try to get a LeaseSet
m_RemoteLeaseSet = GetLocalDestination ()->FindLeaseSet (*m_DestinationIdentHash);
if (m_RemoteLeaseSet && m_RemoteLeaseSet->HasNonExpiredLeases ())
CreateConnection (socket);
else
{
i2p::data::netdb.RequestDestination (*m_DestinationIdentHash, true, GetLocalDestination ()->GetTunnelPool ());
m_Timer.expires_from_now (boost::posix_time::seconds (I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT));
m_Timer.async_wait (boost::bind (&I2PClientTunnel::HandleDestinationRequestTimer,
this, boost::asio::placeholders::error, socket));
}
}
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
else
{
LogPrint ("Remote destination ", m_Destination, " not found");
delete socket;
}
Accept ();
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
}
return m_DestinationIdentHash;
}
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
const i2p::data::IdentHash *identHash = GetIdentHash();
if (identHash)
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, m_DestinationPort, socket);
else
delete socket;
return nullptr;
}
void I2PClientTunnel::HandleDestinationRequestTimer (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_DestinationIdentHash)
{
m_RemoteLeaseSet = GetLocalDestination ()->FindLeaseSet (*m_DestinationIdentHash);
CreateConnection (socket);
return;
}
}
delete socket;
}
void I2PClientTunnel::CreateConnection (boost::asio::ip::tcp::socket * socket)
{
if (m_RemoteLeaseSet) // leaseSet found
{
LogPrint ("New I2PTunnel connection");
auto connection = new I2PTunnelConnection (this, socket, m_RemoteLeaseSet);
AddConnection (connection);
}
else
{
LogPrint ("LeaseSet for I2PTunnel destination not found");
delete socket;
}
}
I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
ClientDestination * localDestination): I2PTunnel (service, localDestination),
m_Endpoint (boost::asio::ip::address::from_string (address), port)
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)
{
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip);
}
void I2PServerTunnel::Start ()
{
Accept ();
m_Endpoint.port (m_Port);
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (m_Address, ec);
if (!ec)
{
m_Endpoint.address (addr);
Accept ();
}
else
{
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ());
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""),
std::bind (&I2PServerTunnel::HandleResolve, this,
std::placeholders::_1, std::placeholders::_2, resolver));
}
}
void I2PServerTunnel::Stop ()
{
ClearConnections ();
ClearHandlers ();
}
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
{
if (!ecode)
{
auto addr = (*it).endpoint ().address ();
LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr);
m_Endpoint.address (addr);
Accept ();
}
else
LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ());
}
void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList)
{
m_AccessList = accessList;
m_IsAccessList = true;
}
void I2PServerTunnel::Accept ()
{
if (m_PortDestination)
m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
auto localDestination = GetLocalDestination ();
if (localDestination)
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
{
if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
}
else
LogPrint ("Local destination not set for server tunnel");
LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel");
}
void I2PServerTunnel::HandleAccept (i2p::stream::Stream * stream)
void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
new I2PTunnelConnection (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
{
if (m_IsAccessList)
{
if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ()))
{
LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
stream->Close ();
return;
}
}
CreateI2PConnection (stream);
}
}
void 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 ();
}
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)
{
}
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (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,
int port, std::shared_ptr<ClientDestination> localDestination,
const std::string& webircpass, int inport, bool gzip):
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
m_WebircPass (webircpass)
{
}
void 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 ();
}
}
}

View File

@@ -4,10 +4,13 @@
#include <inttypes.h>
#include <string>
#include <set>
#include <memory>
#include <sstream>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Destination.h"
#include "Streaming.h"
#include "I2PService.h"
namespace i2p
{
@@ -16,24 +19,32 @@ namespace client
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
// for HTTP tunnels
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
class I2PTunnel;
class I2PTunnelConnection
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
{
public:
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
const i2p::data::LeaseSet * leaseSet);
I2PTunnelConnection (I2PTunnel * owner, i2p::stream::Stream * stream, boost::asio::ip::tcp::socket * socket,
const boost::asio::ip::tcp::endpoint& target);
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
I2PTunnelConnection (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, bool quiet = true); // from I2P
~I2PTunnelConnection ();
private:
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect ();
protected:
void Terminate ();
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
void HandleWrite (const boost::system::error_code& ecode);
void StreamReceive ();
@@ -43,81 +54,155 @@ namespace client
private:
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
boost::asio::ip::tcp::socket * m_Socket;
i2p::stream::Stream * m_Stream;
I2PTunnel * m_Owner;
};
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
std::shared_ptr<i2p::stream::Stream> m_Stream;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsQuiet; // don't send destination
};
class I2PTunnel
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
{
public:
I2PTunnelConnectionHTTP (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);
I2PTunnel (boost::asio::io_service& service, ClientDestination * localDestination):
m_Service (service), m_LocalDestination (localDestination) {};
virtual ~I2PTunnel () { ClearConnections (); };
protected:
void AddConnection (I2PTunnelConnection * conn);
void RemoveConnection (I2PTunnelConnection * conn);
void ClearConnections ();
ClientDestination * GetLocalDestination () { return m_LocalDestination; };
void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; };
void Write (const uint8_t * buf, size_t len);
boost::asio::io_service& GetService () { return m_Service; };
private:
std::string m_Host;
std::stringstream m_InHeader, m_OutHeader;
bool m_HeaderSent;
std::shared_ptr<const i2p::data::IdentityEx> m_From;
};
class I2PTunnelConnectionIRC: public I2PTunnelConnection
{
public:
I2PTunnelConnectionIRC (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& m_WebircPass);
protected:
void Write (const uint8_t * buf, size_t len);
private:
std::shared_ptr<const i2p::data::IdentityEx> m_From;
std::stringstream m_OutPacket, m_InPacket;
bool m_NeedsWebIrc;
std::string m_WebircPass;
};
class I2PClientTunnel: public TCPIPAcceptor
{
protected:
// Implements TCPIPAcceptor
std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
public:
I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort = 0);
~I2PClientTunnel () {}
void Start ();
void Stop ();
const char* GetName() { return m_Name.c_str (); }
private:
boost::asio::io_service& m_Service;
ClientDestination * m_LocalDestination;
std::set<I2PTunnelConnection *> m_Connections;
};
class I2PClientTunnel: public I2PTunnel
{
public:
I2PClientTunnel (boost::asio::io_service& service, const std::string& destination, int port,
ClientDestination * localDestination = nullptr);
~I2PClientTunnel ();
void Start ();
void Stop ();
const i2p::data::IdentHash * GetIdentHash ();
private:
void Accept ();
void HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
void HandleDestinationRequestTimer (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
void CreateConnection (boost::asio::ip::tcp::socket * socket);
private:
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
std::string m_Destination;
std::string m_Name, m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
int m_DestinationPort;
};
class I2PServerTunnel: public I2PTunnel
class I2PServerTunnel: public I2PService
{
public:
I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
ClientDestination * localDestination);
I2PServerTunnel (const std::string& name, const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, int inport = 0, bool gzip = true);
void Start ();
void Stop ();
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
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 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 (i2p::stream::Stream * stream);
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
virtual void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
boost::asio::ip::tcp::endpoint m_Endpoint;
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;
};
}
class I2PServerTunnelHTTP: public I2PServerTunnel
{
public:
I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, const std::string& host,
int inport = 0, bool gzip = true);
private:
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
std::string m_Host;
};
class I2PServerTunnelIRC: public I2PServerTunnel
{
public:
I2PServerTunnelIRC (const std::string& name, const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, const std::string& webircpass,
int inport = 0, bool gzip = true);
private:
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
std::string m_WebircPass;
};
}
}
#endif

View File

@@ -1,13 +1,12 @@
#include <time.h>
#include <stdio.h>
#include <cryptopp/sha.h>
#include <cryptopp/osrng.h>
#include <cryptopp/dsa.h>
#include "base64.h"
#include "CryptoConst.h"
#include "RouterContext.h"
#include "Identity.h"
#include <openssl/sha.h>
#include <openssl/dh.h>
#include <openssl/rand.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Log.h"
#include "Identity.h"
namespace i2p
{
@@ -17,12 +16,16 @@ namespace data
{
// copy public and signing keys together
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
memset (&certificate, 0, sizeof (certificate));
memset (certificate, 0, sizeof (certificate));
return *this;
}
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
{
if ( len < DEFAULT_IDENTITY_SIZE ) {
// buffer too small, don't overflow
return 0;
}
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
return DEFAULT_IDENTITY_SIZE;
}
@@ -30,35 +33,103 @@ namespace data
IdentHash Identity::Hash () const
{
IdentHash hash;
CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE);
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
return hash;
}
IdentityEx::IdentityEx ():
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
}
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type)
{
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
if (type == SIGNING_KEY_TYPE_ECDSA_SHA256_P256)
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
memcpy (m_StandardIdentity.signingKey + 64, signingKey, 64);
m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY;
m_ExtendedLen = 4; // 4 bytes extra
m_StandardIdentity.certificate.length = htobe16 (4);
size_t excessLen = 0;
uint8_t * excessBuf = nullptr;
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break;
}
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
}
m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length
// fill certificate
m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY;
htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen);
// fill extended buffer
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
*(uint16_t *)m_ExtendedBuffer = htobe16 (SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
*(uint16_t *)(m_ExtendedBuffer + 2) = htobe16 (CRYPTO_KEY_TYPE_ELGAMAL);
uint8_t buf[DEFAULT_IDENTITY_SIZE + 4];
ToBuffer (buf, DEFAULT_IDENTITY_SIZE + 4);
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
htobe16buf (m_ExtendedBuffer, type);
htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL);
if (excessLen && excessBuf)
{
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
delete[] excessBuf;
}
// calculate ident hash
uint8_t * buf = new uint8_t[GetFullLen ()];
ToBuffer (buf, GetFullLen ());
SHA256(buf, GetFullLen (), m_IdentHash);
delete[] buf;
}
else // DSA-SHA1
{
memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey));
memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
m_IdentHash = m_StandardIdentity.Hash ();
m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
@@ -67,20 +138,25 @@ namespace data
}
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
FromBuffer (buf, len);
}
IdentityEx::IdentityEx (const IdentityEx& other):
m_Verifier (nullptr), m_ExtendedBuffer (nullptr)
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = other;
}
IdentityEx::IdentityEx (const Identity& standard):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = standard;
}
IdentityEx::~IdentityEx ()
{
delete m_Verifier;
delete[] m_ExtendedBuffer;
}
@@ -99,8 +175,7 @@ namespace data
else
m_ExtendedBuffer = nullptr;
delete m_Verifier;
CreateVerifier ();
m_Verifier = nullptr;
return *this;
}
@@ -114,66 +189,104 @@ namespace data
m_ExtendedBuffer = nullptr;
m_ExtendedLen = 0;
delete m_Verifier;
CreateVerifier ();
m_Verifier = nullptr;
return *this;
}
size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len)
{
if (len < DEFAULT_IDENTITY_SIZE)
{
LogPrint (eLogError, "Identity: buffer length ", len, " is too small");
return 0;
}
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
delete[] m_ExtendedBuffer;
if (m_StandardIdentity.certificate.length)
delete[] m_ExtendedBuffer; m_ExtendedBuffer = nullptr;
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
if (m_ExtendedLen)
{
m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length);
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
{
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
}
else
{
LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
m_ExtendedLen = 0;
return 0;
}
}
else
{
m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
}
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
SHA256(buf, GetFullLen (), m_IdentHash);
delete m_Verifier;
CreateVerifier ();
m_Verifier = nullptr;
return GetFullLen ();
}
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
{
{
const size_t fullLen = GetFullLen();
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
return GetFullLen ();
return fullLen;
}
size_t IdentityEx::FromBase64(const std::string& s)
{
uint8_t buf[512];
auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 512);
const size_t slen = s.length();
uint8_t buf[slen]; // binary data can't exceed base64
const size_t len = Base64ToByteStream (s.c_str(), slen, buf, slen);
return FromBuffer (buf, len);
}
std::string IdentityEx::ToBase64 () const
{
const size_t bufLen = GetFullLen();
const size_t strLen = Base64EncodingBufferSize(bufLen);
uint8_t buf[bufLen];
char str[strLen];
size_t l = ToBuffer (buf, bufLen);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, strLen);
str[l1] = 0;
return std::string (str);
}
size_t IdentityEx::GetSigningPublicKeyLen () const
{
if (m_Verifier)
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->GetPublicKeyLen ();
return 128;
}
size_t IdentityEx::GetSigningPrivateKeyLen () const
{
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->GetPrivateKeyLen ();
return GetSignatureLen ()/2;
}
size_t IdentityEx::GetSignatureLen () const
{
{
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->GetSignatureLen ();
return 40;
return i2p::crypto::DSA_SIGNATURE_LENGTH;
}
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->Verify (buf, len, signature);
return false;
@@ -181,99 +294,237 @@ namespace data
SigningKeyType IdentityEx::GetSigningKeyType () const
{
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return be16toh (*(const uint16_t *)m_ExtendedBuffer); // signing key
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return bufbe16toh (m_ExtendedBuffer); // signing key
return SIGNING_KEY_TYPE_DSA_SHA1;
}
CryptoKeyType IdentityEx::GetCryptoKeyType () const
{
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
return CRYPTO_KEY_TYPE_ELGAMAL;
}
void IdentityEx::CreateVerifier ()
void IdentityEx::CreateVerifier () const
{
auto keyType = GetSigningKeyType ();
switch (keyType)
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey);
m_Verifier.reset (new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + 64);
break;
{
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
m_Verifier.reset (new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding));
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
m_Verifier.reset (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
{
uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto::ECDSAP521Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
{
uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
{
uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
{
uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
m_Verifier.reset (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
break;
}
default:
LogPrint ("Signing key type ", (int)keyType, " is not supported");
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
}
}
void IdentityEx::DropVerifier () const
{
// TODO: potential race condition with Verify
m_Verifier = nullptr;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{
m_Public = Identity (keys);
m_Public = std::make_shared<IdentityEx>(Identity (keys));
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, 20); // 20 - DSA
delete m_Signer;
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_Signer = nullptr;
CreateSigner ();
return *this;
}
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
{
m_Public = other.m_Public;
m_Public = std::make_shared<IdentityEx>(*other.m_Public);
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, 128); // 128
delete m_Signer;
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_Signer = nullptr;
CreateSigner ();
return *this;
}
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
{
size_t ret = m_Public.FromBuffer (buf, len);
m_Public = std::make_shared<IdentityEx>(buf, len);
size_t ret = m_Public->GetFullLen ();
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public.GetSignatureLen ()/2; // 20 for DSA
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
ret += signingPrivateKeySize;
delete m_Signer;
m_Signer = nullptr;
CreateSigner ();
return ret;
}
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
{
size_t ret = m_Public.ToBuffer (buf, len);
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.GetSignatureLen ()/2; // 20 for DSA
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize;
return ret;
}
size_t PrivateKeys::FromBase64(const std::string& s)
{
uint8_t * buf = new uint8_t[s.length ()];
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
size_t ret = FromBuffer (buf, l);
delete[] buf;
return ret;
}
std::string PrivateKeys::ToBase64 () const
{
uint8_t * buf = new uint8_t[GetFullLen ()];
char * str = new char[GetFullLen ()*2];
size_t l = ToBuffer (buf, GetFullLen ());
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
str[l1] = 0;
delete[] buf;
std::string ret(str);
delete[] str;
return ret;
}
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
if (m_Signer)
m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
m_Signer->Sign (buf, len, signature);
}
void PrivateKeys::CreateSigner ()
{
if (m_Public.GetSigningKeyType () == SIGNING_KEY_TYPE_ECDSA_SHA256_P256)
m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey);
else
m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey);
switch (m_Public->GetSigningKeyType ())
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
m_Signer.reset (new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
m_Signer.reset (new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
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));
break;
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
}
}
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type)
{
if (type == SIGNING_KEY_TYPE_ECDSA_SHA256_P256)
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
PrivateKeys keys;
auto& rnd = i2p::context.GetRandomNumberGenerator ();
// signature
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
}
// encryption
uint8_t publicKey[256];
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey);
// signature
uint8_t signingPublicKey[64];
i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
keys.m_Public = IdentityEx (publicKey, signingPublicKey, SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
i2p::crypto::GenerateElGamalKeyPair (keys.m_PrivateKey, publicKey);
// identity
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type);
keys.CreateSigner ();
return keys;
}
@@ -283,12 +534,10 @@ namespace data
Keys CreateRandomKeys ()
{
Keys keys;
auto& rnd = i2p::context.GetRandomNumberGenerator ();
// encryption
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(rnd, keys.privateKey, keys.publicKey);
i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey);
// signing
i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey);
i2p::crypto::CreateDSARandomKeys (keys.signingPrivateKey, keys.signingKey);
return keys;
}
@@ -306,7 +555,7 @@ namespace data
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#endif
IdentHash key;
CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40);
SHA256(buf, 40, key);
return key;
}

View File

@@ -4,69 +4,20 @@
#include <inttypes.h>
#include <string.h>
#include <string>
#include "base64.h"
#include "ElGamal.h"
#include <memory>
#include "Base.h"
#include "Signature.h"
namespace i2p
{
namespace data
{
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; };
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);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
typedef Tag<32> IdentHash;
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
{
return ident.ToBase64 ().substr (0, 4);
}
#pragma pack(1)
struct Keys
{
uint8_t privateKey[256];
@@ -86,11 +37,7 @@ namespace data
{
uint8_t publicKey[256];
uint8_t signingKey[128];
struct
{
uint8_t type;
uint16_t length;
} certificate;
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
Identity () = default;
Identity (const Keys& keys) { *this = keys; };
@@ -98,15 +45,22 @@ namespace data
size_t FromBuffer (const uint8_t * buf, size_t len);
IdentHash Hash () const;
};
#pragma pack()
Keys CreateRandomKeys ();
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
typedef uint16_t SigningKeyType;
typedef uint16_t CryptoKeyType;
class IdentityEx
{
@@ -117,30 +71,36 @@ namespace data
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
IdentityEx (const uint8_t * buf, size_t len);
IdentityEx (const IdentityEx& other);
IdentityEx (const Identity& standard);
~IdentityEx ();
IdentityEx& operator=(const IdentityEx& other);
IdentityEx& operator=(const Identity& standard);
size_t FromBase64(const std::string& s);
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
size_t FromBase64(const std::string& s);
std::string ToBase64 () const;
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; };
const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; };
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
size_t GetSigningPublicKeyLen () const;
size_t GetSigningPrivateKeyLen () const;
size_t GetSignatureLen () const;
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
SigningKeyType GetSigningKeyType () const;
CryptoKeyType GetCryptoKeyType () const;
void DropVerifier () const; // to save memory
private:
void CreateVerifier ();
void CreateVerifier () const;
private:
Identity m_StandardIdentity;
IdentHash m_IdentHash;
i2p::crypto::Verifier * m_Verifier;
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
size_t m_ExtendedLen;
uint8_t * m_ExtendedBuffer;
};
@@ -149,22 +109,25 @@ namespace data
{
public:
PrivateKeys (): m_Signer (nullptr) {};
PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; };
PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; };
PrivateKeys () = default;
PrivateKeys (const PrivateKeys& other) { *this = other; };
PrivateKeys (const Keys& keys) { *this = keys; };
PrivateKeys& operator=(const Keys& keys);
PrivateKeys& operator=(const PrivateKeys& other);
~PrivateKeys () { delete m_Signer; };
~PrivateKeys () = default;
const IdentityEx& GetPublic () const { return m_Public; };
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSignatureLen ()/2; };
size_t GetFullLen () const { return m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); };
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
size_t FromBase64(const std::string& s);
std::string ToBase64 () const;
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
private:
@@ -173,10 +136,10 @@ namespace data
private:
IdentityEx m_Public;
std::shared_ptr<IdentityEx> m_Public;
uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes
i2p::crypto::Signer * m_Signer;
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
std::unique_ptr<i2p::crypto::Signer> m_Signer;
};
// kademlia
@@ -201,23 +164,12 @@ namespace data
{
public:
RoutingDestination (): m_ElGamalEncryption (nullptr) {};
virtual ~RoutingDestination () { delete m_ElGamalEncryption; };
RoutingDestination () {};
virtual ~RoutingDestination () {};
virtual const IdentHash& GetIdentHash () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual bool IsDestination () const = 0; // for garlic
i2p::crypto::ElGamalEncryption * GetElGamalEncryption () const
{
if (!m_ElGamalEncryption)
m_ElGamalEncryption = new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ());
return m_ElGamalEncryption;
}
private:
mutable i2p::crypto::ElGamalEncryption * m_ElGamalEncryption; // use lazy initialization
};
class LocalDestination
@@ -229,8 +181,8 @@ namespace data
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
const IdentityEx& GetIdentity () const { return GetPrivateKeys ().GetPublic (); };
const IdentHash& GetIdentHash () const { return GetIdentity ().GetIdentHash (); };
std::shared_ptr<const IdentityEx> GetIdentity () const { return GetPrivateKeys ().GetPublic (); };
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
GetPrivateKeys ().Sign (buf, len, signature);

354
LICENSE
View File

@@ -1,339 +1,27 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (c) 2013-2015, The PurpleI2P Project
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
All rights reserved.
Preamble
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
2. Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
3. Neither the name of the copyright holder nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior written
permission.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
i2p router for Linux written on C++
Copyright (C) 2013 orignal
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,6 +1,6 @@
#include <string.h>
#include "I2PEndian.h"
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
@@ -12,111 +12,237 @@ namespace i2p
namespace data
{
LeaseSet::LeaseSet (const uint8_t * buf, int len)
LeaseSet::LeaseSet (const uint8_t * buf, size_t len, bool storeLeases):
m_IsValid (true), m_StoreLeases (storeLeases), m_ExpirationTime (0)
{
m_Buffer = new uint8_t[len];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
}
LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool)
LeaseSet::LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool):
m_IsValid (true), m_StoreLeases (true), m_ExpirationTime (0)
{
if (!pool) return;
// header
const i2p::data::LocalDestination& localDestination = pool.GetLocalDestination ();
m_BufferLen = localDestination.GetIdentity ().ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
memcpy (m_Buffer + m_BufferLen, localDestination.GetEncryptionPublicKey (), 256);
auto localDestination = pool->GetLocalDestination ();
if (!localDestination)
{
m_Buffer = nullptr;
m_BufferLen = 0;
m_IsValid = false;
LogPrint (eLogError, "LeaseSet: Destination for local LeaseSet doesn't exist");
return;
}
m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE];
m_BufferLen = localDestination->GetIdentity ()->ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256);
m_BufferLen += 256;
auto signingKeyLen = localDestination.GetIdentity ().GetSigningPublicKeyLen ();
auto signingKeyLen = localDestination->GetIdentity ()->GetSigningPublicKeyLen ();
memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
m_BufferLen += signingKeyLen;
auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum
int numTunnels = pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
if (numTunnels > 16) numTunnels = 16; // 16 tunnels maximum
auto tunnels = pool->GetInboundTunnels (numTunnels);
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
m_BufferLen++;
// leases
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: tunnels)
{
Lease * lease = (Lease *)(m_Buffer + m_BufferLen);
memcpy (lease->tunnelGateway, it->GetNextIdentHash (), 32);
lease->tunnelID = htobe32 (it->GetNextTunnelID ());
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - 60; // 1 minute before expiration
memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32);
m_BufferLen += 32; // gateway id
htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ());
m_BufferLen += 4; // tunnel id
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
lease->endDate = htobe64 (ts);
m_BufferLen += sizeof (Lease);
}
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - it->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + m_BufferLen, ts);
m_BufferLen += 8; // end date
}
// signature
localDestination.Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += localDestination.GetIdentity ().GetSignatureLen ();
LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created");
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += localDestination->GetIdentity ()->GetSignatureLen ();
LogPrint (eLogDebug, "LeaseSet: Local LeaseSet of ", tunnels.size (), " leases created");
ReadFromBuffer ();
}
void LeaseSet::Update (const uint8_t * buf, int len)
void LeaseSet::Update (const uint8_t * buf, size_t len)
{
m_Leases.clear ();
if (len > m_BufferLen)
{
auto oldBuffer = m_Buffer;
m_Buffer = new uint8_t[len];
delete[] oldBuffer;
}
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
ReadFromBuffer (false);
}
void LeaseSet::ReadFromBuffer ()
void LeaseSet::PopulateLeases ()
{
m_StoreLeases = true;
ReadFromBuffer (false);
}
void LeaseSet::ReadFromBuffer (bool readIdentity)
{
size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen);
if (readIdentity || !m_Identity)
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t size = m_Identity->GetFullLen ();
if (size > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", m_BufferLen);
m_IsValid = false;
return;
}
memcpy (m_EncryptionKey, m_Buffer + size, 256);
size += 256; // encryption key
size += m_Identity.GetSigningPublicKeyLen (); // unused signing key
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
uint8_t num = m_Buffer[size];
size++; // num
LogPrint ("LeaseSet num=", (int)num);
LogPrint (eLogDebug, "LeaseSet: read num=", (int)num);
if (!num || num > MAX_NUM_LEASES)
{
LogPrint (eLogError, "LeaseSet: incorrect number of leases", (int)num);
m_IsValid = false;
return;
}
// reset existing leases
if (m_StoreLeases)
for (auto it: m_Leases)
it->isUpdated = false;
else
m_Leases.clear ();
// process leases
m_ExpirationTime = 0;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
const uint8_t * leases = m_Buffer + size;
for (int i = 0; i < num; i++)
{
Lease lease = *(Lease *)leases;
lease.tunnelID = be32toh (lease.tunnelID);
lease.endDate = be64toh (lease.endDate);
m_Leases.push_back (lease);
leases += sizeof (Lease);
// check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway))
{
// if not found request it
LogPrint ("Lease's tunnel gateway not found. Requested");
netdb.RequestDestination (lease.tunnelGateway);
}
Lease lease;
lease.tunnelGateway = leases;
leases += 32; // gateway
lease.tunnelID = bufbe32toh (leases);
leases += 4; // tunnel ID
lease.endDate = bufbe64toh (leases);
leases += 8; // end date
if (ts < lease.endDate + LEASE_ENDDATE_THRESHOLD)
{
if (lease.endDate > m_ExpirationTime)
m_ExpirationTime = lease.endDate;
if (m_StoreLeases)
{
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
if (!ret.second) *(*ret.first) = lease; // update existing
(*ret.first)->isUpdated = true;
// check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway))
{
// if not found request it
LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting");
netdb.RequestDestination (lease.tunnelGateway);
}
}
}
else
LogPrint (eLogWarning, "LeaseSet: Lease is expired already ");
}
if (!m_ExpirationTime)
{
LogPrint (eLogWarning, "LeaseSet: all leases are expired. Dropped");
m_IsValid = false;
return;
}
m_ExpirationTime += LEASE_ENDDATE_THRESHOLD;
// delete old leases
if (m_StoreLeases)
{
for (auto it = m_Leases.begin (); it != m_Leases.end ();)
{
if (!(*it)->isUpdated)
{
(*it)->endDate = 0; // somebody might still hold it
m_Leases.erase (it++);
}
else
it++;
}
}
// verify
if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases))
LogPrint ("LeaseSet verification failed");
if (!m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
{
LogPrint (eLogWarning, "LeaseSet: verification failed");
m_IsValid = false;
}
}
const std::vector<Lease> LeaseSet::GetNonExpiredLeases () const
uint64_t LeaseSet::ExtractTimestamp (const uint8_t * buf, size_t len) const
{
if (!m_Identity) return 0;
size_t size = m_Identity->GetFullLen ();
if (size > len) return 0;
size += 256; // encryption key
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
if (size > len) return 0;
uint8_t num = buf[size];
size++; // num
if (size + num*44 > len) return 0;
uint64_t timestamp= 0 ;
for (int i = 0; i < num; i++)
{
size += 36; // gateway (32) + tunnelId(4)
auto endDate = bufbe64toh (buf + size);
size += 8; // end date
if (!timestamp || endDate < timestamp)
timestamp = endDate;
}
return timestamp;
}
bool LeaseSet::IsNewer (const uint8_t * buf, size_t len) const
{
return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen);
}
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<Lease> leases;
for (auto& it: m_Leases)
if (ts < it.endDate)
std::vector<std::shared_ptr<const Lease> > leases;
for (auto it: m_Leases)
{
auto endDate = it->endDate;
if (withThreshold)
endDate += LEASE_ENDDATE_THRESHOLD;
else
endDate -= LEASE_ENDDATE_THRESHOLD;
if (ts < endDate)
leases.push_back (it);
}
return leases;
}
bool LeaseSet::HasExpiredLeases () const
{
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto& it: m_Leases)
if (ts >= it.endDate) return true;
for (auto it: m_Leases)
if (ts >= it->endDate) return true;
return false;
}
}
bool LeaseSet::HasNonExpiredLeases () const
bool LeaseSet::IsExpired () const
{
if (IsEmpty ()) return true;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto& it: m_Leases)
if (ts < it.endDate) return true;
return false;
return ts > m_ExpirationTime;
}
}
}

View File

@@ -4,6 +4,7 @@
#include <inttypes.h>
#include <string.h>
#include <vector>
#include <memory>
#include "Identity.h"
namespace i2p
@@ -16,60 +17,69 @@ namespace tunnel
namespace data
{
#pragma pack(1)
const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds
struct Lease
{
uint8_t tunnelGateway[32];
IdentHash tunnelGateway;
uint32_t tunnelID;
uint64_t endDate;
bool operator< (const Lease& other) const
{
if (endDate != other.endDate)
return endDate > other.endDate;
else
return tunnelID < other.tunnelID;
}
uint64_t endDate; // 0 means invalid
bool isUpdated; // trasient
};
#pragma pack()
const int MAX_LS_BUFFER_SIZE = 2048;
struct LeaseCmp
{
bool operator() (std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const
{
if (l1->tunnelID != l2->tunnelID)
return l1->tunnelID < l2->tunnelID;
else
return l1->tunnelGateway < l2->tunnelGateway;
};
};
const int MAX_LS_BUFFER_SIZE = 3072;
const uint8_t MAX_NUM_LEASES = 16;
class LeaseSet: public RoutingDestination
{
public:
LeaseSet (const uint8_t * buf, int len);
LeaseSet (const LeaseSet& ) = default;
LeaseSet (const i2p::tunnel::TunnelPool& pool);
LeaseSet& operator=(const LeaseSet& ) = default;
void Update (const uint8_t * buf, int len);
const IdentityEx& GetIdentity () const { return m_Identity; };
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true);
LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool);
~LeaseSet () { delete[] m_Buffer; };
void Update (const uint8_t * buf, size_t len);
bool IsNewer (const uint8_t * buf, size_t len) const;
void PopulateLeases (); // from buffer
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
const uint8_t * GetBuffer () const { return m_Buffer; };
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;
bool HasExpiredLeases () const;
bool IsExpired () const;
bool IsEmpty () const { return m_Leases.empty (); };
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
// implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); };
const std::vector<Lease>& GetLeases () const { return m_Leases; };
const std::vector<Lease> GetNonExpiredLeases () const;
bool HasExpiredLeases () const;
bool HasNonExpiredLeases () const;
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
bool IsDestination () const { return true; };
private:
void ReadFromBuffer ();
void ReadFromBuffer (bool readIdentity = true);
uint64_t ExtractTimestamp (const uint8_t * buf, size_t len) const; // min expiration time
private:
std::vector<Lease> m_Leases;
IdentityEx m_Identity;
bool m_IsValid, m_StoreLeases; // we don't need to store leases for floodfill
std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases;
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t m_EncryptionKey[256];
uint8_t m_Buffer[MAX_LS_BUFFER_SIZE];
uint8_t * m_Buffer;
size_t m_BufferLen;
};
}

74
Log.cpp
View File

@@ -1,5 +1,5 @@
#include "Log.h"
#include <boost/date_time/posix_time/posix_time.hpp>
#include "Log.h"
Log * g_Log = nullptr;
@@ -13,26 +13,74 @@ static const char * g_LogLevelStr[eNumLogLevels] =
void LogMsg::Process()
{
output << boost::posix_time::second_clock::local_time().time_of_day () <<
"/" << g_LogLevelStr[level] << " - ";
auto stream = log ? log->GetLogStream () : nullptr;
auto& output = stream ? *stream : std::cout;
if (log)
output << log->GetTimestamp ();
else
output << boost::posix_time::second_clock::local_time().time_of_day ();
output << "/" << g_LogLevelStr[level] << " - ";
output << s.str();
}
const std::string& Log::GetTimestamp ()
{
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__)
auto ts = std::chrono::monotonic_clock::now ();
#else
auto ts = std::chrono::steady_clock::now ();
#endif
if (ts > m_LastTimestampUpdate + std::chrono::milliseconds (500)) // 0.5 second
{
m_LastTimestampUpdate = ts;
m_Timestamp = boost::posix_time::to_simple_string (boost::posix_time::second_clock::local_time().time_of_day ());
}
return m_Timestamp;
}
void Log::Flush ()
{
if (m_LogFile)
m_LogFile->flush();
if (m_LogStream)
m_LogStream->flush();
}
void Log::SetLogFile (const std::string& fullFilePath)
void Log::SetLogFile (const std::string& fullFilePath, bool truncate)
{
if (m_LogFile) delete m_LogFile;
m_LogFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
if (m_LogFile->is_open ())
LogPrint("Logging to file ", fullFilePath, " enabled.");
else
m_FullFilePath = fullFilePath;
auto mode = std::ofstream::out | std::ofstream::binary;
mode |= truncate ? std::ofstream::trunc : std::ofstream::app;
auto logFile = std::make_shared<std::ofstream> (fullFilePath, mode);
if (logFile->is_open ())
{
delete m_LogFile;
m_LogFile = nullptr;
SetLogStream (logFile);
LogPrint(eLogInfo, "Log: will send messages to ", fullFilePath);
}
}
void Log::ReopenLogFile ()
{
if (m_FullFilePath.length () > 0)
{
SetLogFile (m_FullFilePath, false); // don't truncate
LogPrint(eLogInfo, "Log: file ", m_FullFilePath, " reopen");
}
}
void Log::SetLogLevel (const std::string& level)
{
if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; }
else if (level == "info") { m_MinLevel = eLogInfo; }
else if (level == "debug") { m_MinLevel = eLogDebug; }
else {
LogPrint(eLogError, "Log: Unknown loglevel: ", level);
return;
}
LogPrint(eLogInfo, "Log: min msg level set to ", level);
}
void Log::SetLogStream (std::shared_ptr<std::ostream> logStream)
{
m_LogStream = logStream;
}

84
Log.h
View File

@@ -6,6 +6,8 @@
#include <sstream>
#include <fstream>
#include <functional>
#include <chrono>
#include <memory>
#include "Queue.h"
enum LogLevel
@@ -17,13 +19,14 @@ enum LogLevel
eNumLogLevels
};
class Log;
struct LogMsg
{
std::stringstream s;
std::ostream& output;
Log * log;
LogLevel level;
LogMsg (std::ostream& o = std::cout, LogLevel l = eLogInfo): output (o), level (l) {};
LogMsg (Log * l = nullptr, LogLevel lv = eLogInfo): log (l), level (lv) {};
void Process();
};
@@ -32,11 +35,17 @@ class Log: public i2p::util::MsgQueue<LogMsg>
{
public:
Log (): m_LogFile (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); };
~Log () { delete m_LogFile; };
Log () { SetOnEmpty (std::bind (&Log::Flush, this)); };
~Log () {};
void SetLogFile (const std::string& fullFilePath);
std::ofstream * GetLogFile () const { return m_LogFile; };
void SetLogFile (const std::string& fullFilePath, bool truncate = true);
void ReopenLogFile ();
void SetLogLevel (const std::string& level);
void SetLogStream (std::shared_ptr<std::ostream> logStream);
std::shared_ptr<std::ostream> GetLogStream () const { return m_LogStream; };
const std::string& GetTimestamp ();
LogLevel GetLogLevel () { return m_MinLevel; };
const std::string& GetFullFilePath () const { return m_FullFilePath; };
private:
@@ -44,7 +53,15 @@ class Log: public i2p::util::MsgQueue<LogMsg>
private:
std::ofstream * m_LogFile;
std::string m_FullFilePath; // empty if stream
std::shared_ptr<std::ostream> m_LogStream;
enum LogLevel m_MinLevel;
std::string m_Timestamp;
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__) // gcc 4.6
std::chrono::monotonic_clock::time_point m_LastTimestampUpdate;
#else
std::chrono::steady_clock::time_point m_LastTimestampUpdate;
#endif
};
extern Log * g_Log;
@@ -53,9 +70,21 @@ inline void StartLog (const std::string& fullFilePath)
{
if (!g_Log)
{
g_Log = new Log ();
auto log = new Log ();
if (fullFilePath.length () > 0)
g_Log->SetLogFile (fullFilePath);
log->SetLogFile (fullFilePath);
g_Log = log;
}
}
inline void StartLog (std::shared_ptr<std::ostream> s)
{
if (!g_Log)
{
auto log = new Log ();
if (s)
log->SetLogStream (s);
g_Log = log;
}
}
@@ -63,11 +92,30 @@ inline void StopLog ()
{
if (g_Log)
{
delete g_Log;
auto log = g_Log;
g_Log = nullptr;
log->Stop ();
delete log;
}
}
inline void SetLogLevel (const std::string& level)
{
if (g_Log)
g_Log->SetLogLevel(level);
}
inline void ReopenLogFile ()
{
if (g_Log)
g_Log->ReopenLogFile ();
}
inline bool IsLogToFile ()
{
return g_Log ? !g_Log->GetFullFilePath ().empty () : false;
}
template<typename TValue>
void LogPrint (std::stringstream& s, TValue arg)
{
@@ -84,23 +132,17 @@ void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
template<typename... TArgs>
void LogPrint (LogLevel level, TArgs... args)
{
LogMsg * msg = (g_Log && g_Log->GetLogFile ()) ? new LogMsg (*g_Log->GetLogFile (), level) :
new LogMsg (std::cout, level);
if (g_Log && level > g_Log->GetLogLevel ())
return;
LogMsg * msg = new LogMsg (g_Log, level);
LogPrint (msg->s, args...);
msg->s << std::endl;
if (g_Log)
if (g_Log) {
g_Log->Put (msg);
else
{
} else {
msg->Process ();
delete msg;
}
}
template<typename... TArgs>
void LogPrint (TArgs... args)
{
LogPrint (eLogInfo, args...);
}
#endif

120
Makefile
View File

@@ -1,29 +1,91 @@
UNAME := $(shell uname -s)
ifeq ($(UNAME),Darwin)
include Makefile.osx
else ifeq ($(UNAME), FreeBSD)
include Makefile.bsd
else
include Makefile.linux
endif
all: obj i2p
i2p: $(OBJECTS:obj/%=obj/%)
$(CXX) -o $@ $^ $(LDFLAGS) $(LIBS)
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o
obj/%.o : %.cpp
$(CXX) -o $@ $< -c $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS)
obj:
mkdir -p obj
clean:
rm -fr obj i2p
.PHONY: all
.PHONY: clean
UNAME := $(shell uname -s)
SHLIB := libi2pd.so
ARLIB := libi2pd.a
SHLIB_CLIENT := libi2pdclient.so
ARLIB_CLIENT := libi2pdclient.a
I2PD := i2pd
GREP := fgrep
DEPS := obj/make.dep
include filelist.mk
USE_AESNI := yes
USE_STATIC := no
ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.osx
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.bsd
else ifeq ($(UNAME),Linux)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.linux
else # win32 mingw
DAEMON_SRC += DaemonWin32.cpp Win32/Win32App.cpp
include Makefile.mingw
endif
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
mk_obj_dir:
@mkdir -p obj
@mkdir -p obj/Win32
api: mk_obj_dir $(SHLIB) $(ARLIB)
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build.
## For example, when adding 'hardening flags' to the build
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time.
deps: mk_obj_dir
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
obj/%.o: %.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
# '-' is 'ignore if missing' on first run
-include $(DEPS)
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
endif
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
ar -r $@ $^
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
ar -r $@ $^
clean:
rm -rf obj
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
strip $^
LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
dist:
git archive --format=tar.gz -9 --worktree-attributes \
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
.PHONY: all
.PHONY: clean
.PHONY: deps
.PHONY: dist
.PHONY: api
.PHONY: api_client
.PHONY: mk_obj_dir

View File

@@ -1,7 +1,12 @@
CXX = g++
CXX = clang++
CXXFLAGS = -O2
NEEDED_CXXFLAGS = -std=c++11
include filelist.mk
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build.
## For example, when adding 'hardening flags' to the build
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time.
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread

View File

@@ -1,32 +1,60 @@
CXXFLAGS = -g -Wall
# set defaults instead redefine
CXXFLAGS ?= -g -Wall
INCFLAGS ?=
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build.
## For example, when adding 'hardening flags' to the build
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time.
# detect proper flag for c++11 support by compilers
CXXVER := $(shell $(CXX) -dumpversion)
FGREP = fgrep
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(FGREP) -c "64")
ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # >= 4.10
NEEDED_CXXFLAGS += -std=c++11
ifeq ($(shell expr match $(CXX) 'clang'),5)
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
NEEDED_CXXFLAGS += -std=c++11
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match $(CXX) 'clang'),5)
NEEDED_CXXFLAGS += -std=c++11
NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match ${CXXVER} "5\.[0-9]"),3) # gcc >= 5.0
NEEDED_CXXFLAGS += -std=c++11
else # not supported
$(error Compiler too old)
$(error Compiler too old)
endif
NEEDED_CXXFLAGS += -fPIC
include filelist.mk
INCFLAGS =
LDFLAGS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
ifeq ($(USE_STATIC),yes)
LIBDIR := /usr/lib
LDLIBS = $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_regex.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
USE_AESNI := no
else
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
endif
# UPNP Support (miniupnpc 1.5 or 1.6)
ifeq ($(USE_UPNP),1)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
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
endif
endif
endif

38
Makefile.mingw Normal file
View File

@@ -0,0 +1,38 @@
CXX = g++
WINDRES = windres
CXXFLAGS = -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 = -mwindows -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_regex$(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
DAEMON_RC += Win32/Resource.rc
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
ifeq ($(USE_AESNI),1)
CPU_FLAGS = -maes -DAESNI
else
CPU_FLAGS = -msse
endif
obj/%.o : %.rc
$(WINDRES) -i $< -o $@

View File

@@ -1,17 +1,25 @@
CXX = clang++
CXXFLAGS = -g -Wall -std=c++11 -lstdc++ -I/usr/local/include
include filelist.mk
INCFLAGS = -DCRYPTOPP_DISABLE_ASM
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
CXXFLAGS = -g -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_regex -lboost_program_options -lpthread
ifeq ($(USE_UPNP),1)
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
CXXFLAGS += -maes -DAESNI
# Apple Mac OSX
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
ifeq ($(USE_AESNI),yes)
CXXFLAGS += -maes -DAESNI
endif
# Disabled, since it will be the default make rule. I think its better
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
#install: all
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
# cp -r i2p ${PREFIX}/

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,12 @@
#define NTCP_SESSION_H__
#include <inttypes.h>
#include <list>
#include <map>
#include <memory>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include <cryptopp/adler32.h>
#include "aes.h"
#include "Crypto.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
@@ -17,8 +17,6 @@ namespace i2p
{
namespace transport
{
#pragma pack(1)
struct NTCPPhase1
{
uint8_t pubKey[256];
@@ -31,60 +29,43 @@ namespace transport
struct
{
uint8_t hxy[32];
uint32_t timestamp;
uint8_t timestamp[4];
uint8_t filler[12];
} encrypted;
};
struct NTCPPhase3
{
uint16_t size;
i2p::data::Identity ident;
uint32_t timestamp;
uint8_t padding[15];
uint8_t signature[40];
};
struct NTCPPhase4
{
uint8_t signature[40];
uint8_t padding[8];
};
#pragma pack()
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
const size_t NTCP_BUFFER_SIZE = 1040; // fits one tunnel message (1028)
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
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
class NTCPSession: public TransportSession
class NTCPServer;
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
{
public:
NTCPSession (boost::asio::io_service& service, const i2p::data::RouterInfo * in_RemoteRouter = nullptr);
NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
~NTCPSession ();
void Terminate ();
void Done ();
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
bool IsEstablished () const { return m_IsEstablished; };
void ClientLogin ();
void ServerLogin ();
void SendI2NPMessage (I2NPMessage * msg);
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
protected:
void Terminate ();
virtual void Connected ();
void SendTimeSyncMessage ();
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
private:
void CreateAESKey (uint8_t * pubKey, uint8_t * aesKey);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void Connected ();
void SendTimeSyncMessage ();
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
// client
void SendPhase3 ();
@@ -95,10 +76,12 @@ namespace transport
//server
void SendPhase2 ();
void SendPhase4 (uint32_t tsB);
void SendPhase4 (uint32_t tsA, uint32_t tsB);
void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen);
void HandlePhase3 (uint32_t tsB, size_t paddingLen);
void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
// common
@@ -106,68 +89,87 @@ namespace transport
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
bool DecryptNextBlock (const uint8_t * encrypted);
void Send (i2p::I2NPMessage * msg);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg);
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
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;
bool m_IsEstablished, m_IsTerminated;
i2p::crypto::CBCDecryption m_Decryption;
i2p::crypto::CBCEncryption m_Encryption;
CryptoPP::Adler32 m_Adler;
struct Establisher
{
NTCPPhase1 phase1;
NTCPPhase2 phase2;
NTCPPhase3 phase3;
NTCPPhase4 phase4;
} * m_Establisher;
uint8_t m_ReceiveBuffer[NTCP_BUFFER_SIZE + 16], m_TimeSyncBuffer[16];
i2p::crypto::AESAlignedBuffer<NTCP_BUFFER_SIZE + 16> m_ReceiveBuffer;
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
int m_ReceiveBufferOffset;
i2p::I2NPMessage * m_NextMessage;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
std::shared_ptr<I2NPMessage> m_NextMessage;
size_t m_NextMessageOffset;
i2p::I2NPMessagesHandler m_Handler;
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsSending;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
boost::asio::ip::address m_ConnectedFrom; // for ban
};
class NTCPClient: public NTCPSession
// TODO: move to NTCP.h/.cpp
class NTCPServer
{
public:
NTCPClient (boost::asio::io_service& service, const boost::asio::ip::address& address, int port, const i2p::data::RouterInfo& in_RouterInfo);
NTCPServer ();
~NTCPServer ();
private:
void Start ();
void Stop ();
void Connect ();
void HandleConnect (const boost::system::error_code& ecode);
bool AddNTCPSession (std::shared_ptr<NTCPSession> session);
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);
private:
boost::asio::ip::tcp::endpoint m_Endpoint;
};
void Run ();
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);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
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
class NTCPServerConnection: public NTCPSession
{
public:
NTCPServerConnection (boost::asio::io_service& service):
NTCPSession (service) {};
protected:
virtual void Connected ();
// for HTTP/I2PControl
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
};
}
}

1095
NetDb.cpp

File diff suppressed because it is too large Load Diff

119
NetDb.h
View File

@@ -4,53 +4,31 @@
#include <inttypes.h>
#include <set>
#include <map>
#include <vector>
#include <list>
#include <string>
#include <thread>
#include <mutex>
#include <boost/filesystem.hpp>
#include "Base.h"
#include "FS.h"
#include "Queue.h"
#include "I2NPProtocol.h"
#include "RouterInfo.h"
#include "LeaseSet.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Reseed.h"
#include "NetDbRequests.h"
#include "Family.h"
namespace i2p
{
namespace data
{
class RequestedDestination
{
public:
RequestedDestination (const IdentHash& destination, bool isLeaseSet,
bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr):
m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory),
m_Pool (pool), m_LastRouter (nullptr), m_CreationTime (0) {};
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers ();
const RouterInfo * GetLastRouter () const { return m_LastRouter; };
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
bool IsExploratory () const { return m_IsExploratory; };
bool IsLeaseSet () const { return m_IsLeaseSet; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; };
I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel);
I2NPMessage * CreateRequestMessage (const IdentHash& floodfill);
private:
IdentHash m_Destination;
bool m_IsLeaseSet, m_IsExploratory;
i2p::tunnel::TunnelPool * m_Pool;
std::set<IdentHash> m_ExcludedPeers;
const RouterInfo * m_LastRouter;
uint64_t m_CreationTime;
};
const int NETDB_MIN_ROUTERS = 90;
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60*60; // 1 hour, in seconds
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
class NetDb
{
@@ -62,25 +40,34 @@ namespace data
void Start ();
void Stop ();
void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from);
RouterInfo * FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
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);
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void PublishLeaseSet (const LeaseSet * leaseSet, i2p::tunnel::TunnelPool * pool);
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false,
i2p::tunnel::TunnelPool * pool = nullptr);
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (I2NPMessage * msg);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
void HandleDatabaseLookupMsg (I2NPMessage * msg);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
const RouterInfo * GetRandomRouter () const;
const RouterInfo * GetRandomRouter (const RouterInfo * compatibleWith) const;
const RouterInfo * GetHighBandwidthRandomRouter (const RouterInfo * compatibleWith) const;
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> 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;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (I2NPMessage * msg);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
void Reseed ();
Families& GetFamilies () { return m_Families; };
// for web interface
int GetNumRouters () const { return m_RouterInfos.size (); };
@@ -89,38 +76,38 @@ namespace data
private:
bool CreateNetDb(boost::filesystem::path directory);
void Load (const char * directory);
void SaveUpdated (const char * directory);
void Load ();
bool LoadRouterInfo (const std::string & path);
void SaveUpdated ();
void Run (); // exploratory thread
void Explore (int numDestinations);
void Explore (int numDestinations);
void Publish ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
void ManageLeaseSets ();
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr);
bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found
void DeleteRequestedDestination (RequestedDestination * dest);
void ManageRequests ();
template<typename Filter>
const RouterInfo * GetRandomRouter (Filter filter) const;
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
private:
std::map<IdentHash, LeaseSet *> m_LeaseSets;
std::map<IdentHash, RouterInfo *> m_RouterInfos;
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex;
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
mutable std::mutex m_FloodfillsMutex;
std::vector<RouterInfo *> m_Floodfills;
std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
bool m_IsRunning;
int m_ReseedRetries;
uint64_t m_LastLoad;
std::thread * m_Thread;
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
static const char m_NetDbPath[];
GzipInflator m_Inflator;
Reseeder * m_Reseeder;
Families m_Families;
i2p::fs::HashedStorage m_Storage;
friend class NetDbRequests;
NetDbRequests m_Requests;
};
extern NetDb netdb;

149
NetDbRequests.cpp Normal file
View File

@@ -0,0 +1,149 @@
#include "Log.h"
#include "I2NPProtocol.h"
#include "Transports.h"
#include "NetDb.h"
#include "NetDbRequests.h"
namespace i2p
{
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,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
&m_ExcludedPeers);
m_ExcludedPeers.insert (router->GetIdentHash ());
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
{
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill);
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
void RequestedDestination::ClearExcludedPeers ()
{
m_ExcludedPeers.clear ();
}
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
{
if (m_RequestComplete)
{
m_RequestComplete (r);
m_RequestComplete = nullptr;
}
}
void RequestedDestination::Fail ()
{
if (m_RequestComplete)
{
m_RequestComplete (nullptr);
m_RequestComplete = nullptr;
}
}
void NetDbRequests::Start ()
{
}
void NetDbRequests::Stop ()
{
m_RequestedDestinations.clear ();
}
std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete)
{
// request RouterInfo directly
auto dest = std::make_shared<RequestedDestination> (destination, isExploratory);
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
return nullptr;
}
return dest;
}
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::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
m_RequestedDestinations.erase (it);
}
}
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
{
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
return it->second;
return nullptr;
}
void NetDbRequests::ManageRequests ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{
auto& dest = it->second;
bool done = false;
if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute
{
if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds
{
auto count = dest->GetExcludedPeers ().size ();
if (!dest->IsExploratory () && count < 7)
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool->GetNextInboundTunnel ();
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
if (nextFloodfill && outbound && inbound)
outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (nextFloodfill, inbound));
else
{
done = true;
if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels");
if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels");
if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills");
}
}
else
{
if (!dest->IsExploratory ())
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
done = true;
}
}
}
else // delete obsolete request
done = true;
if (done)
it = m_RequestedDestinations.erase (it);
else
it++;
}
}
}
}

69
NetDbRequests.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef NETDB_REQUESTS_H__
#define NETDB_REQUESTS_H__
#include <memory>
#include <set>
#include <map>
#include "Identity.h"
#include "RouterInfo.h"
namespace i2p
{
namespace data
{
class RequestedDestination
{
public:
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
RequestedDestination (const IdentHash& destination, bool isExploratory = false):
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers ();
bool IsExploratory () const { return m_IsExploratory; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; };
std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill);
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r);
void Fail ();
private:
IdentHash m_Destination;
bool m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime;
RequestComplete m_RequestComplete;
};
class NetDbRequests
{
public:
void Start ();
void Stop ();
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
void ManageRequests ();
private:
std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
};
}
}
#endif

182
Profiling.cpp Normal file
View File

@@ -0,0 +1,182 @@
#include <sys/stat.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "Base.h"
#include "FS.h"
#include "Log.h"
#include "Profiling.h"
namespace i2p
{
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()),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0)
{
}
boost::posix_time::ptime RouterProfile::GetTime () const
{
return boost::posix_time::second_clock::local_time();
}
void RouterProfile::UpdateTime ()
{
m_LastUpdateTime = GetTime ();
}
void RouterProfile::Save ()
{
// fill sections
boost::property_tree::ptree participation;
participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
boost::property_tree::ptree usage;
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
// fill property tree
boost::property_tree::ptree pt;
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
// save to file
std::string ident = m_IdentHash.ToBase64 ();
std::string path = m_ProfilesStorage.Path(ident);
try {
boost::property_tree::write_ini (path, pt);
} catch (std::exception& ex) {
/* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ());
}
}
void RouterProfile::Load ()
{
std::string ident = m_IdentHash.ToBase64 ();
std::string path = m_ProfilesStorage.Path(ident);
boost::property_tree::ptree pt;
if (!i2p::fs::Exists(path)) {
LogPrint(eLogWarning, "Profiling: no profile yet for ", ident);
return;
}
try {
boost::property_tree::read_ini (path, pt);
} catch (std::exception& ex) {
/* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ());
return;
}
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 {
// 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) {
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident);
}
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) {
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident);
}
} else {
*this = RouterProfile (m_IdentHash);
}
} catch (std::exception& ex) {
LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ());
}
}
void RouterProfile::TunnelBuildResponse (uint8_t ret)
{
UpdateTime ();
if (ret > 0)
m_NumTunnelsDeclined++;
else
m_NumTunnelsAgreed++;
}
void RouterProfile::TunnelNonReplied ()
{
m_NumTunnelsNonReplied++;
UpdateTime ();
}
bool RouterProfile::IsLowPartcipationRate () const
{
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
}
bool RouterProfile::IsLowReplyRate () const
{
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
return m_NumTunnelsNonReplied > 10*(total + 1);
}
bool RouterProfile::IsBad ()
{
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{
// reset profile
m_NumTunnelsAgreed = 0;
m_NumTunnelsDeclined = 0;
m_NumTunnelsNonReplied = 0;
isBad = false;
}
if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++;
return isBad;
}
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
{
auto profile = std::make_shared<RouterProfile> (identHash);
profile->Load (); // if possible
return profile;
}
void InitProfilesStorage ()
{
m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
}
void DeleteObsoleteProfiles ()
{
struct stat st;
std::time_t now = std::time(nullptr);
std::vector<std::string> files;
m_ProfilesStorage.Traverse(files);
for (auto path: files) {
if (stat(path.c_str(), &st) != 0) {
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue;
}
if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) {
LogPrint(eLogDebug, "Profiling: removing expired peer profile: ", path);
i2p::fs::Remove(path);
}
}
}
}
}

68
Profiling.h Normal file
View File

@@ -0,0 +1,68 @@
#ifndef PROFILING_H__
#define PROFILING_H__
#include <memory>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "Identity.h"
namespace i2p
{
namespace data
{
// sections
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
const char PEER_PROFILE_SECTION_USAGE[] = "usage";
// params
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime";
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied";
const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
class RouterProfile
{
public:
RouterProfile (const IdentHash& identHash);
RouterProfile& operator= (const RouterProfile& ) = default;
void Save ();
void Load ();
bool IsBad ();
void TunnelBuildResponse (uint8_t ret);
void TunnelNonReplied ();
private:
boost::posix_time::ptime GetTime () const;
void UpdateTime ();
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate () const;
bool IsLowReplyRate () const;
private:
IdentHash m_IdentHash;
boost::posix_time::ptime m_LastUpdateTime;
// participation
uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined;
uint32_t m_NumTunnelsNonReplied;
// usage
uint32_t m_NumTimesTaken;
uint32_t m_NumTimesRejected;
};
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
void InitProfilesStorage ();
void DeleteObsoleteProfiles ();
}
}
#endif

50
Queue.h
View File

@@ -2,6 +2,7 @@
#define QUEUE_H__
#include <queue>
#include <vector>
#include <mutex>
#include <thread>
#include <condition_variable>
@@ -16,17 +17,28 @@ namespace util
{
public:
void Put (Element * e)
void Put (Element e)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_Queue.push (e);
m_NonEmpty.notify_one ();
}
Element * GetNext ()
void Put (const std::vector<Element>& vec)
{
if (!vec.empty ())
{
std::unique_lock<std::mutex> l(m_QueueMutex);
for (auto it: vec)
m_Queue.push (it);
m_NonEmpty.notify_one ();
}
}
Element GetNext ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
Element * el = GetNonThreadSafe ();
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait (l);
@@ -35,10 +47,10 @@ namespace util
return el;
}
Element * GetNextWithTimeout (int usec)
Element GetNextWithTimeout (int usec)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
Element * el = GetNonThreadSafe ();
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec));
@@ -64,16 +76,22 @@ namespace util
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.empty ();
}
int GetSize ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.size ();
}
void WakeUp () { m_NonEmpty.notify_all (); };
Element * Get ()
Element Get ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe ();
}
Element * Peek ()
Element Peek ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe (true);
@@ -81,11 +99,11 @@ namespace util
private:
Element * GetNonThreadSafe (bool peek = false)
Element GetNonThreadSafe (bool peek = false)
{
if (!m_Queue.empty ())
{
Element * el = m_Queue.front ();
auto el = m_Queue.front ();
if (!peek)
m_Queue.pop ();
return el;
@@ -95,13 +113,13 @@ namespace util
private:
std::queue<Element *> m_Queue;
std::queue<Element> m_Queue;
std::mutex m_QueueMutex;
std::condition_variable m_NonEmpty;
};
template<class Msg>
class MsgQueue: public Queue<Msg>
class MsgQueue: public Queue<Msg *>
{
public:
@@ -114,7 +132,7 @@ namespace util
if (m_IsRunning)
{
m_IsRunning = false;
Queue<Msg>::WakeUp ();
Queue<Msg *>::WakeUp ();
m_Thread.join();
}
}
@@ -127,7 +145,7 @@ namespace util
{
while (m_IsRunning)
{
while (Msg * msg = Queue<Msg>::Get ())
while (auto msg = Queue<Msg *>::Get ())
{
msg->Process ();
delete msg;
@@ -135,15 +153,15 @@ namespace util
if (m_OnEmpty != nullptr)
m_OnEmpty ();
if (m_IsRunning)
Queue<Msg>::Wait ();
Queue<Msg *>::Wait ();
}
}
private:
volatile bool m_IsRunning;
std::thread m_Thread;
OnEmpty m_OnEmpty;
std::thread m_Thread;
};
}
}

View File

@@ -1,64 +1,41 @@
i2pd
====
i2p router for Linux written on C++
Independent C++ implementation of I2P router
Requires gcc 4.6 and higher, boost 1.46 and higher, crypto++
on Windows
Requires msvs2013 (require Visual C++ Compiler November 2013 CTP update), boost 1.46 and higher, crypto++
Build Statuses
---------------
- Linux x64 - [![Build Status](https://jenkins.nordcloud.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/)
- Linux ARM - Too be added
- Mac OS X - Too be added
- Microsoft VC13 - Too be added
Testing
License
-------
First, build it.
This project is licensed under the BSD 3-clause license, which can be found in the file
LICENSE in the root of the project source code.
* $ cd i2pd
* $ make
Donations
---------
Next, find out your public ip. (find it for example at http://www.whatismyip.com/)
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
Then, run it with:
Downloads
------------
$ ./i2p --host=YOUR_PUBLIC_IP
Official binary releases could be found at:
http://i2pd.website/releases/
older releases
http://download.i2p.io/purplei2p/i2pd/releases/
The client should now reseed by itself.
Supported OS
------------
To visit an I2P page, you need to find the b32 address of your destination.
After that, go to the webconsole and add it behind the url. (Remove http:// from the address)
* Linux x86/x64 - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
* Mac OS X
* FreeBSD
This should resulting in for example:
http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa.b32.i2p
Options
-------
* --host= - The external IP
* --port= - The port to listen on
* --httpport= - The http port to listen on
* --log= - Enable or disable logging to file. 1 for yes, 0 for no.
* --daemon= - Enable or disable daemon mode. 1 for yes, 0 for no.
* --service= - 1 if uses system folders (/var/run/i2pd.pid, /var/log/i2pd.log, /var/lib/i2pd).
* --unreachable= - 1 if router is declared as unreachable and works through introducers.
* --v6= - 1 if supports communication through ipv6, off by default
* --httpproxyport= - The port to listen on (HTTP Proxy)
* --socksproxyport= - The port to listen on (SOCKS Proxy)
* --ircport= - The local port of IRC tunnel to listen on. 6668 by default
* --ircdest= - I2P destination address of IRC server. For example irc.postman.i2p
* --irckeys= - optional keys file for local destination
* --eepkeys= - File name containing destination keys. For example privKeys.dat
* --eephost= - Address incoming trafic forward to. 127.0.0.1 by default
* --eepport= - Port incoming trafic forward to. 80 by default
* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified
More documentation
------------------
* [Building from source / unix](docs/build_notes_unix.md)
* [Building from source / windows](docs/build_notes_windows.md)
* [Configuring your i2pd](docs/configuration.md)
* [Github wiki](https://github.com/PurpleI2P/i2pd/wiki/)

View File

@@ -1,9 +1,21 @@
#include <iostream>
#include <string.h>
#include <fstream>
#include <sstream>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/bn.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Reseed.h"
#include "FS.h"
#include "Log.h"
#include "Identity.h"
#include "Crypto.h"
#include "NetDb.h"
#include "util.h"
@@ -11,36 +23,21 @@ namespace i2p
{
namespace data
{
static std::vector<std::string> httpReseedHostList = {
"http://193.150.121.66/netDb/",
"http://netdb.i2p2.no/",
"http://reseed.i2p-projekt.de/",
"http://cowpuncher.drollette.com/netdb/",
"http://i2p.mooo.com/netDb/",
"http://reseed.info/",
"http://uk.reseed.i2p2.no/",
"http://us.reseed.i2p2.no/",
"http://jp.reseed.i2p2.no/",
"http://i2p-netdb.innovatio.no/",
"http://ieb9oopo.mooo.com"
};
//TODO: Remember to add custom port support. Not all serves on 443
static std::vector<std::string> httpsReseedHostList = {
"https://193.150.121.66/netDb/",
"https://netdb.i2p2.no/",
"https://reseed.i2p-projekt.de/",
"https://cowpuncher.drollette.com/netdb/",
"https://i2p.mooo.com/netDb/",
"https://reseed.info/",
"https://i2p-netdb.innovatio.no/",
"https://ieb9oopo.mooo.com/",
"https://ssl.webpack.de/ivae2he9.sg4.e-plaza.de/" // Only HTTPS and SU3 (v2) support
};
static std::vector<std::string> httpsReseedHostList =
{
"https://reseed.i2p-projekt.de/", // Only HTTPS
//"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v3) support
"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://www.torontocrypto.org: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
};
//TODO: Implement v2 reseeding. Lightweight zip library is needed.
//TODO: Implement SU3, utils.
Reseeder::Reseeder()
{
}
@@ -49,76 +46,376 @@ namespace data
{
}
bool Reseeder::reseedNow()
int Reseeder::ReseedNowSU3 ()
{
try
auto ind = rand () % httpsReseedHostList.size ();
std::string& reseedHost = httpsReseedHostList[ind];
return ReseedFromSU3 (reseedHost);
}
int Reseeder::ReseedFromSU3 (const std::string& host)
{
std::string url = host + "i2pseeds.su3";
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
std::string su3 = HttpsRequest (url);
if (su3.length () > 0)
{
// Seems like the best place to try to intercept with SSL
/*ssl_server = true;
try {
// SSL
}
catch (std::exception& e)
{
LogPrint("Exception in SSL: ", e.what());
}*/
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
LogPrint("Reseeding from ", reseedHost);
std::string content = i2p::util::http::httpRequest(reseedHost);
if (content == "")
{
LogPrint("Reseed failed");
return false;
}
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase);
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1);
boost::sregex_token_iterator j;
//TODO: Ugly code, try to clean up.
//TODO: Try to reduce N number of variables
std::string name;
std::string routerInfo;
std::string tmpUrl;
std::string filename;
std::string ignoreFileSuffix = ".su3";
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
while (i != j)
{
name = *i++;
if (name.find(ignoreFileSuffix)!=std::string::npos)
continue;
LogPrint("Downloading ", name);
tmpUrl = reseedHost;
tmpUrl.append(name);
routerInfo = i2p::util::http::httpRequest(tmpUrl);
if (routerInfo.size()==0)
continue;
filename = root.string();
#ifndef _WIN32
filename += "/netDb/r";
#else
filename += "\\netDb\\r";
#endif
filename += name.at(11); // first char in id
#ifndef _WIN32
filename.append("/");
#else
filename.append("\\");
#endif
filename.append(name.c_str());
std::ofstream outfile (filename, std::ios::binary);
outfile << routerInfo;
outfile.close();
}
return true;
std::stringstream s(su3);
return ProcessSU3Stream (s);
}
catch (std::exception& ex)
else
{
//TODO: error reporting
return false;
LogPrint (eLogWarning, "Reseed: SU3 download failed");
return 0;
}
}
int Reseeder::ProcessSU3File (const char * filename)
{
std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ())
return ProcessSU3Stream (s);
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];
s.read (magicNumber, 7); // magic number and zero byte 6
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
{
LogPrint (eLogError, "Reseed: Unexpected SU3 magic number");
return 0;
}
s.seekg (1, std::ios::cur); // su3 file format version
SigningKeyType signatureType;
s.read ((char *)&signatureType, 2); // signature type
signatureType = be16toh (signatureType);
uint16_t signatureLength;
s.read ((char *)&signatureLength, 2); // signature length
signatureLength = be16toh (signatureLength);
s.seekg (1, std::ios::cur); // unused
uint8_t versionLength;
s.read ((char *)&versionLength, 1); // version length
s.seekg (1, std::ios::cur); // unused
uint8_t signerIDLength;
s.read ((char *)&signerIDLength, 1); // signer ID length
uint64_t contentLength;
s.read ((char *)&contentLength, 8); // content length
contentLength = be64toh (contentLength);
s.seekg (1, std::ios::cur); // unused
uint8_t fileType;
s.read ((char *)&fileType, 1); // file type
if (fileType != 0x00) // zip file
{
LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType);
return 0;
}
s.seekg (1, std::ios::cur); // unused
uint8_t contentType;
s.read ((char *)&contentType, 1); // content type
if (contentType != 0x03) // reseed data
{
LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType);
return 0;
}
s.seekg (12, std::ios::cur); // unused
s.seekg (versionLength, std::ios::cur); // skip version
char signerID[256];
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)
{
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");
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
}
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
}
else
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
// handle content
int numFiles = 0;
size_t contentPos = s.tellg ();
while (!s.eof ())
{
uint32_t signature;
s.read ((char *)&signature, 4);
signature = le32toh (signature);
if (signature == ZIP_HEADER_SIGNATURE)
{
// next local file
s.seekg (2, std::ios::cur); // version
uint16_t bitFlag;
s.read ((char *)&bitFlag, 2);
bitFlag = le16toh (bitFlag);
uint16_t compressionMethod;
s.read ((char *)&compressionMethod, 2);
compressionMethod = le16toh (compressionMethod);
s.seekg (4, std::ios::cur); // skip fields we don't care about
uint32_t compressedSize, uncompressedSize;
uint32_t crc_32;
s.read ((char *)&crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize);
s.read ((char *)&uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize);
uint16_t fileNameLength, extraFieldLength;
s.read ((char *)&fileNameLength, 2);
fileNameLength = le16toh (fileNameLength);
if ( fileNameLength > 255 ) {
// too big
LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength);
return numFiles;
}
s.read ((char *)&extraFieldLength, 2);
extraFieldLength = le16toh (extraFieldLength);
char localFileName[255];
s.read (localFileName, fileNameLength);
localFileName[fileNameLength] = 0;
s.seekg (extraFieldLength, std::ios::cur);
// take care about data desriptor if presented
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
{
size_t pos = s.tellg ();
if (!FindZipDataDescriptor (s))
{
LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found");
return numFiles;
}
s.read ((char *)&crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
s.read ((char *)&uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize);
// now we know compressed and uncompressed size
s.seekg (pos, std::ios::beg); // back to compressed data
}
LogPrint (eLogDebug, "Reseed: Proccessing file ", localFileName, " ", compressedSize, " bytes");
if (!compressedSize)
{
LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped");
continue;
}
uint8_t * compressed = new uint8_t[compressedSize];
s.read ((char *)compressed, compressedSize);
if (compressionMethod) // we assume Deflate
{
z_stream inflator;
memset (&inflator, 0, sizeof (inflator));
inflateInit2 (&inflator, -MAX_WBITS); // no zlib header
uint8_t * uncompressed = new uint8_t[uncompressedSize];
inflator.next_in = compressed;
inflator.avail_in = compressedSize;
inflator.next_out = uncompressed;
inflator.avail_out = uncompressedSize;
int err;
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0)
{
uncompressedSize -= inflator.avail_out;
if (crc32 (0, uncompressed, uncompressedSize) == crc_32)
{
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
numFiles++;
}
else
LogPrint (eLogError, "Reseed: CRC32 verification failed");
}
else
LogPrint (eLogError, "Reseed: SU3 decompression error ", err);
delete[] uncompressed;
inflateEnd (&inflator);
}
else // no compression
{
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
numFiles++;
}
delete[] compressed;
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
}
else
{
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
LogPrint (eLogWarning, "Reseed: Missing zip central directory header");
break; // no more files
}
size_t end = s.tellg ();
if (end - contentPos >= contentLength)
break; // we are beyond contentLength
}
return numFiles;
}
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 };
bool Reseeder::FindZipDataDescriptor (std::istream& s)
{
size_t nextInd = 0;
while (!s.eof ())
{
uint8_t nextByte;
s.read ((char *)&nextByte, 1);
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd])
{
nextInd++;
if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE))
return true;
}
else
nextInd = 0;
}
return false;
}
void Reseeder::LoadCertificate (const std::string& filename)
{
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
SSL * ssl = SSL_new (ctx);
X509 * cert = SSL_get_certificate (ssl);
// verify
if (cert)
{
// extract issuer name
char name[100];
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
// extract RSA key (we need n only, e = 65537)
RSA * key = X509_get_pubkey (cert)->pkey.rsa;
PublicKey value;
i2p::crypto::bn2buf (key->n, value, 512);
m_SigningKeys[name] = value;
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Reseed: Can't open certificate file ", filename);
SSL_CTX_free (ctx);
}
void Reseeder::LoadCertificates ()
{
std::string certDir = i2p::fs::DataDirPath("certificates", "reseed");
std::vector<std::string> files;
int numCertificates = 0;
if (!i2p::fs::ReadDir(certDir, files)) {
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
return;
}
for (const std::string & file : files) {
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
LogPrint(eLogWarning, "Reseed: ignoring file ", file);
continue;
}
LoadCertificate (file);
numCertificates++;
}
LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded");
}
std::string Reseeder::HttpsRequest (const std::string& address)
{
i2p::util::http::url u(address);
if (u.port_ == 80) u.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);
if (!ecode)
{
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> s(service, ctx);
s.lowest_layer().connect (*it, ecode);
if (!ecode)
{
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 ()));
// 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);
// process response
return i2p::util::http::GetHttpContent (rs);
}
else
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to ", u.host_, ": ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't resolve address ", u.host_, ": ", ecode.message ());
return "";
}
}
}

View File

@@ -1,8 +1,12 @@
#ifndef RESEED_H
#define RESEED_H
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include "Identity.h"
#include "Crypto.h"
namespace i2p
{
@@ -11,13 +15,33 @@ namespace data
class Reseeder
{
typedef Tag<512> PublicKey;
public:
Reseeder();
~Reseeder();
bool reseedNow();
int ReseedNowSU3 ();
void LoadCertificates ();
private:
void LoadCertificate (const std::string& filename);
int ReseedFromSU3 (const std::string& host);
int ProcessSU3File (const char * filename);
int ProcessSU3Stream (std::istream& s);
bool FindZipDataDescriptor (std::istream& s);
std::string HttpsRequest (const std::string& address);
private:
std::map<std::string, PublicKey> m_SigningKeys;
};
}
}
#endif
#endif

View File

@@ -1,24 +1,31 @@
#include <fstream>
#include <cryptopp/dh.h>
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "RouterContext.h"
#include <boost/lexical_cast.hpp>
#include "Config.h"
#include "Crypto.h"
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "NetDb.h"
#include "FS.h"
#include "util.h"
#include "version.h"
#include "Log.h"
#include "Family.h"
#include "RouterContext.h"
namespace i2p
{
RouterContext context;
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_IsUnreachable (false), m_AcceptsTunnels (true)
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_StartupTime (0), m_Status (eRouterStatusOK )
{
}
void RouterContext::Init ()
{
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
if (!Load ())
CreateNewRouter ();
UpdateRouterInfo ();
@@ -26,7 +33,11 @@ namespace i2p
void RouterContext::CreateNewRouter ()
{
m_Keys = i2p::data::CreateRandomKeys ();
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
#else
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
#endif
SaveKeys ();
NewRouterInfo ();
}
@@ -34,29 +45,50 @@ namespace i2p
void RouterContext::NewRouterInfo ()
{
i2p::data::RouterInfo routerInfo;
routerInfo.SetRouterIdentity (GetIdentity ().GetStandardIdentity ());
int port = i2p::util::config::GetArg("-port", 0);
routerInfo.SetRouterIdentity (GetIdentity ());
uint16_t port; i2p::config::GetOption("port", port);
if (!port)
port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range
routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), 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);
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
routerInfo.SetProperty ("coreVersion", I2P_VERSION);
routerInfo.SetProperty ("netId", "2");
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.SetProperty ("stat_uptime", "90m");
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
}
void RouterContext::UpdateRouterInfo ()
{
m_RouterInfo.CreateBuffer (m_Keys);
m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO));
m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO));
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
}
void RouterContext::SetStatus (RouterStatus status)
{
if (status != m_Status)
{
m_Status = status;
switch (m_Status)
{
case eRouterStatusOK:
SetReachable ();
break;
case eRouterStatusFirewalled:
SetUnreachable ();
break;
default:
;
}
}
}
void RouterContext::UpdatePort (int port)
{
bool updated = false;
@@ -88,16 +120,11 @@ namespace i2p
UpdateRouterInfo ();
}
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag)
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
{
bool ret = false;
auto address = routerInfo.GetSSUAddress ();
if (address)
{
ret = m_RouterInfo.AddIntroducer (address, tag);
if (ret)
UpdateRouterInfo ();
}
bool ret = m_RouterInfo.AddIntroducer (introducer);
if (ret)
UpdateRouterInfo ();
return ret;
}
@@ -107,9 +134,72 @@ namespace i2p
UpdateRouterInfo ();
}
void RouterContext::SetFloodfill (bool floodfill)
{
m_IsFloodfill = floodfill;
if (floodfill)
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
else
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
// we don't publish number of routers and leaseset for non-floodfill
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS);
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS);
}
UpdateRouterInfo ();
}
void RouterContext::SetFamily (const std::string& family)
{
std::string signature;
if (family.length () > 0)
signature = i2p::data::CreateFamilySignature (family, GetIdentHash ());
if (signature.length () > 0)
{
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY, family);
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG, signature);
}
else
{
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG);
}
}
void RouterContext::SetHighBandwidth ()
{
if (!m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
{
m_RouterInfo.SetCaps ((m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth) & ~i2p::data::RouterInfo::eExtraBandwidth);
UpdateRouterInfo ();
}
}
void RouterContext::SetLowBandwidth ()
{
if (m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth & ~i2p::data::RouterInfo::eExtraBandwidth);
UpdateRouterInfo ();
}
}
void RouterContext::SetExtraBandwidth ()
{
if (!m_RouterInfo.IsExtraBandwidth () || !m_RouterInfo.IsHighBandwidth ())
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eExtraBandwidth | i2p::data::RouterInfo::eHighBandwidth);
UpdateRouterInfo ();
}
}
bool RouterContext::IsUnreachable () const
{
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
}
void RouterContext::SetUnreachable ()
{
m_IsUnreachable = true;
// set caps
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
// remove NTCP address
@@ -125,11 +215,41 @@ namespace i2p
// delete previous introducers
for (auto& addr : addresses)
addr.introducers.clear ();
// update
UpdateRouterInfo ();
}
void RouterContext::SetReachable ()
{
// update caps
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eUnreachable;
caps |= i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eSSUIntroducer;
if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps);
// insert NTCP back
auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++)
{
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU)
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port);
break;
}
}
// delete previous introducers
for (auto& addr : addresses)
addr.introducers.clear ();
// update
UpdateRouterInfo ();
}
void RouterContext::SetSupportsV6 (bool supportsV6)
{
if (supportsV6)
@@ -162,44 +282,106 @@ namespace i2p
{
// create new address
m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port);
m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), 1472); // TODO
auto mtu = i2p::util::net::GetMTU (host);
if (mtu)
{
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
if (mtu > 1472) { // TODO: magic constant
mtu = 1472;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
}
}
m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO
updated = true;
}
if (updated)
UpdateRouterInfo ();
}
void RouterContext::UpdateStats ()
{
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 ()));
UpdateRouterInfo ();
}
}
bool RouterContext::Load ()
{
std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in);
std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary);
if (!fk.is_open ()) return false;
i2p::data::Keys keys;
fk.read ((char *)&keys, sizeof (keys));
m_Keys = keys;
fk.seekg (0, std::ios::end);
size_t len = fk.tellg();
fk.seekg (0, std::ios::beg);
i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO
if (len == sizeof (i2p::data::Keys)) // old keys file format
{
i2p::data::Keys keys;
fk.read ((char *)&keys, sizeof (keys));
m_Keys = keys;
}
else // new keys file format
{
uint8_t * buf = new uint8_t[len];
fk.read ((char *)buf, len);
m_Keys.FromBuffer (buf, len);
delete[] buf;
}
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); // TODO
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
// Migration to 0.9.24. TODO: remove later
m_RouterInfo.DeleteProperty ("coreVersion");
m_RouterInfo.DeleteProperty ("stat_uptime");
if (IsUnreachable ())
SetReachable (); // we assume reachable until we discover firewall through peer tests
return true;
}
void RouterContext::SaveKeys ()
{
std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out);
i2p::data::Keys keys;
memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey));
memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey));
auto& ident = GetIdentity ().GetStandardIdentity ();
memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey));
memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey));
fk.write ((char *)&keys, sizeof (keys));
// save in the same format as .dat files
std::ofstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ofstream::binary | std::ofstream::out);
size_t len = m_Keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
m_Keys.ToBuffer (buf, len);
fk.write ((char *)buf, len);
delete[] buf;
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
std::shared_ptr<i2p::tunnel::TunnelPool> RouterContext::GetTunnelPool () const
{
return i2p::tunnel::tunnels.GetExploratoryPool ();
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
}
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
uint32_t RouterContext::GetUptime () const
{
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
}
}

View File

@@ -3,9 +3,9 @@
#include <inttypes.h>
#include <string>
#include <memory>
#include <mutex>
#include <boost/asio.hpp>
#include <cryptopp/dsa.h>
#include <cryptopp/osrng.h>
#include "Identity.h"
#include "RouterInfo.h"
#include "Garlic.h"
@@ -15,7 +15,14 @@ namespace i2p
const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys";
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
enum RouterStatus
{
eRouterStatusOK = 0,
eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2
};
class RouterContext: public i2p::garlic::GarlicDestination
{
public:
@@ -24,29 +31,57 @@ namespace i2p
void Init ();
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; };
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
{
return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo,
[](const i2p::data::RouterInfo *) {});
}
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
{
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
[](i2p::garlic::GarlicDestination *) {});
}
uint32_t GetUptime () const;
uint32_t GetStartupTime () const { return m_StartupTime; };
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status);
void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag);
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const { return m_IsUnreachable; };
void SetUnreachable ();
bool IsUnreachable () const;
void SetUnreachable ();
void SetReachable ();
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
void SetFamily (const std::string& family);
void SetHighBandwidth ();
void SetLowBandwidth ();
void SetExtraBandwidth ();
bool AcceptsTunnels () const { return m_AcceptsTunnels; };
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
void SetSupportsV6 (bool supportsV6);
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
void UpdateStats ();
// implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); };
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ().GetStandardIdentity ().publicKey; };
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetStandardIdentity ().publicKey; };
void SetLeaseSetUpdated () {};
// implements GarlicDestination
const i2p::data::LeaseSet * GetLeaseSet () { return nullptr; };
void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
private:
@@ -60,9 +95,11 @@ namespace i2p
i2p::data::RouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys;
CryptoPP::AutoSeededRandomPool m_Rnd;
uint64_t m_LastUpdateTime;
bool m_IsUnreachable, m_AcceptsTunnels;
bool m_AcceptsTunnels, m_IsFloodfill;
uint64_t m_StartupTime; // in seconds since epoch
RouterStatus m_Status;
std::mutex m_GarlicMutex;
};
extern RouterContext context;

View File

@@ -3,15 +3,13 @@
#include "I2PEndian.h"
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <cryptopp/sha.h>
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "base64.h"
#include "version.h"
#include "Crypto.h"
#include "Base.h"
#include "Timestamp.h"
#include "Log.h"
#include "NetDb.h"
#include "RouterInfo.h"
#include "RouterContext.h"
namespace i2p
{
@@ -36,29 +34,44 @@ namespace data
RouterInfo::~RouterInfo ()
{
delete m_Buffer;
delete[] m_Buffer;
}
void RouterInfo::Update (const uint8_t * buf, int len)
{
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
m_IsUpdated = true;
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
m_Properties.clear ();
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer (true);
// don't delete buffer until save to file
// verify signature since we have indentity already
int l = len - m_RouterIdentity->GetSignatureLen ();
if (m_RouterIdentity->Verify (buf, l, buf + l))
{
// clean up
m_IsUpdated = true;
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
m_Properties.clear ();
// copy buffer
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
// skip identity
size_t identityLen = m_RouterIdentity->GetFullLen ();
// read new RI
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
// don't delete buffer until saved to the file
}
else
{
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
}
}
void RouterInfo::SetRouterIdentity (const Identity& identity)
void RouterInfo::SetRouterIdentity (std::shared_ptr<const IdentityEx> identity)
{
m_RouterIdentity = identity;
m_IdentHash = m_RouterIdentity.Hash ();
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
}
@@ -69,9 +82,9 @@ namespace data
{
s.seekg (0,std::ios::end);
m_BufferLen = s.tellg ();
if (m_BufferLen < 40)
if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE)
{
LogPrint(eLogError, "File", m_FullPath, " is malformed");
LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed");
return false;
}
s.seekg(0, std::ios::beg);
@@ -81,7 +94,7 @@ namespace data
}
else
{
LogPrint (eLogError, "Can't open file ", m_FullPath);
LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath);
return false;
}
return true;
@@ -95,39 +108,46 @@ namespace data
void RouterInfo::ReadFromBuffer (bool verifySignature)
{
std::stringstream str (std::string ((char *)m_Buffer, m_BufferLen));
m_RouterIdentity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t identityLen = m_RouterIdentity->GetFullLen ();
if (identityLen >= m_BufferLen)
{
LogPrint (eLogError, "RouterInfo: identity length ", identityLen, " exceeds buffer size ", m_BufferLen);
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
CryptoPP::DSA::PublicKey pubKey;
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_RouterIdentity.signingKey, 128));
CryptoPP::DSA::Verifier verifier (pubKey);
int l = m_BufferLen - 40;
if (!verifier.VerifyMessage ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l, 40))
int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
{
LogPrint (eLogError, "signature verification failed");
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
}
m_RouterIdentity->DropVerifier ();
}
}
void RouterInfo::ReadFromStream (std::istream& s)
{
s.read ((char *)&m_RouterIdentity, DEFAULT_IDENTITY_SIZE);
if (m_RouterIdentity.certificate.type != CERTIFICATE_TYPE_NULL)
{
LogPrint (eLogError, "Certificate type ", m_RouterIdentity.certificate.type, " is not supported");
SetUnreachable (true);
return;
}
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses));
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
for (int i = 0; i < numAddresses; i++)
{
uint8_t supportedTransports = 0;
bool isValidAddress = true;
Address address;
s.read ((char *)&address.cost, sizeof (address.cost));
@@ -143,7 +163,7 @@ namespace data
address.port = 0;
address.mtu = 0;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size));
s.read ((char *)&size, sizeof (size)); if (!s) return;
size = be16toh (size);
while (r < size)
{
@@ -158,17 +178,24 @@ namespace data
address.host = boost::asio::ip::address::from_string (value, ecode);
if (ecode)
{
// TODO: we should try to resolve address here
LogPrint (eLogWarning, "Unexpected address ", value);
isValidAddress = false;
if (address.transportStyle == eTransportNTCP)
{
supportedTransports |= eNTCPV4; // TODO:
address.addressString = value;
}
else
{
supportedTransports |= eSSUV4; // TODO:
address.addressString = value;
}
}
else
{
// add supported protocol
if (address.host.is_v4 ())
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
}
}
else if (!strcmp (key, "port"))
@@ -201,17 +228,21 @@ namespace data
else if (!strcmp (key, "ikey"))
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
}
if (!s) return;
}
if (isValidAddress)
{
m_Addresses.push_back(address);
m_SupportedTransports |= supportedTransports;
}
}
// read peers
uint8_t numPeers;
s.read ((char *)&numPeers, sizeof (numPeers));
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
// read properties
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size));
s.read ((char *)&size, sizeof (size)); if (!s) return;
size = be16toh (size);
while (r < size)
{
@@ -231,9 +262,29 @@ namespace data
// extract caps
if (!strcmp (key, "caps"))
ExtractCaps (value);
// check netId
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != I2PD_NET_ID)
{
LogPrint (eLogError, "Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
m_IsUnreachable = true;
}
// family
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
{
m_Family = value;
boost::to_lower (m_Family);
}
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
{
if (!netdb.GetFamilies ().VerifyFamily (m_Family, GetIdentHash (), value))
{
LogPrint (eLogWarning, "RouterInfo: family signature verification failed");
m_Family.clear ();
}
}
if (!s) return;
}
CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity));
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
SetUnreachable (true);
@@ -254,6 +305,10 @@ namespace data
case CAPS_FLAG_HIGH_BANDWIDTH3:
m_Caps |= Caps::eHighBandwidth;
break;
case CAPS_FLAG_EXTRA_BANDWIDTH1:
case CAPS_FLAG_EXTRA_BANDWIDTH2:
m_Caps |= Caps::eExtraBandwidth;
break;
case CAPS_FLAG_HIDDEN:
m_Caps |= Caps::eHidden;
break;
@@ -278,8 +333,17 @@ namespace data
void RouterInfo::UpdateCapsProperty ()
{
std::string caps;
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH1 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
if (m_Caps & eFloodfill) caps += CAPS_FLAG_FLOODFILL; // floodfill
if (m_Caps & eFloodfill)
{
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
caps += CAPS_FLAG_FLOODFILL; // floodfill
}
else
{
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1;
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
}
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
@@ -411,12 +475,20 @@ namespace data
s.write (properties.str ().c_str (), properties.str ().size ());
}
bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const
{
if (!m_RouterIdentity) return false;
size_t size = m_RouterIdentity->GetFullLen ();
if (size + 8 > len) return false;
return bufbe64toh (buf + size) > m_Timestamp;
}
const uint8_t * RouterInfo::LoadBuffer ()
{
if (!m_Buffer)
{
if (LoadFile ())
LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file");
LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
}
return m_Buffer;
}
@@ -426,7 +498,7 @@ namespace data
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
std::stringstream s;
uint8_t ident[1024];
auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024);
auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024);
s.write ((char *)ident, identLen);
WriteToStream (s);
m_BufferLen = s.str ().size ();
@@ -435,7 +507,7 @@ namespace data
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
// signature
privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
m_BufferLen += privateKeys.GetPublic ().GetSignatureLen ();
m_BufferLen += privateKeys.GetPublic ()->GetSignatureLen ();
}
void RouterInfo::SaveToFile (const std::string& fullPath)
@@ -444,10 +516,13 @@ namespace data
if (m_Buffer)
{
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
f.write ((char *)m_Buffer, m_BufferLen);
}
if (f.is_open ())
f.write ((char *)m_Buffer, m_BufferLen);
else
LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath);
}
else
LogPrint (eLogError, "Can't save to file");
LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL");
}
size_t RouterInfo::ReadString (char * str, std::istream& s)
@@ -475,6 +550,8 @@ namespace data
addr.cost = 2;
addr.date = 0;
addr.mtu = 0;
for (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;
}
@@ -489,26 +566,23 @@ namespace data
addr.date = 0;
addr.mtu = mtu;
memcpy (addr.key, key, 32);
for (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 : eSSUV4;
m_SupportedTransports |= addr.host.is_v6 () ? eSSUV6 : eSSUV4;
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
}
bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag)
bool RouterInfo::AddIntroducer (const Introducer& introducer)
{
for (auto& addr : m_Addresses)
{
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
{
for (auto intro: addr.introducers)
if (intro.iTag == tag) return false; // already presented
Introducer x;
x.iHost = address->host;
x.iPort = address->port;
x.iTag = tag;
memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32>
addr.introducers.push_back (x);
if (intro.iTag == introducer.iTag) return false; // already presented
addr.introducers.push_back (introducer);
return true;
}
}
@@ -545,23 +619,15 @@ namespace data
ExtractCaps (caps);
}
void RouterInfo::SetProperty (const char * key, const char * value)
void RouterInfo::SetProperty (const std::string& key, const std::string& value)
{
m_Properties[key] = value;
}
const char * RouterInfo::GetProperty (const char * key) const
void RouterInfo::DeleteProperty (const std::string& key)
{
auto it = m_Properties.find (key);
if (it != m_Properties.end ())
return it->second.c_str ();
return 0;
}
bool RouterInfo::IsFloodfill () const
{
return m_Caps & Caps::eFloodfill;
}
m_Properties.erase (key);
}
bool RouterInfo::IsNTCP (bool v4only) const
{
@@ -652,5 +718,12 @@ namespace data
}
return nullptr;
}
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
{
if (!m_Profile)
m_Profile = GetRouterProfile (GetIdentHash ());
return m_Profile;
}
}
}

View File

@@ -8,11 +8,18 @@
#include <iostream>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Profiling.h"
namespace i2p
{
namespace data
{
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
const char ROUTER_INFO_PROPERTY_NETID[] = "netId";
const char ROUTER_INFO_PROPERTY_FAMILY[] = "family";
const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig";
const char CAPS_FLAG_FLOODFILL = 'f';
const char CAPS_FLAG_HIDDEN = 'H';
const char CAPS_FLAG_REACHABLE = 'R';
@@ -22,7 +29,9 @@ namespace data
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M';
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N';
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O';
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P';
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X';
const char CAPS_FLAG_SSU_TESTING = 'B';
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
@@ -43,11 +52,12 @@ namespace data
{
eFloodfill = 0x01,
eHighBandwidth = 0x02,
eReachable = 0x04,
eSSUTesting = 0x08,
eSSUIntroducer = 0x10,
eHidden = 0x20,
eUnreachable = 0x40
eExtraBandwidth = 0x04,
eReachable = 0x08,
eSSUTesting = 0x10,
eSSUIntroducer = 0x20,
eHidden = 0x40,
eUnreachable = 0x80
};
enum TransportStyle
@@ -57,11 +67,12 @@ namespace data
eTransportSSU
};
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer
{
boost::asio::ip::address iHost;
int iPort;
Tag<32> iKey;
IntroKey iKey;
uint32_t iTag;
};
@@ -69,11 +80,12 @@ namespace data
{
TransportStyle transportStyle;
boost::asio::ip::address host;
std::string addressString;
int port, mtu;
uint64_t date;
uint8_t cost;
// SSU only
Tag<32> key; // intro key for SSU
IntroKey key; // intro key for SSU
std::vector<Introducer> introducers;
bool IsCompatible (const boost::asio::ip::address& other) const
@@ -81,6 +93,16 @@ namespace data
return (host.is_v4 () && other.is_v4 ()) ||
(host.is_v6 () && other.is_v6 ());
}
bool operator==(const Address& other) const
{
return transportStyle == other.transportStyle && host == other.host && port == other.port;
}
bool operator!=(const Address& other) const
{
return !(*this == other);
}
};
RouterInfo (const std::string& fullPath);
@@ -90,10 +112,9 @@ namespace data
RouterInfo (const uint8_t * buf, int len);
~RouterInfo ();
const Identity& GetRouterIdentity () const { return m_RouterIdentity; };
void SetRouterIdentity (const Identity& identity);
std::string GetIdentHashBase64 () const { return m_IdentHash.ToBase64 (); };
std::string GetIdentHashAbbreviation () const { return m_IdentHash.ToBase64 ().substr (0, 4); };
std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; };
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<Address>& GetAddresses () { return m_Addresses; };
const Address * GetNTCPAddress (bool v4only = true) const;
@@ -102,11 +123,13 @@ namespace data
void AddNTCPAddress (const char * host, int port);
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
bool AddIntroducer (const Address * address, uint32_t tag);
bool AddIntroducer (const Introducer& introducer);
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetProperty (const char * key, const char * value);
const char * GetProperty (const char * key) const;
bool IsFloodfill () const;
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
void DeleteProperty (const std::string& key); // called from RouterContext only
void ClearProperties () { m_Properties.clear (); };
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsNTCP (bool v4only = true) const;
bool IsSSU (bool v4only = true) const;
bool IsV6 () const;
@@ -117,7 +140,9 @@ namespace data
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps);
void SetCaps (const char * caps);
@@ -134,12 +159,16 @@ namespace data
void SetUpdated (bool updated) { m_IsUpdated = updated; };
void SaveToFile (const std::string& fullPath);
void Update (const uint8_t * buf, int len);
void DeleteBuffer () { delete m_Buffer; m_Buffer = nullptr; };
std::shared_ptr<RouterProfile> GetProfile () const;
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
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;
// implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_IdentHash; };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.publicKey; };
const IdentHash& GetIdentHash () const { return m_RouterIdentity->GetIdentHash (); };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity->GetStandardIdentity ().publicKey; };
bool IsDestination () const { return false; };
@@ -158,16 +187,16 @@ namespace data
private:
std::string m_FullPath;
Identity m_RouterIdentity;
IdentHash m_IdentHash;
std::string m_FullPath, m_Family;
std::shared_ptr<const IdentityEx> m_RouterIdentity;
uint8_t * m_Buffer;
int m_BufferLen;
size_t m_BufferLen;
uint64_t m_Timestamp;
std::vector<Address> m_Addresses;
std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_Caps;
mutable std::shared_ptr<RouterProfile> m_Profile;
};
}
}

596
SAM.cpp
View File

@@ -3,11 +3,10 @@
#ifdef _MSC_VER
#include <stdlib.h>
#endif
#include <boost/bind.hpp>
#include "base64.h"
#include <boost/lexical_cast.hpp>
#include "Base.h"
#include "Identity.h"
#include "Log.h"
#include "NetDb.h"
#include "Destination.h"
#include "ClientContext.h"
#include "SAM.h"
@@ -18,29 +17,29 @@ namespace client
{
SAMSocket::SAMSocket (SAMBridge& owner):
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()),
m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_Stream (nullptr),
m_Session (nullptr)
m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
m_Stream (nullptr), m_Session (nullptr)
{
}
SAMSocket::~SAMSocket ()
{
if (m_Stream)
{
m_Stream->Close ();
i2p::stream::DeleteStream (m_Stream);
m_Stream = nullptr;
}
Terminate ();
}
void SAMSocket::Terminate ()
void SAMSocket::CloseStream ()
{
if (m_Stream)
{
{
m_Stream->Close ();
i2p::stream::DeleteStream (m_Stream);
m_Stream = nullptr;
}
m_Stream.reset ();
}
}
void SAMSocket::Terminate ()
{
CloseStream ();
switch (m_SocketType)
{
case eSAMSocketTypeSession:
@@ -49,14 +48,14 @@ namespace client
case eSAMSocketTypeStream:
{
if (m_Session)
m_Session->sockets.remove (this);
m_Session->sockets.remove (shared_from_this ());
break;
}
case eSAMSocketTypeAcceptor:
{
if (m_Session)
{
m_Session->sockets.remove (this);
m_Session->sockets.remove (shared_from_this ());
m_Session->localDestination->StopAcceptingStreams ();
}
break;
@@ -64,39 +63,71 @@ namespace client
default:
;
}
m_SocketType = eSAMSocketTypeTerminated;
m_Socket.close ();
// delete this;
}
void SAMSocket::ReceiveHandshake ()
{
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
boost::bind(&SAMSocket::HandleHandshakeReceived, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint ("SAM handshake read error: ", ecode.message ());
LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_Buffer[bytes_transferred] = 0;
LogPrint ("SAM handshake ", m_Buffer);
if (!memcmp (m_Buffer, SAM_HANDSHAKE, strlen (SAM_HANDSHAKE)))
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
*eol = 0;
LogPrint (eLogDebug, "SAM: handshake ", m_Buffer);
char * separator = strchr (m_Buffer, ' ');
if (separator)
{
// TODO: check version
boost::asio::async_write (m_Socket, boost::asio::buffer (SAM_HANDSHAKE_REPLY, strlen (SAM_HANDSHAKE_REPLY)), boost::asio::transfer_all (),
boost::bind(&SAMSocket::HandleHandshakeReplySent, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
separator = strchr (separator + 1, ' ');
if (separator)
*separator = 0;
}
if (!strcmp (m_Buffer, SAM_HANDSHAKE))
{
std::string version("3.0");
// try to find MIN and MAX, 3.0 if not found
if (separator)
{
separator++;
std::map<std::string, std::string> params;
ExtractParams (separator, params);
auto it = params.find (SAM_PARAM_MAX);
// TODO: check MIN as well
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
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::placeholders::_1, std::placeholders::_2));
}
else
SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true);
}
else
{
LogPrint ("SAM handshake mismatch");
LogPrint (eLogError, "SAM: handshake mismatch");
Terminate ();
}
}
@@ -106,24 +137,24 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM handshake reply send error: ", ecode.message ());
LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
boost::bind(&SAMSocket::HandleMessage, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
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 || m_SocketType == eSAMSocketTypeAcceptor)
if (!m_IsSilent)
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
boost::bind(&SAMSocket::HandleMessageReplySent, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, close));
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, close));
else
{
if (close)
@@ -137,7 +168,7 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM reply send error: ", ecode.message ());
LogPrint (eLogError, "SAM: reply send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -154,14 +185,18 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM read error: ", ecode.message ());
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else if (m_SocketType == eSAMSocketTypeStream)
HandleReceived (ecode, bytes_transferred);
else
{
bytes_transferred += m_BufferOffset;
m_BufferOffset = 0;
m_Buffer[bytes_transferred] = 0;
char * eol = strchr (m_Buffer, '\n');
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
{
*eol = 0;
@@ -184,56 +219,82 @@ namespace client
ProcessDestGenerate ();
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND))
{
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
if (processed < len)
{
m_BufferOffset = len - processed;
if (processed > 0)
memmove (m_Buffer, separator + 1 + processed, m_BufferOffset);
else
{
// restore string back
*separator = ' ';
*eol = '\n';
}
}
// since it's SAM v1 reply is not expected
Receive ();
}
else
{
LogPrint ("SAM unexpected message ", m_Buffer);
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
Terminate ();
}
}
else
{
LogPrint ("SAM malformed message ", m_Buffer);
LogPrint (eLogError, "SAM: malformed message ", m_Buffer);
Terminate ();
}
}
else
{
LogPrint ("SAM malformed message ", m_Buffer);
Terminate ();
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
m_BufferOffset = bytes_transferred;
// try to receive remaining message
Receive ();
}
}
}
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
{
LogPrint ("SAM session create: ", buf);
LogPrint (eLogDebug, "SAM: session create: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, len, params);
ExtractParams (buf, params);
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
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true);
return;
}
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination);
// create destination
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
if (m_Session)
{
m_SocketType = eSAMSocketTypeSession;
if (m_Session->localDestination->IsReady ())
if (style == SAM_VALUE_DATAGRAM)
{
if (style == SAM_VALUE_DATAGRAM)
m_Session->localDestination->CreateDatagramDestination ();
SendSessionCreateReplyOk ();
auto dest = m_Session->localDestination->CreateDatagramDestination ();
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
}
if (m_Session->localDestination->IsReady ())
SendSessionCreateReplyOk ();
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (boost::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
this, boost::asio::placeholders::error));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
}
else
@@ -249,8 +310,8 @@ namespace client
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (boost::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
this, boost::asio::placeholders::error));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
}
}
@@ -272,9 +333,9 @@ namespace client
void SAMSocket::ProcessStreamConnect (char * buf, size_t len)
{
LogPrint ("SAM stream connect: ", buf);
LogPrint (eLogDebug, "SAM: stream connect: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, len, params);
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
std::string& silent = params[SAM_PARAM_SILENT];
@@ -283,75 +344,54 @@ namespace client
m_Session = m_Owner.FindSession (id);
if (m_Session)
{
uint8_t ident[1024];
size_t l = i2p::data::Base64ToByteStream (destination.c_str (), destination.length (), ident, 1024);
i2p::data::IdentityEx dest;
dest.FromBuffer (ident, l);
auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ());
if (leaseSet)
Connect (*leaseSet);
else
auto dest = std::make_shared<i2p::data::IdentityEx> ();
size_t len = dest->FromBase64(destination);
if (len > 0)
{
i2p::data::netdb.RequestDestination (dest.GetIdentHash (), true, m_Session->localDestination->GetTunnelPool ());
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_CONNECT_TIMEOUT));
m_Timer.async_wait (boost::bind (&SAMSocket::HandleStreamDestinationRequestTimer,
this, boost::asio::placeholders::error, dest.GetIdentHash ()));
context.GetAddressBook().InsertAddress(dest);
auto leaseSet = m_Session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet)
Connect(leaseSet);
else
{
m_Session->localDestination->RequestDestination(dest->GetIdentHash(),
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1));
}
}
else
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
}
else
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::Connect (const i2p::data::LeaseSet& remote)
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
{
m_SocketType = eSAMSocketTypeStream;
m_Session->sockets.push_back (this);
m_Session->sockets.push_back (shared_from_this ());
m_Stream = m_Session->localDestination->CreateStream (remote);
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
I2PReceive ();
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
void SAMSocket::HandleStreamDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident)
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (!ecode) // timeout expired
if (leaseSet)
Connect (leaseSet);
else
{
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
Connect (*leaseSet);
else
{
LogPrint ("SAM destination to connect not found");
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
}
}
}
void SAMSocket::HandleNamingLookupDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident)
{
if (!ecode) // timeout expired
{
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
SendNamingLookupReply (leaseSet);
else
{
LogPrint ("SAM name destination not found");
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_KEY_NOT_FOUND, (ident.ToBase32 () + ".b32.i2p").c_str ());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_KEY_NOT_FOUND, (ident.ToBase32 () + ".b32.i2p").c_str ());
#endif
SendMessageReply (m_Buffer, len, false);
}
LogPrint (eLogError, "SAM: destination to connect not found");
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
}
}
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
{
LogPrint ("SAM stream accept: ", buf);
LogPrint (eLogDebug, "SAM: stream accept: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, len, params);
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;
@@ -362,8 +402,8 @@ namespace client
if (!m_Session->localDestination->IsAcceptingStreams ())
{
m_SocketType = eSAMSocketTypeAcceptor;
m_Session->sockets.push_back (this);
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, this, std::placeholders::_1));
m_Session->sockets.push_back (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
@@ -373,56 +413,77 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
{
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;
if (offset + size <= len)
{
if (m_Session)
{
auto d = m_Session->localDestination->GetDatagramDestination ();
if (d)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
}
else
LogPrint (eLogError, "SAM: missing datagram destination");
}
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 ("SAM dest generate");
auto localDestination = i2p::client::context.CreateNewLocalDestination ();
if (localDestination)
{
uint8_t buf[1024];
char priv[1024], pub[1024];
size_t l = localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
priv[l1] = 0;
l = localDestination->GetIdentity ().ToBuffer (buf, 1024);
l1 = i2p::data::ByteStreamToBase64 (buf, l, pub, 1024);
pub[l1] = 0;
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, pub, priv);
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, pub, priv);
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, true);
}
else
SendMessageReply (SAM_DEST_REPLY_I2P_ERROR, strlen(SAM_DEST_REPLY_I2P_ERROR), true);
SendMessageReply (m_Buffer, len, false);
}
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
{
LogPrint ("SAM naming lookup: ", buf);
LogPrint (eLogDebug, "SAM: naming lookup: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, len, params);
ExtractParams (buf, params);
std::string& name = params[SAM_PARAM_NAME];
std::shared_ptr<const i2p::data::IdentityEx> identity;
i2p::data::IdentHash ident;
if (name == "ME")
SendNamingLookupReply (nullptr);
else if (m_Session && context.GetAddressBook ().GetIdentHash (name, ident))
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
SendNamingLookupReply (identity);
else if (m_Session && m_Session->localDestination &&
context.GetAddressBook ().GetIdentHash (name, ident))
{
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
SendNamingLookupReply (leaseSet);
SendNamingLookupReply (leaseSet->GetIdentity ());
else
{
i2p::data::netdb.RequestDestination (ident, true, m_Session->localDestination->GetTunnelPool ());
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_NAMING_LOOKUP_TIMEOUT));
m_Timer.async_wait (boost::bind (&SAMSocket::HandleNamingLookupDestinationRequestTimer,
this, boost::asio::placeholders::error, ident));
}
}
else
m_Session->localDestination->RequestDestination (ident,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
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
@@ -432,23 +493,39 @@ namespace client
}
}
void SAMSocket::SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet)
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
{
uint8_t buf[1024];
char pub[1024];
const i2p::data::IdentityEx& identity = leaseSet ? leaseSet->GetIdentity () : m_Session->localDestination->GetIdentity ();
size_t l = identity.ToBuffer (buf, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, pub, 1024);
pub[l1] = 0;
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 l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, pub);
#else
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, pub);
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
#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, l2, false);
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 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#endif
SendMessageReply (m_Buffer, l, false);
}
void SAMSocket::ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params)
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
{
char * separator;
do
@@ -469,24 +546,39 @@ namespace client
void SAMSocket::Receive ()
{
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
boost::bind((m_SocketType == eSAMSocketTypeSession) ? &SAMSocket::HandleMessage : &SAMSocket::HandleReceived,
this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE)
{
LogPrint (eLogError, "SAM: Buffer is full, terminate");
Terminate ();
return;
}
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));
}
void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint ("SAM read error: ", ecode.message ());
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (m_Stream)
m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred);
Receive ();
{
auto s = shared_from_this ();
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
[s](const boost::system::error_code& ecode)
{
if (!ecode)
s->Receive ();
else
s->Terminate ();
});
}
}
}
@@ -494,8 +586,8 @@ namespace client
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
boost::bind (&SAMSocket::HandleI2PReceive, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
SAM_SOCKET_CONNECTION_MAX_IDLE);
}
@@ -503,14 +595,14 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM stream read error: ", ecode.message ());
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
boost::bind (&SAMSocket::HandleWriteI2PData, this, boost::asio::placeholders::error));
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
}
}
@@ -518,7 +610,7 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM socket write error: ", ecode.message ());
LogPrint (eLogError, "SAM: socket write error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -526,40 +618,90 @@ namespace client
I2PReceive ();
}
void SAMSocket::HandleI2PAccept (i2p::stream::Stream * stream)
void SAMSocket::HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
LogPrint ("SAM incoming I2P connection for session ", m_ID);
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
m_Stream = stream;
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
auto session = m_Owner.FindSession (m_ID);
if (session)
session->localDestination->StopAcceptingStreams ();
m_SocketType = eSAMSocketTypeStream;
if (!m_IsSilent)
{
// send remote peer address
uint8_t ident[1024];
size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, m_Buffer, SAM_SOCKET_BUFFER_SIZE);
m_Buffer[l1] = '\n';
SendMessageReply (m_Buffer, l1 + 1, false);
// get remote peer address
auto ident_ptr = stream->GetRemoteIdentity();
const size_t ident_len = ident_ptr->GetFullLen();
uint8_t* ident = new uint8_t[ident_len];
// 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
}
I2PReceive ();
else
I2PReceive ();
}
else
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
}
SAMBridge::SAMBridge (int port):
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));
}
else
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
}
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
localDestination (dest)
{
}
SAMSession::~SAMSession ()
{
for (auto it: sockets)
it->SetSocketType (eSAMSocketTypeTerminated);
i2p::client::context.DeleteLocalDestination (localDestination);
}
void SAMSession::CloseStreams ()
{
for (auto it: sockets)
{
it->CloseStream ();
it->SetSocketType (eSAMSocketTypeTerminated);
}
sockets.clear ();
}
SAMBridge::SAMBridge (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint),
m_NewSocket (nullptr)
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
{
}
SAMBridge::~SAMBridge ()
{
Stop ();
delete m_NewSocket;
if (m_IsRunning)
Stop ();
}
void SAMBridge::Start ()
@@ -573,6 +715,10 @@ namespace client
void SAMBridge::Stop ()
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto it: m_Sessions)
delete it.second;
m_Sessions.clear ();
m_Service.stop ();
if (m_Thread)
{
@@ -592,59 +738,69 @@ namespace client
}
catch (std::exception& ex)
{
LogPrint ("SAM: ", ex.what ());
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
}
}
void SAMBridge::Accept ()
{
m_NewSocket = new SAMSocket (*this);
m_Acceptor.async_accept (m_NewSocket->GetSocket (), boost::bind (&SAMBridge::HandleAccept, this,
boost::asio::placeholders::error));
auto newSocket = std::make_shared<SAMSocket> (*this);
m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void SAMBridge::HandleAccept(const boost::system::error_code& ecode)
void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket)
{
if (!ecode)
{
LogPrint ("New SAM connection from ", m_NewSocket->GetSocket ().remote_endpoint ());
m_NewSocket->ReceiveHandshake ();
boost::system::error_code ec;
auto ep = socket->GetSocket ().remote_endpoint (ec);
if (!ec)
{
LogPrint (eLogDebug, "SAM: new connection from ", ep);
socket->ReceiveHandshake ();
}
else
LogPrint (eLogError, "SAM: incoming connection error ", ec.message ());
}
else
{
LogPrint ("SAM accept error: ", ecode.message ());
delete m_NewSocket;
m_NewSocket = nullptr;
}
LogPrint (eLogError, "SAM: accept error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Accept ();
}
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination)
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination,
const std::map<std::string, std::string> * params)
{
ClientDestination * localDestination = nullptr;
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (destination != "")
{
uint8_t * buf = new uint8_t[destination.length ()];
size_t l = i2p::data::Base64ToByteStream (destination.c_str (), destination.length (), buf, destination.length ());
i2p::data::PrivateKeys keys;
keys.FromBuffer (buf, l);
delete[] buf;
localDestination = i2p::client::context.CreateNewLocalDestination (keys);
keys.FromBase64 (destination);
localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params);
}
else // transient
localDestination = i2p::client::context.CreateNewLocalDestination ();
{
// extract signature type
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
if (params)
{
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params->end ())
// TODO: extract string values
signatureType = boost::lexical_cast<int> (it->second);
}
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
}
if (localDestination)
{
SAMSession session;
session.localDestination = localDestination;
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession>(id, session));
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession *>(id, new SAMSession (localDestination)));
if (!ret.second)
LogPrint ("Session ", id, " already exists");
return &(ret.first->second);
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
return ret.first->second;
}
return nullptr;
}
@@ -655,20 +811,20 @@ namespace client
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
{
for (auto it1 : it->second.sockets)
delete it1;
it->second.sockets.clear ();
it->second.localDestination->Stop ();
auto session = it->second;
session->localDestination->StopAcceptingStreams ();
session->CloseStreams ();
m_Sessions.erase (it);
delete session;
}
}
SAMSession * SAMBridge::FindSession (const std::string& id)
SAMSession * SAMBridge::FindSession (const std::string& id) const
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
return &it->second;
return it->second;
return nullptr;
}
@@ -677,7 +833,7 @@ namespace client
m_DatagramSocket.async_receive_from (
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
m_SenderEndpoint,
boost::bind (&SAMBridge::HandleReceivedDatagram, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
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)
@@ -688,7 +844,7 @@ namespace client
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
*eol = 0; eol++;
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
if (sessionID)
{
@@ -700,33 +856,23 @@ namespace client
auto session = FindSession (sessionID);
if (session)
{
uint8_t ident[1024];
size_t l = i2p::data::Base64ToByteStream (destination, strlen(destination), ident, 1024);
i2p::data::IdentityEx dest;
dest.FromBuffer (ident, l);
auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ());
if (leaseSet)
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, *leaseSet);
else
{
LogPrint ("SAM datagram destination not found");
i2p::data::netdb.RequestDestination (dest.GetIdentHash (), true,
session->localDestination->GetTunnelPool ());
}
dest.FromBase64 (destination);
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
}
else
LogPrint ("Session ", sessionID, " not found");
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
}
else
LogPrint ("Missing destination key");
LogPrint (eLogError, "SAM: Missing destination key");
}
else
LogPrint ("Missing sessionID");
LogPrint (eLogError, "SAM: Missing sessionID");
ReceiveDatagram ();
}
else
LogPrint ("SAM datagram receive error: ", ecode.message ());
LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ());
}
}
}

78
SAM.h
View File

@@ -7,6 +7,7 @@
#include <list>
#include <thread>
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "Identity.h"
#include "LeaseSet.h"
@@ -17,35 +18,41 @@ namespace i2p
{
namespace client
{
const size_t SAM_SOCKET_BUFFER_SIZE = 4096;
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
const int SAM_CONNECT_TIMEOUT = 5; // in seconds
const int SAM_NAMING_LOOKUP_TIMEOUT = 5; // in seconds
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 char SAM_HANDSHAKE[] = "HELLO VERSION";
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=3.0\n";
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_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_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\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_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_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_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_SILENT[] = "SILENT";
const char SAM_PARAM_DESTINATION[] = "DESTINATION";
const char SAM_PARAM_NAME[] = "NAME";
const char SAM_PARAM_NAME[] = "NAME";
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
const char SAM_PARAM_SIZE[] = "SIZE";
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
const char SAM_VALUE_STREAM[] = "STREAM";
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
@@ -58,24 +65,28 @@ namespace client
eSAMSocketTypeUnknown,
eSAMSocketTypeSession,
eSAMSocketTypeStream,
eSAMSocketTypeAcceptor
eSAMSocketTypeAcceptor,
eSAMSocketTypeTerminated
};
class SAMBridge;
class SAMSession;
class SAMSocket
struct SAMSession;
class SAMSocket: public std::enable_shared_from_this<SAMSocket>
{
public:
SAMSocket (SAMBridge& owner);
~SAMSocket ();
void CloseStream (); // TODO: implement it better
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
void ReceiveHandshake ();
void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; };
SAMSocketType GetSocketType () const { return m_SocketType; };
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);
@@ -86,20 +97,22 @@ namespace client
void I2PReceive ();
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleI2PAccept (i2p::stream::Stream * stream);
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
void HandleWriteI2PData (const boost::system::error_code& ecode);
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void ProcessSessionCreate (char * buf, size_t len);
void ProcessStreamConnect (char * buf, size_t len);
void ProcessStreamAccept (char * buf, size_t len);
void ProcessDestGenerate ();
void ProcessNamingLookup (char * buf, size_t len);
void ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params);
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 (const i2p::data::LeaseSet& remote);
void HandleStreamDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident);
void HandleNamingLookupDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident);
void SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet);
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk ();
@@ -109,41 +122,48 @@ namespace client
boost::asio::ip::tcp::socket m_Socket;
boost::asio::deadline_timer m_Timer;
char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1];
size_t m_BufferOffset;
uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE];
SAMSocketType m_SocketType;
std::string m_ID; // nickname
bool m_IsSilent;
i2p::stream::Stream * m_Stream;
std::shared_ptr<i2p::stream::Stream> m_Stream;
SAMSession * m_Session;
};
struct SAMSession
{
ClientDestination * localDestination;
std::list<SAMSocket *> sockets;
std::shared_ptr<ClientDestination> localDestination;
std::list<std::shared_ptr<SAMSocket> > sockets;
SAMSession (std::shared_ptr<ClientDestination> dest);
~SAMSession ();
void CloseStreams ();
};
class SAMBridge
{
public:
SAMBridge (int port);
SAMBridge (const std::string& address, int port);
~SAMBridge ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
SAMSession * CreateSession (const std::string& id, const std::string& destination = ""); // empty string means transient
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);
SAMSession * FindSession (const std::string& id);
SAMSession * FindSession (const std::string& id) const;
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode);
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket);
void ReceiveDatagram ();
void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred);
@@ -156,10 +176,14 @@ namespace client
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_DatagramSocket;
SAMSocket * m_NewSocket;
std::mutex m_SessionsMutex;
std::map<std::string, SAMSession> m_Sessions;
mutable std::mutex m_SessionsMutex;
std::map<std::string, SAMSession *> m_Sessions;
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
public:
// for HTTP
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
};
}
}

959
SOCKS.cpp

File diff suppressed because it is too large Load Diff

105
SOCKS.h
View File

@@ -1,97 +1,38 @@
#ifndef SOCKS4A_H__
#define SOCKS4A_H__
#ifndef SOCKS_H__
#define SOCKS_H__
#include <thread>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <vector>
#include <mutex>
#include "Identity.h"
#include "Streaming.h"
#include "I2PService.h"
namespace i2p
{
namespace proxy
{
const size_t socks_buffer_size = 8192;
class SOCKS4AHandler {
private:
enum state {
INITIAL,
OKAY,
END
};
void GotClientRequest(boost::system::error_code & ecode, std::string & host, uint16_t port);
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleSockForward(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleStreamRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void CloseSock();
void CloseStream();
void AsyncSockRead();
void AsyncStreamRead();
void SocksFailed();
void LeaseSetTimeout(const boost::system::error_code & ecode);
void StreamWrote(const boost::system::error_code & ecode);
void SockWrote(const boost::system::error_code & ecode);
void SentConnectionSuccess(const boost::system::error_code & ecode);
void ConnectionSuccess();
uint8_t m_sock_buff[socks_buffer_size];
uint8_t m_stream_buff[socks_buffer_size];
boost::asio::io_service * m_ios;
boost::asio::ip::tcp::socket * m_sock;
boost::asio::deadline_timer m_ls_timer;
i2p::stream::Stream * m_stream;
i2p::data::LeaseSet * m_ls;
i2p::data::IdentHash m_dest;
state m_state;
class SOCKSServer: public i2p::client::TCPIPAcceptor
{
public:
SOCKS4AHandler(boost::asio::io_service * ios, boost::asio::ip::tcp::socket * sock) :
m_ios(ios), m_sock(sock), m_ls_timer(*ios),
m_stream(nullptr), m_ls(nullptr), m_state(INITIAL) { AsyncSockRead(); }
~SOCKS4AHandler() { CloseSock(); CloseStream(); }
bool isComplete() { return m_state == END; }
SOCKSServer(const std::string& address, int port, const std::string& outAddress, uint16_t outPort,
std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
~SOCKSServer() {};
void SetUpstreamProxy(const std::string & addr, const uint16_t port);
protected:
// Implements TCPIPAcceptor
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
const char* GetName() { return "SOCKS"; }
private:
std::string m_UpstreamProxyAddress;
uint16_t m_UpstreamProxyPort;
bool m_UseUpstreamProxy;
};
class SOCKS4AServer {
public:
SOCKS4AServer(int port) : m_run(false),
m_thread(nullptr),
m_work(m_ios),
m_acceptor(m_ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_new_sock(nullptr) { }
~SOCKS4AServer() { Stop(); }
void Start();
void Stop();
boost::asio::io_service& GetService () { return m_ios; };
private:
void Run();
void Accept();
void HandleAccept(const boost::system::error_code& ecode);
bool m_run;
std::thread * m_thread;
boost::asio::io_service m_ios;
boost::asio::io_service::work m_work;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket * m_new_sock;
};
typedef SOCKS4AServer SOCKSProxy;
typedef SOCKSServer SOCKSProxy;
}
}

1486
SSU.cpp

File diff suppressed because it is too large Load Diff

190
SSU.h
View File

@@ -7,143 +7,31 @@
#include <list>
#include <set>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include "aes.h"
#include "Crypto.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "TransportSession.h"
#include "SSUData.h"
#include "SSUSession.h"
namespace i2p
{
namespace transport
{
#pragma pack(1)
struct SSUHeader
{
uint8_t mac[16];
uint8_t iv[16];
uint8_t flag;
uint32_t time;
uint8_t GetPayloadType () const { return flag >> 4; };
};
#pragma pack()
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
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 size_t SSU_MAX_NUM_INTRODUCERS = 3;
// payload types (4 bits)
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
enum SessionState
struct SSUPacket
{
eSessionStateUnknown,
eSessionStateIntroduced,
eSessionStateEstablished,
eSessionStateFailed
i2p::crypto::AESAlignedBuffer<1500> buf;
boost::asio::ip::udp::endpoint from;
size_t len;
};
class SSUServer;
class SSUSession: public TransportSession
{
public:
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
const i2p::data::RouterInfo * router = nullptr, bool peerTest = false);
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
~SSUSession ();
void Connect ();
void Introduce (uint32_t iTag, const uint8_t * iKey);
void WaitForIntroduction ();
void Close ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessage (I2NPMessage * msg);
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; };
uint32_t GetCreationTime () const { return m_CreationTime; };
private:
void CreateAESandMacKey (const uint8_t * pubKey);
void PostI2NPMessage (I2NPMessage * msg);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest ();
void SendRelayRequest (uint32_t iTag, const uint8_t * iKey);
void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x);
void ProcessSessionConfirmed (uint8_t * buf, size_t len);
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
void ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from);
void ProcessRelayResponse (uint8_t * buf, size_t len);
void ProcessRelayIntro (uint8_t * buf, size_t len);
void Established ();
void Failed ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void ProcessPeerTest (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);
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
void Send (const uint8_t * buf, size_t size);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
void DecryptSessionKey (uint8_t * buf, size_t len);
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
const uint8_t * GetIntroKey () const;
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;
bool m_PeerTest;
SessionState m_State;
bool m_IsSessionKey;
uint32_t m_RelayTag;
std::set<uint32_t> m_PeerTestNonces;
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
uint8_t m_SessionKey[32], m_MacKey[32];
std::list<i2p::I2NPMessage *> m_DelayedMessages;
SSUData m_Data;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint32_t m_CreationTime; // seconds since epoch
};
class SSUServer
{
public:
@@ -152,53 +40,77 @@ namespace transport
~SSUServer ();
void Start ();
void Stop ();
SSUSession * GetSession (const i2p::data::RouterInfo * router, bool peerTest = false);
SSUSession * FindSession (const i2p::data::RouterInfo * router);
SSUSession * FindSession (const boost::asio::ip::udp::endpoint& e);
SSUSession * GetRandomEstablishedSession (const SSUSession * excluded);
void DeleteSession (SSUSession * session);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = 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);
void DeleteSession (std::shared_ptr<SSUSession> session);
void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Socket.get_io_service(); };
boost::asio::io_service& GetService () { return m_Service; };
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);
SSUSession * FindRelaySession (uint32_t tag);
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest (uint32_t nonce);
private:
void Run ();
void RunV6 ();
void RunReceivers ();
void Receive ();
void ReceiveV6 ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleReceivedBuffer (boost::asio::ip::udp::endpoint& from, uint8_t * buf, std::size_t bytes_transferred);
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
template<typename Filter>
SSUSession * GetRandomSession (Filter filter);
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
void SchedulePeerTestsCleanupTimer ();
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
private:
struct PeerTest
{
uint64_t creationTime;
PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice
};
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
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;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::ip::udp::endpoint m_SenderEndpoint, m_SenderEndpointV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer;
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
uint8_t m_ReceiveBuffer[2*SSU_MTU_V4], m_ReceiveBufferV6[2*SSU_MTU_V6];
std::map<boost::asio::ip::udp::endpoint, SSUSession *> m_Sessions;
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, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
public:
// for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
};
}
}

View File

@@ -10,31 +10,48 @@ namespace i2p
{
namespace transport
{
SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.m_Server.GetService ())
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
{
if (msg->len + fragmentSize > msg->maxLen)
{
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *msg;
msg = newMsg;
}
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
nextFragmentNum++;
}
SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (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_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE;
m_PacketSize = m_MaxPacketSize;
auto remoteRouter = session.GetRemoteRouter ();
if (remoteRouter)
AdjustPacketSize (*remoteRouter);
}
SSUData::~SSUData ()
{
for (auto it: m_IncomleteMessages)
if (it.second)
{
DeleteI2NPMessage (it.second->msg);
delete it.second;
}
for (auto it: m_SentMessages)
delete it.second;
}
void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter)
void SSUData::Start ()
{
auto ssuAddress = remoteRouter.GetSSUAddress ();
ScheduleIncompleteMessagesCleanup ();
}
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;
auto ssuAddress = remoteRouter->GetSSUAddress ();
if (ssuAddress && ssuAddress->mtu)
{
if (m_Session.IsV6 ())
@@ -47,11 +64,11 @@ namespace transport
m_PacketSize >>= 4;
m_PacketSize <<= 4;
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
}
else
{
LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu);
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu);
m_PacketSize = m_MaxPacketSize;
}
}
@@ -61,7 +78,7 @@ namespace transport
{
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
if (routerInfo)
AdjustPacketSize (*routerInfo);
AdjustPacketSize (routerInfo);
}
void SSUData::ProcessSentMessageAck (uint32_t msgID)
@@ -69,7 +86,6 @@ namespace transport
auto it = m_SentMessages.find (msgID);
if (it != m_SentMessages.end ())
{
delete it->second;
m_SentMessages.erase (it);
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
@@ -84,7 +100,7 @@ namespace transport
uint8_t numAcks =*buf;
buf++;
for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (be32toh (((uint32_t *)buf)[i]));
ProcessSentMessageAck (bufbe32toh (buf+i*4));
buf += numAcks*4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
@@ -94,7 +110,7 @@ namespace transport
buf++;
for (int i = 0; i < numBitfields; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf);
uint32_t msgID = bufbe32toh (buf);
buf += 4; // msgID
auto it = m_SentMessages.find (msgID);
// process individual Ack bitfields
@@ -115,10 +131,7 @@ namespace transport
if (bitfield & mask)
{
if (fragment < numSentFragments)
{
delete it->second->fragments[fragment];
it->second->fragments[fragment] = nullptr;
}
it->second->fragments[fragment].reset (nullptr);
}
fragment++;
mask <<= 1;
@@ -137,81 +150,72 @@ namespace transport
buf++;
for (int i = 0; i < numFragments; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf); // message ID
uint32_t msgID = bufbe32toh (buf); // message ID
buf += 4;
uint8_t frag[4];
frag[0] = 0;
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = be32toh (*(uint32_t *)frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
LogPrint (eLogDebug, "SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
{
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
return;
}
// find message with msgID
I2NPMessage * msg = nullptr;
IncompleteMessage * incompleteMessage = nullptr;
auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ())
{
// message exists
incompleteMessage = it->second;
msg = incompleteMessage->msg;
}
else
auto it = m_IncompleteMessages.find (msgID);
if (it == m_IncompleteMessages.end ())
{
// create new message
msg = NewI2NPMessage ();
msg->len -= sizeof (I2NPHeaderShort);
incompleteMessage = new IncompleteMessage (msg);
m_IncomleteMessages[msgID] = incompleteMessage;
}
auto msg = NewI2NPShortMessage ();
msg->len -= I2NP_SHORT_HEADER_SIZE;
it = m_IncompleteMessages.insert (std::make_pair (msgID,
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
}
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
// handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum)
{
// expected fragment
memcpy (msg->buf + msg->len, buf, fragmentSize);
msg->len += fragmentSize;
incompleteMessage->nextFragmentNum++;
incompleteMessage->AttachNextFragment (buf, fragmentSize);
if (!isLast && !incompleteMessage->savedFragments.empty ())
{
// try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
{
auto savedFragment = *it1;
auto& savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
{
memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len);
msg->len += savedFragment->len;
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
isLast = savedFragment->isLast;
incompleteMessage->nextFragmentNum++;
incompleteMessage->savedFragments.erase (it1++);
delete savedFragment;
}
else
break;
}
if (isLast)
LogPrint (eLogDebug, "Message ", msgID, " complete");
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
}
}
else
{
if (fragmentNum < incompleteMessage->nextFragmentNum)
// duplicate fragment
LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored");
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
else
{
// missing fragment
LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
if (!incompleteMessage->savedFragments.insert (savedFragment).second)
{
LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
delete savedFragment;
}
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
else
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
}
isLast = false;
}
@@ -219,8 +223,9 @@ namespace transport
if (isLast)
{
// delete incomplete message
delete incompleteMessage;
m_IncomleteMessages.erase (msgID);
auto msg = incompleteMessage->msg;
incompleteMessage->msg = nullptr;
m_IncompleteMessages.erase (msgID);
// process message
SendMsgAck (msgID);
msg->FromSSU (msgID);
@@ -228,27 +233,29 @@ namespace transport
{
if (!m_ReceivedMessages.count (msgID))
{
if (m_ReceivedMessages.size () > 100) m_ReceivedMessages.clear ();
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
m_ReceivedMessages.clear ();
else
ScheduleDecay ();
m_ReceivedMessages.insert (msgID);
i2p::HandleI2NPMessage (msg);
if (!msg->IsExpired ())
m_Handler.PutNextMessage (msg);
else
LogPrint (eLogInfo, "SSU: message expired");
}
else
{
LogPrint (eLogWarning, "SSU message ", msgID, " already received");
i2p::DeleteI2NPMessage (msg);
}
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
}
else
{
// we expect DeliveryStatus
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
if (msg->GetTypeID () == eI2NPDeliveryStatus)
{
LogPrint ("SSU session established");
LogPrint (eLogDebug, "SSU: session established");
m_Session.Established ();
}
else
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetHeader ()->typeID);
DeleteI2NPMessage (msg);
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
}
}
else
@@ -257,12 +264,17 @@ namespace transport
}
}
void SSUData::FlushReceivedMessage ()
{
m_Handler.Flush ();
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint (eLogDebug, "Process SSU data flags=", (int)flag);
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
@@ -271,30 +283,32 @@ namespace transport
{
uint8_t extendedDataSize = *buf;
buf++; // size
LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented");
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
buf += extendedDataSize;
}
// process data
ProcessFragments (buf);
}
void SSUData::Send (i2p::I2NPMessage * msg)
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{
uint32_t msgID = msg->ToSSU ();
if (m_SentMessages.count (msgID) > 0)
{
LogPrint (eLogWarning, "SSU message ", msgID, " already sent");
DeleteI2NPMessage (msg);
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
return;
}
if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
SentMessage * sentMessage = new SentMessage;
m_SentMessages[msgID] = sentMessage;
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
if (ret.second)
{
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
}
auto& fragments = sentMessage->fragments;
msgID = htobe32 (msgID);
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength ();
uint8_t * msgBuf = msg->GetSSUHeader ();
@@ -305,13 +319,12 @@ namespace transport
Fragment * fragment = new Fragment;
fragment->fragmentNum = fragmentNum;
uint8_t * buf = fragment->buf;
fragments.push_back (fragment);
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++;
*payload = 1; // always 1 message fragment per message
payload++;
*(uint32_t *)payload = msgID;
htobe32buf (payload, msgID);
payload += 4;
bool isLast = (len <= payloadSize);
size_t size = isLast ? len : payloadSize;
@@ -329,11 +342,18 @@ namespace transport
if (size & 0x0F) // make sure 16 bytes boundary
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
fragment->len = size;
fragments.push_back (std::unique_ptr<Fragment> (fragment));
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
m_Session.Send (buf, size);
try
{
m_Session.Send (buf, size);
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
}
if (!isLast)
{
len -= payloadSize;
@@ -343,7 +363,6 @@ namespace transport
len = 0;
fragmentNum++;
}
DeleteI2NPMessage (msg);
}
void SSUData::SendMsgAck (uint32_t msgID)
@@ -354,7 +373,7 @@ namespace transport
payload++;
*payload = 1; // number of ACKs
payload++;
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
htobe32buf (payload, msgID); // msgID
payload += 4;
*payload = 0; // number of fragments
@@ -367,7 +386,7 @@ namespace transport
{
if (fragmentNum > 64)
{
LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64");
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18];
@@ -396,29 +415,94 @@ namespace transport
{
m_ResendTimer.cancel ();
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
m_ResendTimer.async_wait (boost::bind (&SSUData::HandleResendTimer,
this, boost::asio::placeholders::error));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleResendTimer (ecode); });
}
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it : m_SentMessages)
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
{
if (ts >= it.second->nextResendTime && it.second->numResends < MAX_NUM_RESENDS)
if (ts >= it->second->nextResendTime)
{
for (auto f: it.second->fragments)
if (f) m_Session.Send (f->buf, f->len); // resend
if (it->second->numResends < MAX_NUM_RESENDS)
{
for (auto& f: it->second->fragments)
if (f)
{
try
{
m_Session.Send (f->buf, f->len); // resend
}
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->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
it++;
}
else
{
LogPrint (eLogInfo, "SSU: message has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
it = m_SentMessages.erase (it);
}
}
else
it++;
}
ScheduleResend ();
}
}
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 ();
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
auto s = m_Session.shared_from_this();
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
}
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
{
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
{
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
it = m_IncompleteMessages.erase (it);
}
else
it++;
}
ScheduleIncompleteMessagesCleanup ();
}
}
}
}

View File

@@ -6,6 +6,7 @@
#include <map>
#include <vector>
#include <set>
#include <memory>
#include <boost/asio.hpp>
#include "I2NPProtocol.h"
#include "Identity.h"
@@ -25,6 +26,9 @@ namespace transport
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424
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
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
@@ -47,7 +51,7 @@ namespace transport
struct FragmentCmp
{
bool operator() (const Fragment * f1, const Fragment * f2) const
bool operator() (const std::unique_ptr<Fragment>& f1, const std::unique_ptr<Fragment>& f2) const
{
return f1->fragmentNum < f2->fragmentNum;
};
@@ -55,21 +59,20 @@ namespace transport
struct IncompleteMessage
{
I2NPMessage * msg;
std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum;
std::set<Fragment *, FragmentCmp> savedFragments;
uint32_t lastFragmentInsertTime; // in seconds
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0) {};
~IncompleteMessage () { for (auto it: savedFragments) { delete it; }; };
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
};
struct SentMessage
{
std::vector<Fragment *> fragments;
std::vector<std::unique_ptr<Fragment> > fragments;
uint32_t nextResendTime; // in seconds
int numResends;
~SentMessage () { for (auto it: fragments) { delete it; }; };
};
class SSUSession;
@@ -77,12 +80,17 @@ namespace transport
{
public:
SSUData (SSUSession& session);
SSUData (SSUSession& session);
~SSUData ();
void Start ();
void Stop ();
void ProcessMessage (uint8_t * buf, size_t len);
void Send (i2p::I2NPMessage * msg);
void FlushReceivedMessage ();
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
private:
@@ -95,17 +103,23 @@ 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);
void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter);
private:
SSUSession& m_Session;
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
std::map<uint32_t, SentMessage *> m_SentMessages;
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;
boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer;
int m_MaxPacketSize, m_PacketSize;
i2p::I2NPMessagesHandler m_Handler;
};
}
}

1149
SSUSession.cpp Normal file

File diff suppressed because it is too large Load Diff

164
SSUSession.h Normal file
View File

@@ -0,0 +1,164 @@
#ifndef SSU_SESSION_H__
#define SSU_SESSION_H__
#include <inttypes.h>
#include <set>
#include <memory>
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "TransportSession.h"
#include "SSUData.h"
namespace i2p
{
namespace transport
{
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
struct SSUHeader
{
uint8_t mac[16];
uint8_t iv[16];
uint8_t flag;
uint8_t time[4];
uint8_t GetPayloadType () const { return flag >> 4; };
bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
};
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
// payload types (4 bits)
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
// extended options
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
enum SessionState
{
eSessionStateUnknown,
eSessionStateIntroduced,
eSessionStateEstablished,
eSessionStateClosed,
eSessionStateFailed
};
enum PeerTestParticipant
{
ePeerTestParticipantUnknown = 0,
ePeerTestParticipantAlice1,
ePeerTestParticipantAlice2,
ePeerTestParticipantBob,
ePeerTestParticipantCharlie
};
class SSUServer;
class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession>
{
public:
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, bool peerTest = false);
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
~SSUSession ();
void Connect ();
void WaitForConnect ();
void Introduce (const i2p::data::RouterInfo::Introducer& introducer,
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
void WaitForIntroduction ();
void Close ();
void Done ();
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);
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void FlushData ();
private:
boost::asio::io_service& GetService ();
void CreateAESandMacKey (const uint8_t * pubKey);
size_t GetSSUHeaderSize (const uint8_t * buf) const;
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest ();
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x, bool sendRelayTag = true);
void ProcessSessionConfirmed (const uint8_t * buf, size_t len);
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
void ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
void SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from);
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 ProcessData (uint8_t * buf, size_t len);
void SendSesionDestroyed ();
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
void Send (const uint8_t * buf, size_t size);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey,
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
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;
bool m_IsPeerTest;
SessionState m_State;
bool m_IsSessionKey;
uint32_t m_RelayTag;
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey;
i2p::data::RouterInfo::IntroKey m_IntroKey;
uint32_t m_CreationTime; // seconds since epoch
SSUData m_Data;
bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
std::map<uint32_t, std::shared_ptr<const i2p::data::RouterInfo> > m_RelayRequests; // nonce->Charlie
};
}
}
#endif

486
Signature.cpp Normal file
View File

@@ -0,0 +1,486 @@
#include <memory>
#include "Log.h"
#include "Signature.h"
namespace i2p
{
namespace crypto
{
class Ed25519
{
public:
Ed25519 ()
{
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * tmp = BN_new ();
q = BN_new ();
// 2^255-19
BN_set_bit (q, 255); // 2^255
BN_sub_word (q, 19);
l = BN_new ();
// 2^252 + 27742317777372353535851937790883648493
BN_set_bit (l, 252);
two_252_2 = BN_dup (l);
BN_dec2bn (&tmp, "27742317777372353535851937790883648493");
BN_add (l, l, tmp);
BN_sub_word (two_252_2, 2); // 2^252 - 2
// -121665*inv(121666)
d = BN_new ();
BN_set_word (tmp, 121666);
BN_mod_inverse (tmp, tmp, q, ctx);
BN_set_word (d, 121665);
BN_set_negative (d, 1);
BN_mul (d, d, tmp, ctx);
// 2^((q-1)/4)
I = BN_new ();
BN_free (tmp);
tmp = BN_dup (q);
BN_sub_word (tmp, 1);
BN_div_word (tmp, 4);
BN_set_word (I, 2);
BN_mod_exp (I, I, tmp, q, ctx);
BN_free (tmp);
// 4*inv(5)
BIGNUM * By = BN_new ();
BN_set_word (By, 5);
BN_mod_inverse (By, By, q, ctx);
BN_mul_word (By, 4);
BIGNUM * Bx = RecoverX (By, ctx);
BN_mod (Bx, Bx, q, ctx); // % q
BN_mod (By, By, q, ctx); // % q
// precalculate Bi256 table
Bi256Carry = { Bx, By }; // B
for (int i = 0; i < 32; i++)
{
Bi256[i][0] = Bi256Carry; // first point
for (int j = 1; j < 128; j++)
Bi256[i][j] = Sum (Bi256[i][j-1], Bi256[i][0], ctx); // (256+j+1)^i*B
Bi256Carry = Bi256[i][127];
for (int j = 0; j < 128; j++) // add first point 128 more times
Bi256Carry = Sum (Bi256Carry, Bi256[i][0], ctx);
}
BN_CTX_free (ctx);
}
Ed25519 (const Ed25519& other): q (BN_dup (other.q)), l (BN_dup (other.l)),
d (BN_dup (other.d)), I (BN_dup (other.I)), two_252_2 (BN_dup (other.two_252_2)),
Bi256Carry (other.Bi256Carry)
{
for (int i = 0; i < 32; i++)
for (int j = 0; j < 128; j++)
Bi256[i][j] = other.Bi256[i][j];
}
~Ed25519 ()
{
BN_free (q);
BN_free (l);
BN_free (d);
BN_free (I);
BN_free (two_252_2);
}
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const
{
return MulB (expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian
}
EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const
{
return DecodePoint (buf, ctx);
}
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const
{
EncodePoint (Normalize (publicKey, ctx), buf);
}
bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const
{
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * h = DecodeBN<64> (digest);
// signature 0..31 - R, 32..63 - S
// B*S = R + PK*h => R = B*S - PK*h
// we don't decode R, but encode (B*S - PK*h)
auto Bs = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx); // B*S;
BN_mod (h, h, l, ctx); // public key is multiple of B, but B%l = 0
auto PKh = Mul (publicKey, h, ctx); // PK*h
uint8_t diff[32];
EncodePoint (Normalize (Sum (Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded
bool passed = !memcmp (signature, diff, 32); // R
BN_free (h);
BN_CTX_free (ctx);
if (!passed)
LogPrint (eLogError, "25519 signature verification failed");
return passed;
}
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len,
uint8_t * signature) const
{
BN_CTX * bnCtx = BN_CTX_new ();
// calculate r
SHA512_CTX ctx;
SHA512_Init (&ctx);
SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
SHA512_Update (&ctx, buf, len); // data
uint8_t digest[64];
SHA512_Final (digest, &ctx);
BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors
// calculate R
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
// calculate S
SHA512_Init (&ctx);
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
BIGNUM * h = DecodeBN<64> (digest);
// S = (r + h*a) % l
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (expandedPrivateKey); // left half of expanded key
BN_mod_mul (h, h, a, l, bnCtx); // %l
BN_mod_add (h, h, r, l, bnCtx); // %l
memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2);
EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S
BN_free (r); BN_free (h); BN_free (a);
BN_CTX_free (bnCtx);
}
private:
EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const
{
// x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2)
// y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2)
// z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2)
// t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2)
BIGNUM * x3 = BN_new (), * y3 = BN_new (), * z3 = BN_new (), * t3 = BN_new ();
BN_mul (x3, p1.x, p2.x, ctx); // A = x1*x2
BN_mul (y3, p1.y, p2.y, ctx); // B = y1*y2
BIGNUM * t1 = p1.t, * t2 = p2.t;
if (!t1) { t1 = BN_new (); BN_mul (t1, p1.x, p1.y, ctx); }
if (!t2) { t2 = BN_new (); BN_mul (t2, p2.x, p2.y, ctx); }
BN_mul (t3, t1, t2, ctx);
BN_mul (t3, t3, d, ctx); // C = d*t1*t2
if (!p1.t) BN_free (t1);
if (!p2.t) BN_free (t2);
if (p1.z)
{
if (p2.z)
BN_mul (z3, p1.z, p2.z, ctx); // D = z1*z2
else
BN_copy (z3, p1.z); // D = z1
}
else
{
if (p2.z)
BN_copy (z3, p2.z); // D = z2
else
BN_one (z3); // D = 1
}
BIGNUM * E = BN_new (), * F = BN_new (), * G = BN_new (), * H = BN_new ();
BN_add (E, p1.x, p1.y);
BN_add (F, p2.x, p2.y);
BN_mul (E, E, F, ctx); // (x1 + y1)*(x2 + y2)
BN_sub (E, E, x3);
BN_sub (E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B
BN_sub (F, z3, t3); // F = D - C
BN_add (G, z3, t3); // G = D + C
BN_add (H, y3, x3); // H = B + A
BN_mod_mul (x3, E, F, q, ctx); // x3 = E*F
BN_mod_mul (y3, G, H, q, ctx); // y3 = G*H
BN_mod_mul (z3, F, G, q, ctx); // z3 = F*G
BN_mod_mul (t3, E, H, q, ctx); // t3 = E*H
BN_free (E); BN_free (F); BN_free (G); BN_free (H);
return EDDSAPoint {x3, y3, z3, t3};
}
EDDSAPoint Double (const EDDSAPoint& p, BN_CTX * ctx) const
{
BIGNUM * x2 = BN_new (), * y2 = BN_new (), * z2 = BN_new (), * t2 = BN_new ();
BN_sqr (x2, p.x, ctx); // x2 = A = x^2
BN_sqr (y2, p.y, ctx); // y2 = B = y^2
if (p.t)
BN_sqr (t2, p.t, ctx); // t2 = t^2
else
{
BN_mul (t2, p.x, p.y, ctx); // t = x*y
BN_sqr (t2, t2, ctx); // t2 = t^2
}
BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2
if (p.z)
BN_sqr (z2, p.z, ctx); // z2 = D = z^2
else
BN_one (z2); // z2 = 1
BIGNUM * E = BN_new (), * F = BN_new (), * G = BN_new (), * H = BN_new ();
// E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy
BN_mul (E, p.x, p.y, ctx);
BN_lshift1 (E, E); // E =2*x*y
BN_sub (F, z2, t2); // F = D - C
BN_add (G, z2, t2); // G = D + C
BN_add (H, y2, x2); // H = B + A
BN_mod_mul (x2, E, F, q, ctx); // x2 = E*F
BN_mod_mul (y2, G, H, q, ctx); // y2 = G*H
BN_mod_mul (z2, F, G, q, ctx); // z2 = F*G
BN_mod_mul (t2, E, H, q, ctx); // t2 = E*H
BN_free (E); BN_free (F); BN_free (G); BN_free (H);
return EDDSAPoint {x2, y2, z2, t2};
}
EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const
{
BIGNUM * zero = BN_new (), * one = BN_new ();
BN_zero (zero); BN_one (one);
EDDSAPoint res {zero, one};
if (!BN_is_zero (e))
{
int bitCount = BN_num_bits (e);
for (int i = bitCount - 1; i >= 0; i--)
{
res = Double (res, ctx);
if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx);
}
}
return res;
}
EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const // B*e, e is 32 bytes Little Endian
{
BIGNUM * zero = BN_new (), * one = BN_new ();
BN_zero (zero); BN_one (one);
EDDSAPoint res {zero, one};
bool carry = false;
for (int i = 0; i < 32; i++)
{
uint8_t x = e[i];
if (carry)
{
if (x < 255)
{
x++;
carry = false;
}
else
x = 0;
}
if (x > 0)
{
if (x <= 128)
res = Sum (res, Bi256[i][x-1], ctx);
else
{
res = Sum (res, -Bi256[i][255-x], ctx); // -Bi[256-x]
carry = true;
}
}
}
if (carry) res = Sum (res, Bi256Carry, ctx);
return res;
}
EDDSAPoint Normalize (const EDDSAPoint& p, BN_CTX * ctx) const
{
if (p.z)
{
BIGNUM * x = BN_new (), * y = BN_new ();
BN_mod_inverse (y, p.z, q, ctx);
BN_mod_mul (x, p.x, y, q, ctx); // x = x/z
BN_mod_mul (y, p.y, y, q, ctx); // y = y/z
return EDDSAPoint{x, y};
}
else
return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)};
}
bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const
{
BIGNUM * x2 = BN_new ();
BN_sqr (x2, p.x, ctx); // x^2
BIGNUM * y2 = BN_new ();
BN_sqr (y2, p.y, ctx); // y^2
// y^2 - x^2 - 1 - d*x^2*y^2
BIGNUM * tmp = BN_new ();
BN_mul (tmp, d, x2, ctx);
BN_mul (tmp, tmp, y2, ctx);
BN_sub (tmp, y2, tmp);
BN_sub (tmp, tmp, x2);
BN_sub_word (tmp, 1);
BN_mod (tmp, tmp, q, ctx); // % q
bool ret = BN_is_zero (tmp);
BN_free (x2);
BN_free (y2);
BN_free (tmp);
return ret;
}
BIGNUM * RecoverX (const BIGNUM * y, BN_CTX * ctx) const
{
BIGNUM * y2 = BN_new ();
BN_sqr (y2, y, ctx); // y^2
// xx = (y^2 -1)*inv(d*y^2 +1)
BIGNUM * xx = BN_new ();
BN_mul (xx, d, y2, ctx);
BN_add_word (xx, 1);
BN_mod_inverse (xx, xx, q, ctx);
BN_sub_word (y2, 1);
BN_mul (xx, y2, xx, ctx);
// x = srqt(xx) = xx^(2^252-2)
BIGNUM * x = BN_new ();
BN_mod_exp (x, xx, two_252_2, q, ctx);
// check (x^2 -xx) % q
BN_sqr (y2, x, ctx);
BN_mod_sub (y2, y2, xx, q, ctx);
if (!BN_is_zero (y2))
BN_mod_mul (x, x, I, q, ctx);
if (BN_is_odd (x))
BN_sub (x, q, x);
BN_free (y2);
BN_free (xx);
return x;
}
EDDSAPoint DecodePoint (const uint8_t * buf, BN_CTX * ctx) const
{
// buf is 32 bytes Little Endian, convert it to Big Endian
uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH];
for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH/2; i++) // invert bytes
{
buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i];
buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i];
}
bool isHighestBitSet = buf1[0] & 0x80;
if (isHighestBitSet)
buf1[0] &= 0x7f; // clear highest bit
BIGNUM * y = BN_new ();
BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y);
auto x = RecoverX (y, ctx);
if (BN_is_bit_set (x, 0) != isHighestBitSet)
BN_sub (x, q, x); // x = q - x
BIGNUM * z = BN_new (), * t = BN_new ();
BN_one (z); BN_mod_mul (t, x, y, q, ctx); // pre-calculate t
EDDSAPoint p {x, y, z, t};
if (!IsOnCurve (p, ctx))
LogPrint (eLogError, "Decoded point is not on 25519");
return p;
}
void EncodePoint (const EDDSAPoint& p, uint8_t * buf) const
{
EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH);
if (BN_is_bit_set (p.x, 0)) // highest bit
buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit
}
template<int len>
BIGNUM * DecodeBN (const uint8_t * buf) const
{
// buf is Little Endian convert it to Big Endian
uint8_t buf1[len];
for (size_t i = 0; i < len/2; i++) // invert bytes
{
buf1[i] = buf[len -1 - i];
buf1[len -1 - i] = buf[i];
}
BIGNUM * res = BN_new ();
BN_bin2bn (buf1, len, res);
return res;
}
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const
{
bn2buf (bn, buf, len);
// To Little Endian
for (size_t i = 0; i < len/2; i++) // invert bytes
{
uint8_t tmp = buf[i];
buf[i] = buf[len -1 - i];
buf[len -1 - i] = tmp;
}
}
private:
BIGNUM * q, * l, * d, * I;
// transient values
BIGNUM * two_252_2; // 2^252-2
EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes
// if j > 128 we use 256 - j and carry 1 to next byte
// Bi256[0][0] = B, base point
EDDSAPoint Bi256Carry; // Bi256[32][0]
};
static std::unique_ptr<Ed25519> g_Ed25519;
std::unique_ptr<Ed25519>& GetEd25519 ()
{
if (!g_Ed25519)
g_Ed25519.reset (new Ed25519());
return g_Ed25519;
}
EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey)
{
memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
BN_CTX * ctx = BN_CTX_new ();
m_PublicKey = GetEd25519 ()->DecodePublicKey (m_PublicKeyEncoded, ctx);
BN_CTX_free (ctx);
}
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
uint8_t digest[64];
SHA512_CTX ctx;
SHA512_Init (&ctx);
SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
return GetEd25519 ()->Verify (m_PublicKey, digest, signature);
}
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey)
{
// 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] |= 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);
BN_CTX_free (ctx);
}
void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
}
}
}

View File

@@ -2,12 +2,15 @@
#define SIGNATURE_H__
#include <inttypes.h>
#include <cryptopp/dsa.h>
#include <cryptopp/asn.h>
#include <cryptopp/oids.h>
#include <cryptopp/osrng.h>
#include <cryptopp/eccrypto.h>
#include "CryptoConst.h"
#include <string.h>
#include <openssl/sha.h>
#include <openssl/dsa.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include "Crypto.h"
namespace i2p
{
@@ -21,6 +24,7 @@ namespace crypto
virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0;
virtual size_t GetPublicKeyLen () const = 0;
virtual size_t GetSignatureLen () const = 0;
virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; };
};
class Signer
@@ -28,7 +32,7 @@ namespace crypto
public:
virtual ~Signer () {};
virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0;
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
};
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
@@ -40,13 +44,28 @@ namespace crypto
DSAVerifier (const uint8_t * signingKey)
{
m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH));
m_PublicKey = CreateDSA ();
m_PublicKey->pub_key = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
}
~DSAVerifier ()
{
DSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
CryptoPP::DSA::Verifier verifier (m_PublicKey);
return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH);
// calculate SHA1 digest
uint8_t digest[20];
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 verification
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
@@ -54,7 +73,7 @@ namespace crypto
private:
CryptoPP::DSA::PublicKey m_PublicKey;
DSA * m_PublicKey;
};
class DSASigner: public Signer
@@ -63,91 +82,361 @@ namespace crypto
DSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH));
m_PrivateKey = CreateDSA ();
m_PrivateKey->priv_key = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
}
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
~DSASigner ()
{
CryptoPP::DSA::Signer signer (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature);
DSA_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
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);
DSA_SIG_free(sig);
}
private:
CryptoPP::DSA::PrivateKey m_PrivateKey;
DSA * m_PrivateKey;
};
inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CryptoPP::DSA::PrivateKey privateKey;
CryptoPP::DSA::PublicKey publicKey;
privateKey.Initialize (rnd, dsap, dsaq, dsag);
privateKey.MakePublicKey (publicKey);
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
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);
}
struct SHA256Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA256 (buf, len, digest);
}
const size_t ECDSAP256_PUBLIC_KEY_LENGTH = 64;
const size_t ECDSAP256_PUBLIC_KEY_HALF_LENGTH = ECDSAP256_PUBLIC_KEY_LENGTH/2;
const size_t ECDSAP256_SIGNATURE_LENGTH = 64;
const size_t ECDSAP256_PRIVATE_KEY_LENGTH = ECDSAP256_SIGNATURE_LENGTH/2;
class ECDSAP256Verifier: public Verifier
{
enum { hashLen = 32 };
};
struct SHA384Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA384 (buf, len, digest);
}
enum { hashLen = 48 };
};
struct SHA512Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA512 (buf, len, digest);
}
enum { hashLen = 64 };
};
template<typename Hash, int curve, size_t keyLen>
class ECDSAVerifier: public Verifier
{
public:
ECDSAP256Verifier (const uint8_t * signingKey)
ECDSAVerifier (const uint8_t * signingKey)
{
m_PublicKey.Initialize (CryptoPP::ASN1::secp256r1(),
CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, ECDSAP256_PUBLIC_KEY_HALF_LENGTH),
CryptoPP::Integer (signingKey + ECDSAP256_PUBLIC_KEY_HALF_LENGTH, ECDSAP256_PUBLIC_KEY_HALF_LENGTH)));
}
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));
}
~ECDSAVerifier ()
{
EC_KEY_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::Verifier verifier (m_PublicKey);
return verifier.VerifyMessage (buf, len, signature, ECDSAP256_SIGNATURE_LENGTH);
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);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return keyLen; };
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
size_t GetPublicKeyLen () const { return ECDSAP256_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return ECDSAP256_SIGNATURE_LENGTH; };
private:
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PublicKey m_PublicKey;
};
EC_KEY * m_PublicKey;
};
class ECDSAP256Signer: public Signer
template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer
{
public:
ECDSAP256Signer (const uint8_t * signingPrivateKey)
ECDSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey.Initialize (CryptoPP::ASN1::secp256r1(), CryptoPP::Integer (signingPrivateKey, ECDSAP256_PRIVATE_KEY_LENGTH));
m_PrivateKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL));
}
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
~ECDSASigner ()
{
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::Signer signer (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature);
EC_KEY_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
// signatureLen = keyLen
bn2buf (sig->r, signature, keyLen/2);
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
ECDSA_SIG_free(sig);
}
private:
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PrivateKey m_PrivateKey;
EC_KEY * m_PrivateKey;
};
inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PrivateKey privateKey;
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PublicKey publicKey;
privateKey.Initialize (rnd, CryptoPP::ASN1::secp256r1());
privateKey.MakePublicKey (publicKey);
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, ECDSAP256_PRIVATE_KEY_LENGTH);
auto q = publicKey.GetPublicElement ();
q.x.Encode (signingPublicKey, ECDSAP256_PUBLIC_KEY_HALF_LENGTH);
q.y.Encode (signingPublicKey + ECDSAP256_PUBLIC_KEY_HALF_LENGTH, ECDSAP256_PUBLIC_KEY_HALF_LENGTH);
EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_generate_key (signingKey);
bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2);
BIGNUM * x = BN_new(), * y = BN_new();
EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey),
EC_KEY_get0_public_key (signingKey), x, y, NULL);
bn2buf (x, signingPublicKey, keyLen/2);
bn2buf (y, signingPublicKey + keyLen/2, keyLen/2);
BN_free (x); BN_free (y);
EC_KEY_free (signingKey);
}
// ECDSA_SHA256_P256
const size_t ECDSAP256_KEY_LENGTH = 64;
typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA384_P384
const size_t ECDSAP384_KEY_LENGTH = 96;
typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA512_P521
const size_t ECDSAP521_KEY_LENGTH = 132;
typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// RSA
template<typename Hash, int type, size_t keyLen>
class RSAVerifier: public Verifier
{
public:
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);
}
~RSAVerifier ()
{
RSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
return RSA_verify (type, digest, Hash::hashLen, signature, GetSignatureLen (), m_PublicKey);
}
size_t GetPublicKeyLen () const { return keyLen; }
size_t GetSignatureLen () const { return keyLen; }
size_t GetPrivateKeyLen () const { return GetSignatureLen ()*2; };
private:
RSA * m_PublicKey;
};
template<typename Hash, int type, size_t keyLen>
class RSASigner: public Signer
{
public:
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);
}
~RSASigner ()
{
RSA_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
unsigned int signatureLen = keyLen;
RSA_sign (type, digest, Hash::hashLen, signature, &signatureLen, m_PrivateKey);
}
private:
RSA * m_PrivateKey;
};
inline void CreateRSARandomKeys (size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
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);
BN_free (e); // this e is not assigned to rsa->e
RSA_free (rsa);
}
// RSA_SHA256_2048
const size_t RSASHA2562048_KEY_LENGTH = 256;
typedef RSAVerifier<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Verifier;
typedef RSASigner<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Signer;
// RSA_SHA384_3072
const size_t RSASHA3843072_KEY_LENGTH = 384;
typedef RSAVerifier<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Verifier;
typedef RSASigner<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Signer;
// RSA_SHA512_4096
const size_t RSASHA5124096_KEY_LENGTH = 512;
typedef RSAVerifier<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Verifier;
typedef RSASigner<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Signer;
// EdDSA
struct EDDSAPoint
{
BIGNUM * x, * y;
BIGNUM * z, * t; // projective coordinates
EDDSAPoint (): x(nullptr), y(nullptr), z(nullptr), t(nullptr) {};
EDDSAPoint (const EDDSAPoint& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
{ *this = other; };
EDDSAPoint (EDDSAPoint&& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
{ *this = std::move (other); };
EDDSAPoint (BIGNUM * x1, BIGNUM * y1, BIGNUM * z1 = nullptr, BIGNUM * t1 = nullptr): x(x1), y(y1), z(z1), t(t1) {};
~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); };
EDDSAPoint& operator=(EDDSAPoint&& other)
{
if (x) BN_free (x); x = other.x; other.x = nullptr;
if (y) BN_free (y); y = other.y; other.y = nullptr;
if (z) BN_free (z); z = other.z; other.z = nullptr;
if (t) BN_free (t); t = other.t; other.t = nullptr;
return *this;
}
EDDSAPoint& operator=(const EDDSAPoint& other)
{
if (x) BN_free (x); x = other.x ? BN_dup (other.x) : nullptr;
if (y) BN_free (y); y = other.y ? BN_dup (other.y) : nullptr;
if (z) BN_free (z); z = other.z ? BN_dup (other.z) : nullptr;
if (t) BN_free (t); t = other.t ? BN_dup (other.t) : nullptr;
return *this;
}
EDDSAPoint operator-() const
{
BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL;
if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); };
if (y) y1 = BN_dup (y);
if (z) z1 = BN_dup (z);
if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); };
return EDDSAPoint {x1, y1, z1, t1};
}
};
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
class EDDSA25519Verifier: public Verifier
{
public:
EDDSA25519Verifier (const uint8_t * signingKey);
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
private:
EDDSAPoint m_PublicKey;
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
class EDDSA25519Signer: public Signer
{
public:
EDDSA25519Signer (const uint8_t * signingPrivateKey);
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; };
private:
uint8_t m_ExpandedPrivateKey[64];
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,12 +3,15 @@
#include <inttypes.h>
#include <string>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <functional>
#include <memory>
#include <mutex>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "Base.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "LeaseSet.h"
@@ -39,29 +42,35 @@ namespace stream
const size_t STREAMING_MTU = 1730;
const size_t MAX_PACKET_SIZE = 4096;
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int RESEND_TIMEOUT = 10; // in seconds
const int ACK_SEND_TIMEOUT = 200; // in milliseconds
const int MAX_NUM_RESEND_ATTEMPTS = 5;
const int MAX_NUM_RESEND_ATTEMPTS = 6;
const int WINDOW_SIZE = 6; // in messages
const int MIN_WINDOW_SIZE = 1;
const int MAX_WINDOW_SIZE = 128;
const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
struct Packet
{
uint8_t buf[MAX_PACKET_SIZE];
size_t len, offset;
int numResendAttempts;
uint8_t buf[MAX_PACKET_SIZE];
uint64_t sendTime;
Packet (): len (0), offset (0), numResendAttempts (0) {};
Packet (): len (0), offset (0), sendTime (0) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return be32toh (*(uint32_t *)buf); };
uint32_t GetReceiveStreamID () const { return be32toh (*(uint32_t *)(buf + 4)); };
uint32_t GetSeqn () const { return be32toh (*(uint32_t *)(buf + 8)); };
uint32_t GetAckThrough () const { return be32toh (*(uint32_t *)(buf + 12)); };
uint32_t GetSendStreamID () const { return bufbe32toh (buf); };
uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
uint32_t GetSeqn () const { return bufbe32toh (buf + 8); };
uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); };
uint8_t GetNACKCount () const { return buf[16]; };
uint32_t GetNACK (int i) const { return be32toh (((uint32_t *)(buf + 17))[i]); };
uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); };
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags () const { return be16toh (*(uint16_t *)(GetOption () - 2)); };
uint16_t GetOptionSize () const { return be16toh (*(uint16_t *)GetOption ()); };
uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); };
uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); };
const uint8_t * GetOptionData () const { return GetOption () + 2; };
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
@@ -76,41 +85,63 @@ namespace stream
return p1->GetSeqn () < p2->GetSeqn ();
};
};
enum StreamStatus
{
eStreamStatusNew = 0,
eStreamStatusOpen,
eStreamStatusReset,
eStreamStatusClosing,
eStreamStatusClosed
};
class StreamingDestination;
class Stream
class Stream: public std::enable_shared_from_this<Stream>
{
public:
typedef std::function<void (const boost::system::error_code& ecode)> SendHandler;
Stream (boost::asio::io_service& service, StreamingDestination& local,
const i2p::data::LeaseSet& remote, int port = 0); // outgoing
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
~Stream ();
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_IsOpen; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void HandleNextPacket (Packet * packet);
size_t Send (const uint8_t * buf, size_t len);
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
void Close ();
void Cancel () { m_ReceiveTimer.cancel (); };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
size_t GetSendQueueSize () const { return m_SentPackets.size (); };
size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); };
size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); };
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
private:
void Terminate ();
void SendBuffer ();
void SendQuickAck ();
void SendClose ();
bool SendPacket (Packet * packet);
void SendPackets (const std::vector<Packet *>& packets);
@@ -119,108 +150,125 @@ namespace stream
void ProcessAck (Packet * packet);
size_t ConcatenatePackets (uint8_t * buf, size_t len);
void UpdateCurrentRemoteLease ();
void UpdateCurrentRemoteLease (bool expired = false);
template<typename Buffer, typename ReceiveHandler>
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void HandleAckSendTimer (const boost::system::error_code& ecode);
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
private:
boost::asio::io_service& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
int32_t m_LastReceivedSequenceNumber;
bool m_IsOpen, m_IsReset, m_IsAckSendScheduled;
StreamStatus m_Status;
bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
i2p::data::IdentityEx m_RemoteIdentity;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
i2p::garlic::GarlicRoutingSession * m_RoutingSession;
i2p::data::Lease m_CurrentRemoteLease;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
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;
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
std::set<Packet *, PacketCmp> m_SentPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port;
std::mutex m_SendBufferMutex;
std::stringstream m_SendBuffer;
int m_WindowSize, m_RTT, m_RTO;
uint64_t m_LastWindowSizeIncreaseTime;
int m_NumResendAttempts;
SendHandler m_SendHandler;
};
class StreamingDestination
class StreamingDestination: public std::enable_shared_from_this<StreamingDestination>
{
public:
StreamingDestination (i2p::client::ClientDestination& owner): m_Owner (owner) {};
~StreamingDestination () {};
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0, bool gzip = true);
~StreamingDestination ();
void Start ();
void Stop ();
Stream * CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port = 0);
void DeleteStream (Stream * stream);
void SetAcceptor (const std::function<void (Stream *)>& acceptor) { m_Acceptor = acceptor; };
void ResetAcceptor () { m_Acceptor = nullptr; };
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void DeleteStream (std::shared_ptr<Stream> stream);
void SetAcceptor (const Acceptor& acceptor);
void ResetAcceptor ();
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
i2p::client::ClientDestination& GetOwner () { return m_Owner; };
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_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);
private:
void HandleNextPacket (Packet * packet);
Stream * CreateNewIncomingStream ();
std::shared_ptr<Stream> CreateNewIncomingStream ();
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
private:
i2p::client::ClientDestination& m_Owner;
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
uint16_t m_LocalPort;
bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex;
std::map<uint32_t, Stream *> m_Streams;
std::function<void (Stream *)> m_Acceptor;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
Acceptor m_Acceptor;
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
public:
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
// for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
};
void DeleteStream (Stream * stream);
//-------------------------------------------------
template<typename Buffer, typename ReceiveHandler>
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
{
if (!m_ReceiveQueue.empty ())
auto s = shared_from_this();
m_Service.post ([=](void)
{
m_Service.post ([=](void) { this->HandleReceiveTimer (
boost::asio::error::make_error_code (boost::asio::error::operation_aborted),
buffer, handler); });
}
else
{
m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
{ this->HandleReceiveTimer (ecode, buffer, handler); });
}
if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset)
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler);
else
{
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
{ s->HandleReceiveTimer (ecode, buffer, handler); });
}
});
}
template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
{
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
if (ecode == boost::asio::error::operation_aborted)
if (received > 0)
handler (boost::system::error_code (), received);
else if (ecode == boost::asio::error::operation_aborted)
{
// timeout not expired
if (m_IsOpen)
// no error
handler (boost::system::error_code (), received);
if (m_Status == eStreamStatusReset)
handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0);
else
// socket closed
handler (m_IsReset ? boost::asio::error::make_error_code (boost::asio::error::connection_reset) :
boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
}
else
// timeout expired

View File

@@ -14,71 +14,96 @@ namespace tunnel
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID),
m_NextIdent (nextIdent), m_NumTransmittedBytes (0)
TunnelBase (receiveTunnelID, nextTunnelID, nextIdent)
{
m_Encryption.SetKeys (layerKey, ivKey);
}
void TransitTunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
{
m_Encryption.Encrypt (tunnelMsg->GetPayload () + 4);
m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4);
}
void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
TransitTunnelParticipant::~TransitTunnelParticipant ()
{
EncryptTunnelMsg (tunnelMsg);
}
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{
auto newMsg = CreateEmptyTunnelDataMsg ();
EncryptTunnelMsg (tunnelMsg, newMsg);
LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
*(uint32_t *)(tunnelMsg->GetPayload ()) = htobe32 (m_NextTunnelID);
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transport::transports.SendMessage (m_NextIdent, tunnelMsg);
htobe32buf (newMsg->GetPayload (), GetNextTunnelID ());
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
m_TunnelDataMsgs.push_back (newMsg);
}
void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
void TransitTunnelParticipant::FlushTunnelDataMsgs ()
{
if (!m_TunnelDataMsgs.empty ())
{
auto num = m_TunnelDataMsgs.size ();
if (num > 1)
LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num);
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs);
m_TunnelDataMsgs.clear ();
}
}
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID);
i2p::DeleteI2NPMessage (msg);
LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ());
}
void TransitTunnelGateway::SendTunnelDataMsg (i2p::I2NPMessage * msg)
void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{
LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ());
}
void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
TunnelMessageBlock block;
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendTunnelDataMsg (block);
m_Gateway.PutTunnelDataMsg (block);
}
void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
void TransitTunnelGateway::FlushTunnelDataMsgs ()
{
EncryptTunnelMsg (tunnelMsg);
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendBuffer ();
}
LogPrint ("TransitTunnel endpoint for ", GetTunnelID ());
m_Endpoint.HandleDecryptedTunnelDataMsg (tunnelMsg);
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{
auto newMsg = CreateEmptyTunnelDataMsg ();
EncryptTunnelMsg (tunnelMsg, newMsg);
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
}
TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID,
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
bool isGateway, bool isEndpoint)
{
if (isEndpoint)
{
LogPrint ("TransitTunnel endpoint: ", receiveTunnelID, " created");
return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
LogPrint (eLogInfo, "TransitTunnel: endpoint ", receiveTunnelID, " created");
return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
else if (isGateway)
{
LogPrint ("TransitTunnel gateway: ", receiveTunnelID, " created");
return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
LogPrint (eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created");
return std::make_shared<TransitTunnelGateway> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
else
{
LogPrint ("TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return new TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
}
}

View File

@@ -2,8 +2,10 @@
#define TRANSIT_TUNNEL_H__
#include <inttypes.h>
#include <vector>
#include <mutex>
#include "aes.h"
#include <memory>
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "TunnelEndpoint.h"
#include "TunnelGateway.h"
@@ -13,7 +15,7 @@ namespace i2p
{
namespace tunnel
{
class TransitTunnel: public TunnelBase // tunnel patricipant
class TransitTunnel: public TunnelBase
{
public:
@@ -21,26 +23,38 @@ namespace tunnel
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey);
virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
virtual void SendTunnelDataMsg (i2p::I2NPMessage * msg);
virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
uint32_t GetTunnelID () const { return m_TunnelID; };
virtual size_t GetNumTransmittedBytes () const { return 0; };
// implements TunnelBase
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
private:
uint32_t m_TunnelID, m_NextTunnelID;
i2p::data::IdentHash m_NextIdent;
size_t m_NumTransmittedBytes;
i2p::crypto::TunnelEncryption m_Encryption;
};
class TransitTunnelParticipant: public TransitTunnel
{
public:
TransitTunnelParticipant (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes (0) {};
~TransitTunnelParticipant ();
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
void FlushTunnelDataMsgs ();
private:
size_t m_NumTransmittedBytes;
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
};
class TransitTunnelGateway: public TransitTunnel
{
public:
@@ -51,7 +65,8 @@ namespace tunnel
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void FlushTunnelDataMsgs ();
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
private:
@@ -70,7 +85,7 @@ namespace tunnel
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
private:
@@ -78,7 +93,7 @@ namespace tunnel
TunnelEndpoint m_Endpoint;
};
TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID,
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
bool isGateway, bool isEndpoint);

View File

@@ -3,24 +3,26 @@
#include <inttypes.h>
#include <iostream>
#include <memory>
#include <vector>
#include "Identity.h"
#include "Crypto.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
namespace i2p
{
namespace transport
{
struct DHKeysPair // transient keys for transport sessions
{
uint8_t publicKey[256];
uint8_t privateKey[256];
};
class SignedData
{
public:
SignedData () {};
SignedData () {}
SignedData (const SignedData& other)
{
m_Stream << other.m_Stream.rdbuf ();
}
void Insert (const uint8_t * buf, size_t len)
{
m_Stream.write ((char *)buf, len);
@@ -32,9 +34,9 @@ namespace transport
m_Stream.write ((char *)&t, sizeof (T));
}
bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
{
return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
}
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
@@ -51,23 +53,31 @@ namespace transport
{
public:
TransportSession (const i2p::data::RouterInfo * in_RemoteRouter):
m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr)
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router)
{
if (m_RemoteRouter)
m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity ();
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
}
virtual ~TransportSession () { delete m_DHKeysPair; };
virtual ~TransportSession () {};
virtual void Done () = 0;
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
const i2p::data::RouterInfo * GetRemoteRouter () { return m_RemoteRouter; };
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; };
protected:
const i2p::data::RouterInfo * m_RemoteRouter;
i2p::data::IdentityEx m_RemoteIdentity;
DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
};
}
}

View File

@@ -1,7 +1,6 @@
#include <cryptopp/dh.h>
#include <boost/bind.hpp>
#include <openssl/dh.h>
#include "Log.h"
#include "CryptoConst.h"
#include "Crypto.h"
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "NetDb.h"
@@ -57,37 +56,36 @@ namespace transport
{
if (num > 0)
{
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
i2p::crypto::DHKeys dh;
for (int i = 0; i < num; i++)
{
i2p::transport::DHKeysPair * pair = new i2p::transport::DHKeysPair ();
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey);
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
}
}
DHKeysPair * DHKeysPairSupplier::Acquire ()
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
{
if (!m_Queue.empty ())
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
auto pair = m_Queue.front ();
m_Queue.pop ();
m_Acquired.notify_one ();
return pair;
if (!m_Queue.empty ())
{
auto pair = m_Queue.front ();
m_Queue.pop ();
m_Acquired.notify_one ();
return pair;
}
}
else // queue is empty, create new
{
DHKeysPair * pair = new DHKeysPair ();
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey);
return pair;
}
// queue is empty, create new
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
return pair;
}
void DHKeysPairSupplier::Return (DHKeysPair * pair)
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
@@ -96,8 +94,10 @@ namespace transport
Transports transports;
Transports::Transports ():
m_Thread (nullptr), m_Work (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr),
m_SSUServer (nullptr), m_DHKeysPairSupplier (5) // 5 pre-generated keys
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
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)
{
}
@@ -115,61 +115,45 @@ namespace transport
auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses)
{
if (address.transportStyle == RouterInfo::eTransportNTCP && address.host.is_v4 ())
if (!m_NTCPServer)
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port));
LogPrint ("Start listening TCP port ", address.port);
auto conn = new NTCPServerConnection (m_Service);
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
conn, boost::asio::placeholders::error));
if (context.SupportsV6 ())
{
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 ("Start listening V6 TCP port ", address.port);
auto conn = new NTCPServerConnection (m_Service);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6,
this, conn, boost::asio::placeholders::error));
}
m_NTCPServer = new NTCPServer ();
m_NTCPServer->Start ();
}
else if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
{
if (!m_SSUServer)
{
m_SSUServer = new SSUServer (address.port);
LogPrint ("Start listening UDP port ", address.port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address.port);
m_SSUServer->Start ();
DetectExternalIP ();
}
else
LogPrint ("SSU server already exists");
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));
}
void Transports::Stop ()
{
m_PeerCleanupTimer.cancel ();
m_Peers.clear ();
if (m_SSUServer)
{
m_SSUServer->Stop ();
delete m_SSUServer;
m_SSUServer = nullptr;
}
for (auto session: m_NTCPSessions)
delete session.second;
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
if (m_NTCPServer)
{
m_NTCPServer->Stop ();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
m_DHKeysPairSupplier.Stop ();
m_IsRunning = false;
@@ -192,187 +176,411 @@ namespace transport
}
catch (std::exception& ex)
{
LogPrint ("Transports: ", ex.what ());
LogPrint (eLogError, "Transports: runtime exception: ", ex.what ());
}
}
}
void Transports::AddNTCPSession (NTCPSession * session)
void Transports::UpdateBandwidth ()
{
if (session)
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
}
void Transports::RemoveNTCPSession (NTCPSession * session)
{
if (session)
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
}
void Transports::HandleAccept (NTCPServerConnection * conn, const boost::system::error_code& error)
{
if (!error)
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_LastBandwidthUpdateTime > 0)
{
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
conn->ServerLogin ();
auto delta = ts - m_LastBandwidthUpdateTime;
if (delta > 0)
{
m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second
m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second
}
}
else
delete conn;
if (error != boost::asio::error::operation_aborted)
{
conn = new NTCPServerConnection (m_Service);
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
conn, boost::asio::placeholders::error));
}
m_LastBandwidthUpdateTime = ts;
m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes;
m_LastOutBandwidthUpdateBytes = m_TotalSentBytes;
}
void Transports::HandleAcceptV6 (NTCPServerConnection * conn, const boost::system::error_code& error)
{
if (!error)
{
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
conn->ServerLogin ();
}
else
delete conn;
if (error != boost::asio::error::operation_aborted)
{
conn = new NTCPServerConnection (m_Service);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6, this,
conn, boost::asio::placeholders::error));
}
bool Transports::IsBandwidthExceeded () const
{
if (i2p::context.GetRouterInfo ().IsExtraBandwidth ()) return false;
auto bw = std::max (m_InBandwidth, m_OutBandwidth);
return bw > (i2p::context.GetRouterInfo ().IsHighBandwidth () ? HIGH_BANDWIDTH_LIMIT : LOW_BANDWIDTH_LIMIT);
}
NTCPSession * Transports::GetNextNTCPSession ()
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
{
for (auto session: m_NTCPSessions)
if (session.second->IsEstablished ())
return session.second;
return 0;
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
}
NTCPSession * Transports::FindNTCPSession (const i2p::data::IdentHash& ident)
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
{
auto it = m_NTCPSessions.find (ident);
if (it != m_NTCPSessions.end ())
return it->second;
return 0;
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
}
void Transports::SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs)
{
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
{
// we send it to ourself
i2p::HandleI2NPMessage (msg);
else
m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
}
void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
{
auto session = FindNTCPSession (ident);
if (session)
session->SendI2NPMessage (msg);
else
for (auto it: msgs)
i2p::HandleI2NPMessage (it);
return;
}
auto it = m_Peers.find (ident);
if (it == m_Peers.end ())
{
RouterInfo * r = netdb.FindRouter (ident);
if (r)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (r) : nullptr;
if (ssuSession)
ssuSession->SendI2NPMessage (msg);
else
{
// existing session not found. create new
// try NTCP first if message size < 16K
auto address = r->GetNTCPAddress (!context.SupportsV6 ());
if (address && !r->UsesIntroducer () && !r->IsUnreachable () && msg->GetLength () < NTCP_MAX_MESSAGE_SIZE)
{
auto s = new NTCPClient (m_Service, address->host, address->port, *r);
AddNTCPSession (s);
s->SendI2NPMessage (msg);
}
else
{
// then SSU
auto s = m_SSUServer ? m_SSUServer->GetSession (r) : nullptr;
if (s)
s->SendI2NPMessage (msg);
else
bool connected = false;
try
{
auto r = netdb.FindRouter (ident);
{
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;
}
connected = ConnectToPeer (ident, it->second);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ());
}
if (!connected) return;
}
if (!it->second.sessions.empty ())
it->second.sessions.front ()->SendI2NPMessages (msgs);
else
{
for (auto it1: msgs)
it->second.delayedMessages.push_back (it1);
}
}
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
{
if (peer.router) // we have RI already
{
if (!peer.numAttempts) // NTCP
{
peer.numAttempts++;
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
if (address)
{
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
#else
boost::system::error_code ecode;
address->host.to_string (ecode);
if (!ecode)
#endif
{
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address->host, address->port, s);
return true;
}
}
else // we don't have address
{
if (address->addressString.length () > 0) // trying to resolve
{
LogPrint ("No NTCP and SSU addresses available");
DeleteI2NPMessage (msg);
LogPrint (eLogDebug, "Transports: Resolving NTCP ", address->addressString);
NTCPResolve (address->addressString, ident);
return true;
}
}
}
else
LogPrint (eLogWarning, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
}
if (peer.numAttempts == 1)// SSU
{
peer.numAttempts++;
if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ()))
{
auto address = peer.router->GetSSUAddress (!context.SupportsV6 ());
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
#else
boost::system::error_code ecode;
address->host.to_string (ecode);
if (!ecode)
#endif
{
m_SSUServer->CreateSession (peer.router, address->host, address->port);
return true;
}
else // we don't have address
{
if (address->addressString.length () > 0) // trying to resolve
{
LogPrint (eLogDebug, "Transports: Resolving SSU ", address->addressString);
SSUResolve (address->addressString, ident);
return true;
}
}
}
}
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
return false;
}
else // otherwise request RI
{
LogPrint (eLogInfo, "Transports: RouterInfo for ", ident.ToBase64 (), " not found, requested");
i2p::data::netdb.RequestDestination (ident, std::bind (
&Transports::RequestComplete, this, std::placeholders::_1, ident));
}
return true;
}
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));
}
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident)
{
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
if (r)
{
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, Trying to connect");
it->second.router = r;
ConnectToPeer (ident, it->second);
}
else
{
LogPrint ("Router not found. Requested");
i2p::data::netdb.RequestDestination (ident);
auto resendTimer = new boost::asio::deadline_timer (m_Service);
resendTimer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
resendTimer->async_wait (boost::bind (&Transports::HandleResendTimer,
this, boost::asio::placeholders::error, resendTimer, ident, msg));
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
}
void Transports::HandleResendTimer (const boost::system::error_code& ecode,
boost::asio::deadline_timer * timer, const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident)
{
RouterInfo * r = netdb.FindRouter (ident);
if (r)
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));
}
void Transports::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)
{
auto it1 = m_Peers.find (ident);
if (it1 != m_Peers.end ())
{
LogPrint ("Router found. Sending message");
PostMessage (ident, msg);
}
else
auto& peer = it1->second;
if (!ecode && peer.router)
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetNTCPAddress ();
if (addr)
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address, addr->port, s);
return;
}
}
LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ());
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);
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
std::bind (&Transports::HandleSSUResolve, this,
std::placeholders::_1, std::placeholders::_2, ident, resolver));
}
void Transports::HandleSSUResolve (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)
{
auto it1 = m_Peers.find (ident);
if (it1 != m_Peers.end ())
{
LogPrint ("Router not found. Failed to send message");
DeleteI2NPMessage (msg);
}
delete timer;
}
void Transports::CloseSession (const i2p::data::RouterInfo * router)
auto& peer = it1->second;
if (!ecode && peer.router)
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetSSUAddress (!context.SupportsV6 ());;
if (addr)
{
m_SSUServer->CreateSession (peer.router, address, addr->port);
return;
}
}
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it1);
}
}
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service.post (boost::bind (&Transports::PostCloseSession, this, router));
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (const i2p::data::RouterInfo * router)
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
if (ssuSession) // try SSU first
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint ("SSU session closed");
LogPrint (eLogDebug, "Transports: SSU session closed");
}
// TODO: delete NTCP
}
void Transports::DetectExternalIP ()
{
for (int i = 0; i < 5; i++)
if (m_SSUServer)
{
auto router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU () && m_SSUServer)
m_SSUServer->GetSession (router, true); // peer test
}
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
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
}
}
}
else
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
}
DHKeysPair * Transports::GetNextDHKeysPair ()
void Transports::PeerTest ()
{
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 ()))
{
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, true); // peer test
}
}
}
}
std::shared_ptr<i2p::crypto::DHKeys> Transports::GetNextDHKeysPair ()
{
return m_DHKeysPairSupplier.Acquire ();
}
void Transports::ReuseDHKeysPair (DHKeysPair * pair)
void Transports::ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
m_DHKeysPairSupplier.Return (pair);
}
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
{
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 ())
{
it->second.sessions.push_back (session);
session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear ();
}
else // incoming connection
{
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]()
{
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash ();
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
it->second.sessions.remove (session);
if (it->second.sessions.empty ()) // TODO: why?
{
if (it->second.delayedMessages.size () > 0)
ConnectToPeer (ident, it->second);
else
{
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
}
});
}
bool Transports::IsConnected (const i2p::data::IdentHash& ident) const
{
std::unique_lock<std::mutex> l(m_PeersMutex);
auto it = m_Peers.find (ident);
return it != m_Peers.end ();
}
void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Peers.begin (); it != m_Peers.end (); )
{
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);
it = m_Peers.erase (it);
}
else
it++;
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
DetectExternalIP ();
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
}
}
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{
if (!m_Peers.size ()) return nullptr;
std::unique_lock<std::mutex> l(m_PeersMutex);
auto it = m_Peers.begin ();
std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr;
}
}
}

View File

@@ -6,9 +6,11 @@
#include <condition_variable>
#include <functional>
#include <map>
#include <vector>
#include <queue>
#include <string>
#include <cryptopp/osrng.h>
#include <memory>
#include <atomic>
#include <boost/asio.hpp>
#include "TransportSession.h"
#include "NTCPSession.h"
@@ -29,8 +31,8 @@ namespace transport
~DHKeysPairSupplier ();
void Start ();
void Stop ();
DHKeysPair * Acquire ();
void Return (DHKeysPair * pair);
std::shared_ptr<i2p::crypto::DHKeys> Acquire ();
void Return (std::shared_ptr<i2p::crypto::DHKeys> pair);
private:
@@ -40,15 +42,32 @@ namespace transport
private:
const int m_QueueSize;
std::queue<DHKeysPair *> m_Queue;
std::queue<std::shared_ptr<i2p::crypto::DHKeys> > m_Queue;
bool m_IsRunning;
std::thread * m_Thread;
std::condition_variable m_Acquired;
std::mutex m_AcquiredMutex;
CryptoPP::AutoSeededRandomPool m_Rnd;
};
struct Peer
{
int numAttempts;
std::shared_ptr<const i2p::data::RouterInfo> router;
std::list<std::shared_ptr<TransportSession> > sessions;
uint64_t creationTime;
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
void Done ()
{
for (auto it: sessions)
it->Done ();
}
};
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
const uint32_t LOW_BANDWIDTH_LIMIT = 32*1024; // 32KBs
const uint32_t HIGH_BANDWIDTH_LIMIT = 256*1024; // 256KBs
class Transports
{
public:
@@ -60,28 +79,47 @@ namespace transport
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
i2p::transport::DHKeysPair * GetNextDHKeysPair ();
void ReuseDHKeysPair (DHKeysPair * pair);
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
void AddNTCPSession (NTCPSession * session);
void RemoveNTCPSession (NTCPSession * session);
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
void PeerConnected (std::shared_ptr<TransportSession> session);
void PeerDisconnected (std::shared_ptr<TransportSession> session);
bool IsConnected (const i2p::data::IdentHash& ident) const;
NTCPSession * GetNextNTCPSession ();
NTCPSession * FindNTCPSession (const i2p::data::IdentHash& ident);
void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; };
void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; };
uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; };
uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; };
uint32_t GetInBandwidth () const { return m_InBandwidth; }; // bytes per second
uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; // bytes per second
bool IsBandwidthExceeded () const;
size_t GetNumPeers () const { return m_Peers.size (); };
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void CloseSession (const i2p::data::RouterInfo * router);
void PeerTest ();
private:
void Run ();
void HandleAccept (NTCPServerConnection * conn, const boost::system::error_code& error);
void HandleAcceptV6 (NTCPServerConnection * conn, const boost::system::error_code& error);
void HandleResendTimer (const boost::system::error_code& ecode, boost::asio::deadline_timer * timer,
const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void PostCloseSession (const i2p::data::RouterInfo * router);
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void 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);
void SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident);
void HandleSSUResolve (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);
void UpdateBandwidth ();
void DetectExternalIP ();
private:
@@ -90,18 +128,26 @@ namespace transport
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
boost::asio::deadline_timer m_PeerCleanupTimer;
std::map<i2p::data::IdentHash, NTCPSession *> m_NTCPSessions;
NTCPServer * m_NTCPServer;
SSUServer * m_SSUServer;
mutable std::mutex m_PeersMutex;
std::map<i2p::data::IdentHash, Peer> m_Peers;
DHKeysPairSupplier m_DHKeysPairSupplier;
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes;
uint32_t m_InBandwidth, m_OutBandwidth;
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
uint64_t m_LastBandwidthUpdateTime;
public:
// for HTTP only
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
const NTCPServer * GetNTCPServer () const { return m_NTCPServer; };
const SSUServer * GetSSUServer () const { return m_SSUServer; };
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
};
extern Transports transports;

View File

@@ -1,8 +1,9 @@
#include <string.h>
#include "I2PEndian.h"
#include <thread>
#include <algorithm>
#include <vector>
#include <cryptopp/sha.h>
#include <openssl/rand.h>
#include "RouterContext.h"
#include "Log.h"
#include "Timestamp.h"
@@ -16,24 +17,23 @@ namespace i2p
namespace tunnel
{
Tunnel::Tunnel (TunnelConfig * config):
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending)
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)
{
}
Tunnel::~Tunnel ()
{
delete m_Config;
}
void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel)
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
auto numHops = m_Config->GetNumHops ();
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
I2NPMessage * msg = NewI2NPMessage ();
auto msg = NewI2NPShortMessage ();
*msg->GetPayload () = numRecords;
msg->len += numRecords*sizeof (I2NPBuildRequestRecordElGamalEncrypted) + 1;
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
// shuffle records
std::vector<int> recordIndicies;
@@ -41,22 +41,18 @@ namespace tunnel
std::random_shuffle (recordIndicies.begin(), recordIndicies.end());
// create real records
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(msg->GetPayload () + 1);
uint8_t * records = msg->GetPayload () + 1;
TunnelHopConfig * hop = m_Config->GetFirstHop ();
int i = 0;
while (hop)
{
uint32_t msgID;
if (hop->next) // we set replyMsgID for last hop only
RAND_bytes ((uint8_t *)&msgID, 4);
else
msgID = replyMsgID;
int idx = recordIndicies[i];
EncryptBuildRequestRecord (*hop->router,
CreateBuildRequestRecord (hop->router->GetIdentHash (),
hop->tunnelID,
hop->nextRouter->GetIdentHash (),
hop->nextTunnelID,
hop->layerKey, hop->ivKey,
hop->replyKey, hop->replyIV,
hop->next ? rnd.GenerateWord32 () : replyMsgID, // we set replyMsgID for last hop only
hop->isGateway, hop->isEndpoint),
records[idx]);
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID);
hop->recordIndex = idx;
i++;
hop = hop->next;
@@ -65,7 +61,7 @@ namespace tunnel
for (int i = numHops; i < numRecords; i++)
{
int idx = recordIndicies[i];
rnd.GenerateBlock ((uint8_t *)(records + idx), sizeof (records[idx]));
RAND_bytes (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE);
}
// decrypt real records
@@ -79,14 +75,13 @@ namespace tunnel
while (hop1)
{
decryption.SetIV (hop->replyIV);
decryption.Decrypt((uint8_t *)&records[hop1->recordIndex],
sizeof (I2NPBuildRequestRecordElGamalEncrypted),
(uint8_t *)&records[hop1->recordIndex]);
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
hop1 = hop1->next;
}
hop = hop->prev;
}
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild);
// send message
if (outboundTunnel)
@@ -97,7 +92,7 @@ namespace tunnel
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
{
LogPrint ("TunnelBuildResponse ", (int)msg[0], " records.");
LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records.");
i2p::crypto::CBCDecryption decryption;
TunnelHopConfig * hop = m_Config->GetLastHop ();
@@ -111,12 +106,12 @@ namespace tunnel
auto idx = hop1->recordIndex;
if (idx >= 0 && idx < msg[0])
{
uint8_t * record = msg + 1 + idx*sizeof (I2NPBuildResponseRecord);
uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE;
decryption.SetIV (hop->replyIV);
decryption.Decrypt(record, sizeof (I2NPBuildResponseRecord), record);
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
}
else
LogPrint ("Tunnel hop index ", idx, " is out of range");
LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range");
hop1 = hop1->prev;
}
hop = hop->prev;
@@ -126,47 +121,112 @@ namespace tunnel
hop = m_Config->GetFirstHop ();
while (hop)
{
I2NPBuildResponseRecord * record = (I2NPBuildResponseRecord *)(msg + 1 + hop->recordIndex*sizeof (I2NPBuildResponseRecord));
LogPrint ("Ret code=", (int)record->ret);
if (record->ret)
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);
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelBuildResponse (ret);
if (ret)
// if any of participants declined the tunnel is not established
established = false;
hop = hop->next;
}
if (established)
{
// change reply keys to layer keys
hop = m_Config->GetFirstHop ();
// create tunnel decryptions from layer and iv keys in reverse order
hop = m_Config->GetLastHop ();
while (hop)
{
hop->decryption.SetKeys (hop->layerKey, hop->ivKey);
hop = hop->next;
auto tunnelHop = new TunnelHop;
tunnelHop->ident = hop->ident;
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;
}
void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
{
uint8_t * payload = tunnelMsg->GetPayload () + 4;
TunnelHopConfig * hop = m_Config->GetLastHop ();
while (hop)
{
hop->decryption.Decrypt (payload);
hop = hop->prev;
}
}
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg)
{
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
msg->from = this;
EncryptTunnelMsg (msg);
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
const uint8_t * inPayload = in->GetPayload () + 4;
uint8_t * outPayload = out->GetPayload () + 4;
for (auto& it: m_Hops)
{
it->decryption.Decrypt (inPayload, outPayload);
inPayload = outPayload;
}
}
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
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 ());
return peers;
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetInvertedPeers () const
{
// hops are in inverted order
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > ret;
for (auto& it: m_Hops)
ret.push_back (it->ident);
return ret;
}
void Tunnel::PrintHops (std::stringstream& s) const
{
for (auto& it: m_Hops)
{
s << "";
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
auto newMsg = CreateEmptyTunnelDataMsg ();
EncryptTunnelMsg (msg, newMsg);
newMsg->from = shared_from_this ();
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
}
void InboundTunnel::Print (std::stringstream& s) const
{
PrintHops (s);
s << "" << 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 ();
HandleI2NPMessage (msg);
}
}
void ZeroHopsInboundTunnel::Print (std::stringstream& s) const
{
s << "" << GetTunnelID () << ":me";
}
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
{
TunnelMessageBlock block;
if (gwHash)
@@ -184,8 +244,7 @@ namespace tunnel
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendTunnelDataMsg (block);
SendTunnelDataMsg ({block});
}
void OutboundTunnel::SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs)
@@ -196,55 +255,84 @@ namespace tunnel
m_Gateway.SendBuffer ();
}
void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{
LogPrint (eLogError, "Tunnel: incoming message for outbound tunnel ", GetTunnelID ());
}
void OutboundTunnel::Print (std::stringstream& s) const
{
s << GetTunnelID () << ":me";
PrintHops (s);
s << "";
}
ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel ():
OutboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
m_NumSentBytes (0)
{
}
void ZeroHopsOutboundTunnel::SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs)
{
for (auto& msg : msgs)
{
switch (msg.deliveryType)
{
case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data);
break;
case eDeliveryTypeTunnel:
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
break;
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 ⇒ ";
}
Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_ExploratoryPool (nullptr)
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0)
{
}
Tunnels::~Tunnels ()
{
for (auto& it : m_OutboundTunnels)
delete it;
m_OutboundTunnels.clear ();
for (auto& it : m_InboundTunnels)
delete it.second;
m_InboundTunnels.clear ();
for (auto& it : m_TransitTunnels)
delete it.second;
m_TransitTunnels.clear ();
/*for (auto& it : m_PendingTunnels)
delete it.second;
m_PendingTunnels.clear ();*/
for (auto& it: m_Pools)
delete it.second;
m_Pools.clear ();
}
InboundTunnel * Tunnels::GetInboundTunnel (uint32_t tunnelID)
std::shared_ptr<TunnelBase> Tunnels::GetTunnel (uint32_t tunnelID)
{
auto it = m_InboundTunnels.find(tunnelID);
if (it != m_InboundTunnels.end ())
return it->second;
return nullptr;
}
TransitTunnel * Tunnels::GetTransitTunnel (uint32_t tunnelID)
{
auto it = m_TransitTunnels.find(tunnelID);
if (it != m_TransitTunnels.end ())
auto it = m_Tunnels.find(tunnelID);
if (it != m_Tunnels.end ())
return it->second;
return nullptr;
}
Tunnel * Tunnels::GetPendingTunnel (uint32_t replyMsgID)
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
{
auto it = m_PendingTunnels.find(replyMsgID);
if (it != m_PendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
}
std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID)
{
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
}
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);
return it->second;
@@ -252,29 +340,27 @@ namespace tunnel
return nullptr;
}
InboundTunnel * Tunnels::GetNextInboundTunnel ()
std::shared_ptr<InboundTunnel> Tunnels::GetNextInboundTunnel ()
{
InboundTunnel * tunnel = nullptr;
std::shared_ptr<InboundTunnel> tunnel;
size_t minReceived = 0;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
{
if (!it.second->IsEstablished ()) continue;
if (!tunnel || it.second->GetNumReceivedBytes () < minReceived)
if (!it->IsEstablished ()) continue;
if (!tunnel || it->GetNumReceivedBytes () < minReceived)
{
tunnel = it.second;
minReceived = it.second->GetNumReceivedBytes ();
tunnel = it;
minReceived = it->GetNumReceivedBytes ();
}
}
return tunnel;
}
OutboundTunnel * Tunnels::GetNextOutboundTunnel ()
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
OutboundTunnel * tunnel = nullptr;
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
if (!m_OutboundTunnels.size ()) return nullptr;
uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0;
std::shared_ptr<OutboundTunnel> tunnel;
for (auto it: m_OutboundTunnels)
{
if (it->IsEstablished ())
@@ -287,28 +373,28 @@ namespace tunnel
return tunnel;
}
TunnelPool * Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination& localDestination, int numHops)
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops,
int numOutboundHops, int numInboundTunnels, int numOutboundTunnels)
{
auto pool = new TunnelPool (localDestination, numHops);
auto pool = std::make_shared<TunnelPool> (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels);
std::unique_lock<std::mutex> l(m_PoolsMutex);
m_Pools[pool->GetIdentHash ()] = pool;
m_Pools.push_back (pool);
return pool;
}
void Tunnels::DeleteTunnelPool (TunnelPool * pool)
void Tunnels::DeleteTunnelPool (std::shared_ptr<TunnelPool> pool)
{
if (pool)
{
StopTunnelPool (pool);
m_Pools.erase (pool->GetLocalDestination ().GetIdentHash ());
for (auto it: m_PendingTunnels)
if (it.second->GetTunnelPool () == pool)
it.second->SetTunnelPool (nullptr);
delete pool;
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
m_Pools.remove (pool);
}
}
}
void Tunnels::StopTunnelPool (TunnelPool * pool)
void Tunnels::StopTunnelPool (std::shared_ptr<TunnelPool> pool)
{
if (pool)
{
@@ -317,10 +403,12 @@ namespace tunnel
}
}
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
void Tunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel)
{
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
m_TransitTunnels[tunnel->GetTunnelID ()] = 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 ()
@@ -350,25 +438,59 @@ namespace tunnel
{
try
{
I2NPMessage * msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
while (msg)
{
uint32_t tunnelID = be32toh (*(uint32_t *)msg->GetPayload ());
InboundTunnel * tunnel = GetInboundTunnel (tunnelID);
if (tunnel)
tunnel->HandleTunnelDataMsg (msg);
else
{
TransitTunnel * transitTunnel = GetTransitTunnel (tunnelID);
if (transitTunnel)
transitTunnel->HandleTunnelDataMsg (msg);
else
{
LogPrint ("Tunnel ", tunnelID, " not found");
i2p::DeleteI2NPMessage (msg);
}
}
msg = m_Queue.Get ();
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
if (msg)
{
uint32_t prevTunnelID = 0, tunnelID = 0;
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)
{
if (typeID == eI2NPTunnelData)
tunnel->HandleTunnelDataMsg (msg);
else // tunnel gateway assumed
HandleTunnelGatewayMsg (tunnel, msg);
}
else
LogPrint (eLogWarning, "Tunnel: tunnel with id ", tunnelID, " not found");
break;
}
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
break;
default:
LogPrint (eLogWarning, "Tunnel: unexpected messsage type ", (int) typeID);
}
msg = m_Queue.Get ();
if (msg)
{
prevTunnelID = tunnelID;
prevTunnel = tunnel;
}
else if (tunnel)
tunnel->FlushTunnelDataMsgs ();
}
while (msg);
}
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
@@ -380,11 +502,38 @@ namespace tunnel
}
catch (std::exception& ex)
{
LogPrint ("Tunnels: ", ex.what ());
LogPrint (eLogError, "Tunnel: runtime exception: ", ex.what ());
}
}
}
void Tunnels::HandleTunnelGatewayMsg (std::shared_ptr<TunnelBase> tunnel, std::shared_ptr<I2NPMessage> msg)
{
if (!tunnel)
{
LogPrint (eLogError, "Tunnel: missing tunnel for gateway");
return;
}
const uint8_t * payload = msg->GetPayload ();
uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET);
// we make payload as new I2NP message to send
msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
if (msg->offset + len > msg->len)
{
LogPrint (eLogError, "Tunnel: gateway payload ", (int)len, " exceeds message length ", (int)msg->len);
return;
}
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
i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg));
tunnel->SendTunnelDataMsg (msg);
}
void Tunnels::ManageTunnels ()
{
ManagePendingTunnels ();
@@ -395,10 +544,17 @@ namespace tunnel
}
void Tunnels::ManagePendingTunnels ()
{
ManagePendingTunnels (m_PendingInboundTunnels);
ManagePendingTunnels (m_PendingOutboundTunnels);
}
template<class PendingTunnels>
void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels)
{
// check pending tunnel. delete failed or timeout
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_PendingTunnels.begin (); it != m_PendingTunnels.end ();)
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
{
auto tunnel = it->second;
switch (tunnel->GetState ())
@@ -406,24 +562,43 @@ namespace tunnel
case eTunnelStatePending:
if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT)
{
LogPrint ("Pending tunnel build request ", it->first, " timeout. Deleted");
delete tunnel;
it = m_PendingTunnels.erase (it);
LogPrint (eLogWarning, "Tunnel: pending build request ", it->first, " timeout, deleted");
// update stats
auto config = tunnel->GetTunnelConfig ();
if (config)
{
auto hop = config->GetFirstHop ();
while (hop)
{
if (hop->ident)
{
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelNonReplied ();
}
hop = hop->next;
}
}
// delete
it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++;
}
else
it++;
break;
case eTunnelStateBuildFailed:
LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted");
delete tunnel;
it = m_PendingTunnels.erase (it);
LogPrint (eLogWarning, "Tunnel: pending build request ", it->first, " failed, deleted");
it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++;
break;
case eTunnelStateBuildReplyReceived:
// intermidiate state, will be either established of build failed
// intermediate state, will be either established of build failed
it++;
break;
default:
it = m_PendingTunnels.erase (it);
// success
it = pendingTunnels.erase (it);
m_NumSuccesiveTunnelCreations++;
}
}
}
@@ -437,23 +612,27 @@ namespace tunnel
auto tunnel = *it;
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired");
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
}
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
it = m_OutboundTunnels.erase (it);
}
delete tunnel;
LogPrint (eLogDebug, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " expired");
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
// we don't have outbound tunnels in m_Tunnels
it = m_OutboundTunnels.erase (it);
}
else
{
if (tunnel->IsEstablished () && ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
if (tunnel->IsEstablished ())
{
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
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++;
}
}
@@ -462,15 +641,14 @@ namespace tunnel
if (m_OutboundTunnels.size () < 5)
{
// trying to create one more oubound tunnel
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel) return;
LogPrint ("Creating one hop outbound tunnel...");
auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::data::netdb.GetRandomRouter ();
if (!inboundTunnel || !router) return;
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
CreateTunnel<OutboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomRouter ()
},
inboundTunnel->GetTunnelConfig ()));
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())
);
}
}
@@ -480,26 +658,31 @@ namespace tunnel
{
for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();)
{
auto tunnel = it->second;
auto tunnel = *it;
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired");
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
}
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
it = m_InboundTunnels.erase (it);
}
delete tunnel;
LogPrint (eLogDebug, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " expired");
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
m_Tunnels.erase (tunnel->GetTunnelID ());
it = m_InboundTunnels.erase (it);
}
else
{
if (tunnel->IsEstablished () && ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
if (tunnel->IsEstablished ())
{
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
tunnel->SetIsRecreated ();
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->RecreateInboundTunnel (tunnel);
}
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
}
it++;
}
}
@@ -507,22 +690,29 @@ namespace tunnel
if (m_InboundTunnels.empty ())
{
LogPrint ("Creating zero hops inbound tunnel...");
LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel");
CreateZeroHopsInboundTunnel ();
CreateZeroHopsOutboundTunnel ();
if (!m_ExploratoryPool)
m_ExploratoryPool = CreateTunnelPool (i2p::context, 2); // 2-hop exploratory
{
m_ExploratoryPool = CreateTunnelPool (2, 2, 5, 5); // 2-hop exploratory, 5 tunnels
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
}
return;
}
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
{
// trying to create one more inbound tunnel
LogPrint ("Creating one hop inbound tunnel...");
// trying to create one more inbound tunnel
auto router = i2p::data::netdb.GetRandomRouter ();
if (!router) {
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
return;
}
LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel");
CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
i2p::data::netdb.GetRandomRouter ()
}));
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () })
);
}
}
@@ -531,15 +721,12 @@ namespace tunnel
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
{
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
auto tunnel = *it;
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired");
auto tmp = it->second;
{
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
it = m_TransitTunnels.erase (it);
}
delete tmp;
LogPrint (eLogDebug, "Tunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired");
m_Tunnels.erase (tunnel->GetTunnelID ());
it = m_TransitTunnels.erase (it);
}
else
it++;
@@ -551,8 +738,8 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_PoolsMutex);
for (auto it: m_Pools)
{
TunnelPool * pool = it.second;
if (pool->IsActive ())
auto pool = it;
if (pool && pool->IsActive ())
{
pool->CreateTunnels ();
pool->TestTunnels ();
@@ -560,24 +747,40 @@ namespace tunnel
}
}
void Tunnels::PostTunnelData (I2NPMessage * msg)
void Tunnels::PostTunnelData (std::shared_ptr<I2NPMessage> msg)
{
if (msg) m_Queue.Put (msg);
}
template<class TTunnel>
TTunnel * Tunnels::CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel)
void Tunnels::PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
TTunnel * newTunnel = new TTunnel (config);
uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
m_PendingTunnels[replyMsgID] = newTunnel;
m_Queue.Put (msgs);
}
template<class TTunnel>
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
auto newTunnel = std::make_shared<TTunnel> (config);
uint32_t replyMsgID;
RAND_bytes ((uint8_t *)&replyMsgID, 4);
AddPendingTunnel (replyMsgID, newTunnel);
newTunnel->Build (replyMsgID, outboundTunnel);
return newTunnel;
}
void Tunnels::AddOutboundTunnel (OutboundTunnel * newTunnel)
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_PendingInboundTunnels[replyMsgID] = tunnel;
}
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel)
{
m_PendingOutboundTunnels[replyMsgID] = tunnel;
}
void Tunnels::AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel)
{
// we don't need to insert it to m_Tunnels
m_OutboundTunnels.push_back (newTunnel);
auto pool = newTunnel->GetTunnelPool ();
if (pool && pool->IsActive ())
@@ -586,33 +789,78 @@ namespace tunnel
newTunnel->SetTunnelPool (nullptr);
}
void Tunnels::AddInboundTunnel (InboundTunnel * newTunnel)
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel)
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel;
auto pool = newTunnel->GetTunnelPool ();
if (!pool)
{
// build symmetric outbound tunnel
CreateTunnel<OutboundTunnel> (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ());
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 ());
}
else
{
if (pool->IsActive ())
pool->TunnelCreated (newTunnel);
else
newTunnel->SetTunnelPool (nullptr);
}
}
else
{
if (pool->IsActive ())
pool->TunnelCreated (newTunnel);
else
newTunnel->SetTunnelPool (nullptr);
}
LogPrint (eLogError, "Tunnel: tunnel with id ", newTunnel->GetTunnelID (), " already exists");
}
void Tunnels::CreateZeroHopsInboundTunnel ()
{
CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
&i2p::context.GetRouterInfo ()
}));
auto inboundTunnel = std::make_shared<ZeroHopsInboundTunnel> ();
inboundTunnel->SetState (eTunnelStateEstablished);
m_InboundTunnels.push_back (inboundTunnel);
m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel;
}
void Tunnels::CreateZeroHopsOutboundTunnel ()
{
auto outboundTunnel = std::make_shared<ZeroHopsOutboundTunnel> ();
outboundTunnel->SetState (eTunnelStateEstablished);
m_OutboundTunnels.push_back (outboundTunnel);
// we don't insert into m_Tunnels
}
int Tunnels::GetTransitTunnelsExpirationTimeout ()
{
int timeout = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// TODO: possible race condition with I2PControl
for (auto it: m_TransitTunnels)
{
int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
if (t > timeout) timeout = t;
}
return timeout;
}
size_t Tunnels::CountTransitTunnels() const
{
// TODO: locking
return m_TransitTunnels.size();
}
size_t Tunnels::CountInboundTunnels() const
{
// TODO: locking
return m_InboundTunnels.size();
}
size_t Tunnels::CountOutboundTunnels() const
{
// TODO: locking
return m_OutboundTunnels.size();
}
}
}

181
Tunnel.h
View File

@@ -3,12 +3,15 @@
#include <inttypes.h>
#include <map>
#include <unordered_map>
#include <list>
#include <vector>
#include <string>
#include <thread>
#include <mutex>
#include <memory>
#include "Queue.h"
#include "Crypto.h"
#include "TunnelConfig.h"
#include "TunnelPool.h"
#include "TransitTunnel.h"
@@ -23,6 +26,7 @@ namespace tunnel
{
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes
const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message
@@ -41,73 +45,118 @@ namespace tunnel
class InboundTunnel;
class Tunnel: public TunnelBase
{
struct TunnelHop
{
std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::crypto::TunnelDecryption decryption;
};
public:
Tunnel (TunnelConfig * config);
Tunnel (std::shared_ptr<const TunnelConfig> config);
~Tunnel ();
void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0);
void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
TunnelConfig * GetTunnelConfig () const { return m_Config; }
std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; }
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
TunnelState GetState () const { return m_State; };
void SetState (TunnelState state) { m_State = state; };
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; };
bool IsRecreated () const { return m_IsRecreated; };
void SetIsRecreated () { m_IsRecreated = true; };
TunnelPool * GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (TunnelPool * pool) { m_Pool = pool; };
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
virtual void Print (std::stringstream& s) const {};
// implements TunnelBase
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); };
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
protected:
void PrintHops (std::stringstream& s) const;
private:
TunnelConfig * m_Config;
TunnelPool * m_Pool; // pool, tunnel belongs to, or null
std::shared_ptr<const TunnelConfig> m_Config;
std::vector<std::unique_ptr<TunnelHop> > m_Hops;
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State;
bool m_IsRecreated;
};
class OutboundTunnel: public Tunnel
{
public:
OutboundTunnel (TunnelConfig * config): Tunnel (config), m_Gateway (this) {};
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
const i2p::data::RouterInfo * GetEndpointRouter () const
{ return GetTunnelConfig ()->GetLastHop ()->router; };
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
OutboundTunnel (std::shared_ptr<const TunnelConfig> config):
Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
virtual void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; };
virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
void Print (std::stringstream& s) const;
// implements TunnelBase
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
private:
std::mutex m_SendMutex;
TunnelGateway m_Gateway;
i2p::data::IdentHash m_EndpointIdentHash;
};
class InboundTunnel: public Tunnel
class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel>
{
public:
InboundTunnel (TunnelConfig * config): Tunnel (config), m_Endpoint (true) {};
void HandleTunnelDataMsg (I2NPMessage * msg);
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
// implements TunnelBase
uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
void Print (std::stringstream& s) const;
private:
TunnelEndpoint m_Endpoint;
};
class ZeroHopsInboundTunnel: public InboundTunnel
{
public:
ZeroHopsInboundTunnel ();
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void Print (std::stringstream& s) const;
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
private:
size_t m_NumReceivedBytes;
};
class ZeroHopsOutboundTunnel: public OutboundTunnel
{
public:
ZeroHopsOutboundTunnel ();
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs);
void Print (std::stringstream& s) const;
size_t GetNumSentBytes () const { return m_NumSentBytes; };
private:
size_t m_NumSentBytes;
};
class Tunnels
{
public:
@@ -117,49 +166,64 @@ namespace tunnel
void Start ();
void Stop ();
InboundTunnel * GetInboundTunnel (uint32_t tunnelID);
Tunnel * GetPendingTunnel (uint32_t replyMsgID);
InboundTunnel * GetNextInboundTunnel ();
OutboundTunnel * GetNextOutboundTunnel ();
TunnelPool * GetExploratoryPool () const { return m_ExploratoryPool; };
TransitTunnel * GetTransitTunnel (uint32_t tunnelID);
void AddTransitTunnel (TransitTunnel * tunnel);
void AddOutboundTunnel (OutboundTunnel * newTunnel);
void AddInboundTunnel (InboundTunnel * newTunnel);
void PostTunnelData (I2NPMessage * msg);
std::shared_ptr<InboundTunnel> GetPendingInboundTunnel (uint32_t replyMsgID);
std::shared_ptr<OutboundTunnel> GetPendingOutboundTunnel (uint32_t replyMsgID);
std::shared_ptr<InboundTunnel> GetNextInboundTunnel ();
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel ();
std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
std::shared_ptr<TunnelBase> GetTunnel (uint32_t tunnelID);
int GetTransitTunnelsExpirationTimeout ();
void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
template<class TTunnel>
TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0);
TunnelPool * CreateTunnelPool (i2p::garlic::GarlicDestination& localDestination, int numHops);
void DeleteTunnelPool (TunnelPool * pool);
void StopTunnelPool (TunnelPool * pool);
std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
int numOuboundHops, int numInboundTunnels, int numOutboundTunnels);
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
private:
template<class TTunnel>
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
void HandleTunnelGatewayMsg (std::shared_ptr<TunnelBase> tunnel, std::shared_ptr<I2NPMessage> msg);
void Run ();
void ManageTunnels ();
void ManageOutboundTunnels ();
void ManageInboundTunnels ();
void ManageTransitTunnels ();
void ManagePendingTunnels ();
template<class PendingTunnels>
void ManagePendingTunnels (PendingTunnels& pendingTunnels);
void ManageTunnelPools ();
void CreateZeroHopsInboundTunnel ();
void CreateZeroHopsOutboundTunnel ();
private:
bool m_IsRunning;
std::thread * m_Thread;
std::map<uint32_t, Tunnel *> m_PendingTunnels; // by replyMsgID
std::mutex m_InboundTunnelsMutex;
std::map<uint32_t, InboundTunnel *> m_InboundTunnels;
std::mutex m_OutboundTunnelsMutex;
std::list<OutboundTunnel *> m_OutboundTunnels;
std::mutex m_TransitTunnelsMutex;
std::map<uint32_t, TransitTunnel *> m_TransitTunnels;
std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_PendingInboundTunnels; // by replyMsgID
std::map<uint32_t, std::shared_ptr<OutboundTunnel> > m_PendingOutboundTunnels; // by replyMsgID
std::list<std::shared_ptr<InboundTunnel> > m_InboundTunnels;
std::list<std::shared_ptr<OutboundTunnel> > m_OutboundTunnels;
std::list<std::shared_ptr<TransitTunnel> > m_TransitTunnels;
std::unordered_map<uint32_t, std::shared_ptr<TunnelBase> > m_Tunnels; // tunnelID->tunnel known by this id
std::mutex m_PoolsMutex;
std::map<i2p::data::IdentHash, TunnelPool *> m_Pools;
TunnelPool * m_ExploratoryPool;
i2p::util::Queue<I2NPMessage> m_Queue;
std::list<std::shared_ptr<TunnelPool>> m_Pools;
std::shared_ptr<TunnelPool> m_ExploratoryPool;
i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
// some stats
int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations;
public:
@@ -167,6 +231,17 @@ namespace tunnel
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; };
size_t CountTransitTunnels() const;
size_t CountInboundTunnels() const;
size_t CountOutboundTunnels() const;
int GetQueueSize () { return m_Queue.GetSize (); };
int GetTunnelCreationSuccessRate () const // in percents
{
int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations;
return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0;
}
};
extern Tunnels tunnels;

View File

@@ -2,6 +2,7 @@
#define TUNNEL_BASE_H__
#include <inttypes.h>
#include <memory>
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "Identity.h"
@@ -25,33 +26,39 @@ namespace tunnel
TunnelDeliveryType deliveryType;
i2p::data::IdentHash hash;
uint32_t tunnelID;
I2NPMessage * data;
std::shared_ptr<I2NPMessage> data;
};
class TunnelBase
{
public:
//WARNING!!! GetSecondsSinceEpoch() return uint64_t
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
TunnelBase (uint32_t tunnelID, uint32_t nextTunnelID, i2p::data::IdentHash nextIdent):
m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {};
virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0;
virtual uint32_t GetNextTunnelID () const = 0;
virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0;
virtual uint32_t GetTunnelID () const = 0; // as known at our side
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
virtual void FlushTunnelDataMsgs () {};
virtual void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) = 0;
uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
virtual uint32_t GetTunnelID () const { return m_TunnelID; }; // as known at our side
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
private:
uint32_t m_TunnelID, m_NextTunnelID;
i2p::data::IdentHash m_NextIdent;
uint32_t m_CreationTime; // seconds since epoch
};
struct TunnelCreationTimeCmp
{
bool operator() (const TunnelBase * t1, const TunnelBase * t2) const
bool operator() (std::shared_ptr<const TunnelBase> t1, std::shared_ptr<const TunnelBase> t2) const
{
if (t1->GetCreationTime () != t2->GetCreationTime ())
return t1->GetCreationTime () > t2->GetCreationTime ();

View File

@@ -4,9 +4,12 @@
#include <inttypes.h>
#include <sstream>
#include <vector>
#include "aes.h"
#include "RouterInfo.h"
#include <memory>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Identity.h"
#include "RouterContext.h"
#include "Timestamp.h"
namespace i2p
{
@@ -14,7 +17,8 @@ namespace tunnel
{
struct TunnelHopConfig
{
const i2p::data::RouterInfo * router, * nextRouter;
std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::data::IdentHash nextIdent;
uint32_t tunnelID, nextTunnelID;
uint8_t layerKey[32];
uint8_t ivKey[32];
@@ -23,38 +27,36 @@ namespace tunnel
bool isGateway, isEndpoint;
TunnelHopConfig * next, * prev;
i2p::crypto::TunnelDecryption decryption;
int recordIndex; // record # in tunnel build message
TunnelHopConfig (const i2p::data::RouterInfo * r)
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r)
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (layerKey, 32);
rnd.GenerateBlock (ivKey, 32);
rnd.GenerateBlock (replyIV, 16);
tunnelID = rnd.GenerateWord32 ();
RAND_bytes (layerKey, 32);
RAND_bytes (ivKey, 32);
RAND_bytes (replyKey, 32);
RAND_bytes (replyIV, 16);
RAND_bytes ((uint8_t *)&tunnelID, 4);
isGateway = true;
isEndpoint = true;
router = r;
nextRouter = 0;
ident = r;
//nextRouter = nullptr;
nextTunnelID = 0;
next = 0;
prev = 0;
next = nullptr;
prev = nullptr;
}
void SetNextRouter (const i2p::data::RouterInfo * r)
void SetNextIdent (const i2p::data::IdentHash& ident)
{
nextRouter = r;
nextIdent = ident;
isEndpoint = false;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
nextTunnelID = rnd.GenerateWord32 ();
RAND_bytes ((uint8_t *)&nextTunnelID, 4);
}
void SetReplyHop (const TunnelHopConfig * replyFirstHop)
void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent)
{
nextRouter = replyFirstHop->router;
nextTunnelID = replyFirstHop->tunnelID;
nextIdent = replyIdent;
nextTunnelID = replyTunnelID;
isEndpoint = true;
}
@@ -66,7 +68,7 @@ namespace tunnel
next->prev = this;
next->isGateway = false;
isEndpoint = false;
nextRouter = next->router;
nextIdent = next->ident->GetIdentHash ();
nextTunnelID = next->tunnelID;
}
}
@@ -81,35 +83,47 @@ namespace tunnel
isGateway = false;
}
}
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) const
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32);
htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
uint8_t flag = 0;
if (isGateway) flag |= 0x80;
if (isEndpoint) flag |= 0x40;
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ());
elGamalEncryption.Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
}
};
class TunnelConfig
{
public:
TunnelConfig (std::vector<const i2p::data::RouterInfo *> peers,
const TunnelConfig * replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers) // inbound
{
TunnelHopConfig * prev = nullptr;
for (auto it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
if (replyTunnelConfig) // outbound
{
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
}
else // inbound
m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ());
CreatePeers (peers);
m_LastHop->SetNextIdent (i2p::context.GetIdentHash ());
}
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers,
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound
{
CreatePeers (peers);
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelID, replyIdent);
}
~TunnelConfig ()
@@ -146,75 +160,88 @@ namespace tunnel
return num;
}
void Print (std::stringstream& s) const
{
TunnelHopConfig * hop = m_FirstHop;
if (!m_FirstHop->isGateway)
s << "me";
s << "-->" << m_FirstHop->tunnelID;
while (hop)
{
s << ":" << hop->router->GetIdentHashAbbreviation () << "-->";
if (!hop->isEndpoint)
s << hop->nextTunnelID;
else
return;
hop = hop->next;
}
// we didn't reach enpoint that mean we are last hop
s << ":me";
virtual bool IsInbound () const { return m_FirstHop->isGateway; }
virtual uint32_t GetTunnelID () const
{
if (!m_FirstHop) return 0;
return IsInbound () ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID;
}
TunnelConfig * Invert () const
{
TunnelConfig * newConfig = new TunnelConfig ();
TunnelHopConfig * hop = m_FirstHop, * nextNewHop = nullptr;
while (hop)
{
TunnelHopConfig * newHop = new TunnelHopConfig (hop->router);
if (nextNewHop)
newHop->SetNext (nextNewHop);
nextNewHop = newHop;
newHop->isEndpoint = hop->isGateway;
newHop->isGateway = hop->isEndpoint;
if (!hop->prev) // first hop
{
newConfig->m_LastHop = newHop;
if (hop->isGateway) // inbound tunnel
newHop->SetReplyHop (m_FirstHop); // use it as reply tunnel
else
newHop->SetNextRouter (&i2p::context.GetRouterInfo ());
}
if (!hop->next) newConfig->m_FirstHop = newHop; // last hop
hop = hop->next;
}
return newConfig;
virtual uint32_t GetNextTunnelID () const
{
if (!m_FirstHop) return 0;
return m_FirstHop->tunnelID;
}
TunnelConfig * Clone (const TunnelConfig * replyTunnelConfig = nullptr) const
{
std::vector<const i2p::data::RouterInfo *> peers;
TunnelHopConfig * hop = m_FirstHop;
while (hop)
{
peers.push_back (hop->router);
hop = hop->next;
}
return new TunnelConfig (peers, replyTunnelConfig);
virtual const i2p::data::IdentHash& GetNextIdentHash () const
{
return m_FirstHop->ident->GetIdentHash ();
}
virtual const i2p::data::IdentHash& GetLastIdentHash () const
{
return m_LastHop->ident->GetIdentHash ();
}
private:
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const
{
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
TunnelHopConfig * hop = m_FirstHop;
while (hop)
{
peers.push_back (hop->ident);
hop = hop->next;
}
return peers;
}
protected:
// this constructor can't be called from outside
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
{
}
private:
template<class Peers>
void CreatePeers (const Peers& peers)
{
TunnelHopConfig * prev = nullptr;
for (auto it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
}
private:
TunnelHopConfig * m_FirstHop, * m_LastHop;
};
class ZeroHopsTunnelConfig: public TunnelConfig
{
public:
ZeroHopsTunnelConfig () { RAND_bytes ((uint8_t *)&m_TunnelID, 4);};
bool IsInbound () const { return true; }; // TODO:
uint32_t GetTunnelID () const { return m_TunnelID; };
uint32_t GetNextTunnelID () const { return m_TunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return i2p::context.GetIdentHash (); };
const i2p::data::IdentHash& GetLastIdentHash () const { return i2p::context.GetIdentHash (); };
private:
uint32_t m_TunnelID;
};
}
}

View File

@@ -1,5 +1,6 @@
#include "I2PEndian.h"
#include <string.h>
#include <openssl/sha.h>
#include "Log.h"
#include "NetDb.h"
#include "I2NPProtocol.h"
@@ -13,13 +14,9 @@ namespace tunnel
{
TunnelEndpoint::~TunnelEndpoint ()
{
for (auto it: m_IncompleteMessages)
i2p::DeleteI2NPMessage (it.second.data);
for (auto it: m_OutOfSequenceFragments)
i2p::DeleteI2NPMessage (it.second.data);
}
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (I2NPMessage * msg)
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg)
{
m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE;
@@ -27,16 +24,14 @@ namespace tunnel
uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum
if (zero)
{
LogPrint ("TunnelMessage: zero found at ", (int)(zero-decrypted));
uint8_t * fragment = zero + 1;
// verify checksum
memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv
SHA256(fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16, hash); // payload + iv
if (memcmp (hash, decrypted, 4))
{
LogPrint ("TunnelMessage: checksum verification failed");
i2p::DeleteI2NPMessage (msg);
LogPrint (eLogError, "TunnelMessage: checksum verification failed");
return;
}
// process fragments
@@ -57,17 +52,14 @@ namespace tunnel
switch (m.deliveryType)
{
case eDeliveryTypeLocal: // 0
LogPrint ("Delivery type local");
break;
case eDeliveryTypeTunnel: // 1
LogPrint ("Delivery type tunnel");
m.tunnelID = be32toh (*(uint32_t *)fragment);
m.tunnelID = bufbe32toh (fragment);
fragment += 4; // tunnelID
m.hash = i2p::data::IdentHash (fragment);
fragment += 32; // hash
break;
case eDeliveryTypeRouter: // 2
LogPrint ("Delivery type router");
m.hash = i2p::data::IdentHash (fragment);
fragment += 32; // to hash
break;
@@ -79,34 +71,36 @@ namespace tunnel
if (isFragmented)
{
// Message ID
msgID = be32toh (*(uint32_t *)fragment);
msgID = bufbe32toh (fragment);
fragment += 4;
LogPrint ("Fragmented message ", msgID);
isLastFragment = false;
}
}
else
{
// follow on
msgID = be32toh (*(uint32_t *)fragment); // MessageID
msgID = bufbe32toh (fragment); // MessageID
fragment += 4;
fragmentNum = (flag >> 1) & 0x3F; // 6 bits
isLastFragment = flag & 0x01;
LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last");
}
uint16_t size = be16toh (*(uint16_t *)fragment);
uint16_t size = bufbe16toh (fragment);
fragment += 2;
LogPrint ("Fragment size=", (int)size);
msg->offset = fragment - msg->buf;
msg->len = msg->offset + size;
if (msg->len > msg->maxLen)
{
LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
return;
}
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
{
// this is not last message. we have to copy it
m.data = NewI2NPMessage ();
m.data->offset += sizeof (TunnelGatewayHeader); // reserve room for TunnelGateway header
m.data->len += sizeof (TunnelGatewayHeader);
m.data = NewI2NPShortMessage ();
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
*(m.data) = *msg;
}
else
@@ -121,9 +115,11 @@ namespace tunnel
if (!isFollowOnFragment) // create new incomlete message
{
m.nextFragmentNum = 1;
auto& msg = m_IncompleteMessages[msgID];
msg = m;
HandleOutOfSequenceFragment (msgID, msg);
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
if (ret.second)
HandleOutOfSequenceFragment (msgID, ret.first->second);
else
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, "already exists");
}
else
{
@@ -131,18 +127,15 @@ namespace tunnel
HandleFollowOnFragment (msgID, isLastFragment, m);
}
}
else
LogPrint ("Message is fragmented, but msgID is not presented");
else
LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented");
}
fragment += size;
}
}
else
{
LogPrint ("TunnelMessage: zero not found");
i2p::DeleteI2NPMessage (msg);
}
LogPrint (eLogError, "TunnelMessage: zero not found");
}
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
@@ -155,10 +148,17 @@ namespace tunnel
auto& msg = it->second;
if (m.nextFragmentNum == msg.nextFragmentNum)
{
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if messega is not too long
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
{
memcpy (msg.data->buf + msg.data->len, fragment, size); // concatenate fragment
msg.data->len += size;
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (fragment, size) < size) // concatenate fragment
LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
if (isLastFragment)
{
// message complete
@@ -173,32 +173,28 @@ namespace tunnel
}
else
{
LogPrint ("Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
i2p::DeleteI2NPMessage (msg.data);
LogPrint (eLogError, "TunnelMessage: Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped");
m_IncompleteMessages.erase (it);
}
i2p::DeleteI2NPMessage (m.data);
}
else
{
LogPrint ("Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
}
}
else
{
LogPrint ("First fragment of message ", msgID, " not found. Saved");
LogPrint (eLogWarning, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
}
}
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, I2NPMessage * data)
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
{
auto it = m_OutOfSequenceFragments.find (msgID);
if (it == m_OutOfSequenceFragments.end ())
m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (msgID, {fragmentNum, isLastFragment, data}));
else
i2p::DeleteI2NPMessage (data);
}
void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
@@ -208,10 +204,17 @@ namespace tunnel
{
if (it->second.fragmentNum == msg.nextFragmentNum)
{
LogPrint ("Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
LogPrint (eLogWarning, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
auto size = it->second.data->GetLength ();
memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment
msg.data->len += size;
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
LogPrint (eLogError, "Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
if (it->second.isLastFragment)
{
// message complete
@@ -220,7 +223,6 @@ namespace tunnel
}
else
msg.nextFragmentNum++;
i2p::DeleteI2NPMessage (it->second.data);
m_OutOfSequenceFragments.erase (it);
}
}
@@ -228,42 +230,37 @@ namespace tunnel
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
{
LogPrint ("TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetHeader()->typeID);
if (!m_IsInbound && msg.data->IsExpired ())
{
LogPrint (eLogInfo, "TunnelMessage: message expired");
return;
}
auto typeID = msg.data->GetTypeID ();
LogPrint (eLogDebug, "TunnelMessage: handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID);
// catch RI or reply with new list of routers
if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) &&
!m_IsInbound && msg.deliveryType != eDeliveryTypeLocal)
i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg.data));
switch (msg.deliveryType)
{
case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data);
break;
case eDeliveryTypeTunnel:
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
break;
case eDeliveryTypeRouter:
if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us
i2p::HandleI2NPMessage (msg.data);
if (!m_IsInbound) // outbound transit tunnel
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
else
{
// to somebody else
if (!m_IsInbound) // outbound transit tunnel
{
if (msg.data->GetHeader()->typeID == eI2NPDatabaseStore ||
msg.data->GetHeader()->typeID == eI2NPDatabaseSearchReply )
{
// catch RI or reply with new list of routers
auto ds = NewI2NPMessage ();
*ds = *(msg.data);
i2p::data::netdb.PostI2NPMsg (ds);
}
i2p::transport::transports.SendMessage (msg.hash, msg.data);
}
else // we shouldn't send this message. possible leakage
{
LogPrint ("Message to another router arrived from an inbound tunnel. Dropped");
i2p::DeleteI2NPMessage (msg.data);
}
}
LogPrint (eLogError, "TunnelMessage: Delivery type 'tunnel' arrived from an inbound tunnel, dropped");
break;
case eDeliveryTypeRouter:
if (!m_IsInbound) // outbound transit tunnel
i2p::transport::transports.SendMessage (msg.hash, msg.data);
else // we shouldn't send this message. possible leakage
LogPrint (eLogError, "TunnelMessage: Delivery type 'router' arrived from an inbound tunnel, dropped");
break;
default:
LogPrint ("TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
};
}
}

View File

@@ -22,7 +22,7 @@ namespace tunnel
{
uint8_t fragmentNum;
bool isLastFragment;
I2NPMessage * data;
std::shared_ptr<I2NPMessage> data;
};
public:
@@ -31,14 +31,14 @@ namespace tunnel
~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void HandleDecryptedTunnelDataMsg (I2NPMessage * msg);
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
private:
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m);
void HandleNextMessage (const TunnelMessageBlock& msg);
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, I2NPMessage * data);
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg);
private:

View File

@@ -1,6 +1,7 @@
#include <string.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "I2PEndian.h"
#include <cryptopp/sha.h>
#include "Log.h"
#include "RouterContext.h"
#include "Transports.h"
@@ -10,6 +11,18 @@ namespace i2p
{
namespace tunnel
{
TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0)
{
RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE);
for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++)
if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1;
}
TunnelGatewayBuffer::~TunnelGatewayBuffer ()
{
}
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block)
{
bool messageCreated = false;
@@ -26,7 +39,7 @@ namespace tunnel
{
if (block.deliveryType == eDeliveryTypeTunnel)
{
*(uint32_t *)(di + diLen) = htobe32 (block.tunnelID);
htobe32buf (di + diLen, block.tunnelID);
diLen += 4; // tunnelID
}
@@ -36,12 +49,12 @@ namespace tunnel
di[0] = block.deliveryType << 5; // set delivery type
// create fragments
I2NPMessage * msg = block.data;
std::shared_ptr<I2NPMessage> msg = block.data;
auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
if (fullMsgLen <= m_RemainingSize)
{
// message fits. First and last fragment
*(uint16_t *)(di + diLen) = htobe16 (msg->GetLength ());
htobe16buf (di + diLen, msg->GetLength ());
diLen += 2; // size
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ());
@@ -49,7 +62,6 @@ namespace tunnel
m_RemainingSize -= diLen + msg->GetLength ();
if (!m_RemainingSize)
CompleteCurrentTunnelDataMessage ();
DeleteI2NPMessage (msg);
}
else
{
@@ -68,14 +80,15 @@ namespace tunnel
if (diLen + 6 <= m_RemainingSize)
{
// delivery instructions fit
uint32_t msgID = msg->GetHeader ()->msgID; // in network bytes order
uint32_t msgID;
memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order
size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size)
// first fragment
di[0] |= 0x08; // fragmented
*(uint32_t *)(di + diLen) = msgID;
htobuf32 (di + diLen, msgID);
diLen += 4; // Message ID
*(uint16_t *)(di + diLen) = htobe16 (size);
htobe16buf (di + diLen, size);
diLen += 2; // size
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size);
@@ -96,9 +109,9 @@ namespace tunnel
{
buf[0] |= 0x01;
isLastFragment = true;
}
*(uint32_t *)(buf + 1) = msgID; //Message ID
*(uint16_t *)(buf + 5) = htobe16 (s); // size
}
htobuf32 (buf + 1, msgID); //Message ID
htobe16buf (buf + 5, s); // size
memcpy (buf + 7, msg->GetBuffer () + size, s);
m_CurrentTunnelDataMsg->len += s+7;
if (isLastFragment)
@@ -112,7 +125,6 @@ namespace tunnel
size += s;
fragmentNumber++;
}
DeleteI2NPMessage (msg);
}
else
{
@@ -131,9 +143,10 @@ namespace tunnel
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
{
m_CurrentTunnelDataMsg = NewI2NPMessage ();
m_CurrentTunnelDataMsg = NewI2NPShortMessage ();
m_CurrentTunnelDataMsg->Align (12);
// we reserve space for padding
m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + sizeof (I2NPHeader);
m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE;
m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset;
m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
}
@@ -144,25 +157,28 @@ namespace tunnel
uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer ();
size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset;
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - sizeof (I2NPHeader);
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE;
uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
*(uint32_t *)(buf) = htobe32 (m_TunnelID);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (buf + 4, 16); // original IV
htobe32buf (buf, m_TunnelID);
RAND_bytes (buf + 4, 16); // original IV
memcpy (payload + size, buf + 4, 16); // copy IV for checksum
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, payload, size+16);
SHA256(payload, size+16, hash);
memcpy (buf+20, hash, 4); // checksum
payload[-1] = 0; // zero
ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1
if (paddingSize > 0)
memset (buf + 24, 1, paddingSize); // padding TODO: fill with random data
{
// non-zero padding
auto randomOffset = rand () % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1);
memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize);
}
// we can't fill message header yet because encryption is required
m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg);
m_CurrentTunnelDataMsg = nullptr;
}
void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block)
{
if (block.data)
@@ -184,11 +200,11 @@ namespace tunnel
auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto tunnelMsg : tunnelMsgs)
{
m_Tunnel->EncryptTunnelMsg (tunnelMsg);
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transport::transports.SendMessage (m_Tunnel->GetNextIdentHash (), tunnelMsg);
m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg);
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData);
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
}
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs);
m_Buffer.ClearTunnelDataMsgs ();
}
}

View File

@@ -3,6 +3,7 @@
#include <inttypes.h>
#include <vector>
#include <memory>
#include "I2NPProtocol.h"
#include "TunnelBase.h"
@@ -13,10 +14,10 @@ namespace tunnel
class TunnelGatewayBuffer
{
public:
TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {};
TunnelGatewayBuffer (uint32_t tunnelID);
~TunnelGatewayBuffer ();
void PutI2NPMsg (const TunnelMessageBlock& block);
const std::vector<I2NPMessage *>& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
const std::vector<std::shared_ptr<I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
void ClearTunnelDataMsgs ();
void CompleteCurrentTunnelDataMessage ();
@@ -27,16 +28,17 @@ namespace tunnel
private:
uint32_t m_TunnelID;
std::vector<I2NPMessage *> m_TunnelDataMsgs;
I2NPMessage * m_CurrentTunnelDataMsg;
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelDataMsgs;
std::shared_ptr<I2NPMessage> m_CurrentTunnelDataMsg;
size_t m_RemainingSize;
uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE];
};
class TunnelGateway
{
public:
TunnelGateway (TunnelBase * tunnel):
TunnelGateway (TunnelBase * tunnel):
m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {};
void SendTunnelDataMsg (const TunnelMessageBlock& block);
void PutTunnelDataMsg (const TunnelMessageBlock& block);

View File

@@ -1,18 +1,22 @@
#include <algorithm>
#include <openssl/rand.h>
#include "I2PEndian.h"
#include "CryptoConst.h"
#include "Crypto.h"
#include "Tunnel.h"
#include "NetDb.h"
#include "Timestamp.h"
#include "Garlic.h"
#include "Transports.h"
#include "Log.h"
#include "TunnelPool.h"
namespace i2p
{
namespace tunnel
{
TunnelPool::TunnelPool (i2p::garlic::GarlicDestination& localDestination, int numHops, int numTunnels):
m_LocalDestination (localDestination), m_NumHops (numHops), m_NumTunnels (numTunnels),
m_IsActive (true)
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true)
{
}
@@ -21,6 +25,27 @@ namespace tunnel
DetachTunnels ();
}
void TunnelPool::SetExplicitPeers (std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers)
{
m_ExplicitPeers = explicitPeers;
if (m_ExplicitPeers)
{
int size = m_ExplicitPeers->size ();
if (m_NumInboundHops > size)
{
m_NumInboundHops = size;
LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers");
}
if (m_NumOutboundHops > size)
{
m_NumOutboundHops = size;
LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers");
}
m_NumInboundTunnels = 1;
m_NumOutboundTunnels = 1;
}
}
void TunnelPool::DetachTunnels ()
{
{
@@ -38,54 +63,56 @@ namespace tunnel
m_Tests.clear ();
}
void TunnelPool::TunnelCreated (InboundTunnel * createdTunnel)
void TunnelPool::TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel)
{
if (!m_IsActive) return;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.insert (createdTunnel);
}
m_LocalDestination.SetLeaseSetUpdated ();
if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated ();
}
void TunnelPool::TunnelExpired (InboundTunnel * expiredTunnel)
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
RecreateInboundTunnel (expiredTunnel);
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.erase (expiredTunnel);
}
}
void TunnelPool::TunnelCreated (OutboundTunnel * createdTunnel)
void TunnelPool::TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel)
{
if (!m_IsActive) return;
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
}
//CreatePairedInboundTunnel (createdTunnel);
}
void TunnelPool::TunnelExpired (OutboundTunnel * expiredTunnel)
void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
RecreateOutboundTunnel (expiredTunnel);
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.erase (expiredTunnel);
}
}
std::vector<InboundTunnel *> TunnelPool::GetInboundTunnels (int num) const
std::vector<std::shared_ptr<InboundTunnel> > TunnelPool::GetInboundTunnels (int num) const
{
std::vector<InboundTunnel *> v;
std::vector<std::shared_ptr<InboundTunnel> > v;
int i = 0;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
@@ -100,38 +127,54 @@ namespace tunnel
return v;
}
OutboundTunnel * TunnelPool::GetNextOutboundTunnel (OutboundTunnel * suggested) const
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
return GetNextTunnel (m_OutboundTunnels, suggested);
return GetNextTunnel (m_OutboundTunnels, excluded);
}
InboundTunnel * TunnelPool::GetNextInboundTunnel (InboundTunnel * suggested) const
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
return GetNextTunnel (m_InboundTunnels, suggested);
return GetNextTunnel (m_InboundTunnels, excluded);
}
template<class TTunnels>
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels,
typename TTunnels::value_type suggested) const
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const
{
if (tunnels.empty ()) return nullptr;
if (suggested && tunnels.count (suggested) > 0 && suggested->IsEstablished ())
return suggested;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, tunnels.size ()/2), i = 0;
if (tunnels.empty ()) return nullptr;
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
typename TTunnels::value_type tunnel = nullptr;
for (auto it: tunnels)
{
if (it->IsEstablished ())
if (it->IsEstablished () && it != excluded)
{
tunnel = it;
i++;
}
if (i > ind && tunnel) break;
}
}
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
return tunnel;
}
std::shared_ptr<OutboundTunnel> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const
{
if (old && old->IsEstablished ()) return old;
std::shared_ptr<OutboundTunnel> tunnel;
if (old)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels)
if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
{
tunnel = it;
break;
}
}
if (!tunnel)
tunnel = GetNextOutboundTunnel ();
return tunnel;
}
@@ -143,7 +186,7 @@ namespace tunnel
for (auto it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumTunnels; i++)
for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel ();
num = 0;
@@ -152,16 +195,22 @@ namespace tunnel
for (auto it : m_OutboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumTunnels; i++)
for (int i = num; i < m_NumOutboundTunnels; i++)
CreateOutboundTunnel ();
}
void TunnelPool::TestTunnels ()
{
auto& rnd = i2p::context.GetRandomNumberGenerator ();
for (auto it: m_Tests)
decltype(m_Tests) tests;
{
LogPrint ("Tunnel test ", (int)it.first, " failed");
std::unique_lock<std::mutex> l(m_TestsMutex);
tests = m_Tests;
m_Tests.clear ();
}
for (auto it: tests)
{
LogPrint (eLogWarning, "Tunnels: test of tunnel ", it.first, " failed");
// if test failed again with another tunnel we consider it failed
if (it.second.first)
{
@@ -183,13 +232,15 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.erase (it.second.second);
}
m_LocalDestination.SetLeaseSetUpdated ();
if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated ();
}
else
it.second.second->SetState (eTunnelStateTestFailed);
}
}
m_Tests.clear ();
// new tests
auto it1 = m_OutboundTunnels.begin ();
auto it2 = m_InboundTunnels.begin ();
while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
@@ -207,124 +258,200 @@ namespace tunnel
}
if (!failed)
{
uint32_t msgID = rnd.GenerateWord32 ();
m_Tests[msgID] = std::make_pair (*it1, *it2);
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
{
std::unique_lock<std::mutex> l(m_TestsMutex);
m_Tests[msgID] = std::make_pair (*it1, *it2);
}
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
CreateDeliveryStatusMsg (msgID));
it1++; it2++;
}
}
}
void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg)
void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
auto it = m_Tests.find (be32toh (deliveryStatus->msgID));
if (it != m_Tests.end ())
if (m_LocalDestination)
m_LocalDestination->ProcessGarlicMessage (msg);
else
LogPrint (eLogWarning, "Tunnels: local destination doesn't exist, dropped");
}
void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
uint32_t msgID = bufbe32toh (buf);
buf += 4;
uint64_t timestamp = bufbe64toh (buf);
decltype(m_Tests)::mapped_type test;
bool found = false;
{
std::unique_lock<std::mutex> l(m_TestsMutex);
auto it = m_Tests.find (msgID);
if (it != m_Tests.end ())
{
found = true;
test = it->second;
m_Tests.erase (it);
}
}
if (found)
{
// restore from test failed state if any
if (it->second.first->GetState () == eTunnelStateTestFailed)
it->second.first->SetState (eTunnelStateEstablished);
if (it->second.second->GetState () == eTunnelStateTestFailed)
it->second.second->SetState (eTunnelStateEstablished);
LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - be64toh (deliveryStatus->timestamp), " milliseconds");
m_Tests.erase (it);
DeleteI2NPMessage (msg);
if (test.first->GetState () == eTunnelStateTestFailed)
test.first->SetState (eTunnelStateEstablished);
if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished);
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds");
}
else
m_LocalDestination.ProcessDeliveryStatusMessage (msg);
{
if (m_LocalDestination)
m_LocalDestination->ProcessDeliveryStatusMessage (msg);
else
LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
}
}
const i2p::data::RouterInfo * TunnelPool::SelectNextHop (const i2p::data::RouterInfo * prevHop) const
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const
{
auto hop = m_NumHops >= 3 ? i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop) :
i2p::data::netdb.GetRandomRouter (prevHop);
if (!hop)
bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ());
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop):
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
if (!hop || hop->GetProfile ()->IsBad ())
hop = i2p::data::netdb.GetRandomRouter ();
return hop;
}
void TunnelPool::CreateInboundTunnel ()
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint ("Creating destination inbound tunnel...");
const i2p::data::RouterInfo * prevHop = &i2p::context.GetRouterInfo ();
std::vector<const i2p::data::RouterInfo *> hops;
int numHops = m_NumHops;
if (outboundTunnel)
{
// last hop
auto hop = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
if (hop->GetIdentHash () != i2p::context.GetIdentHash ()) // outbound shouldn't be zero-hop tunnel
{
prevHop = hop;
hops.push_back (prevHop);
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
auto prevHop = i2p::context.GetSharedRouterInfo ();
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
if (i2p::transport::transports.GetNumPeers () > 25)
{
auto r = i2p::transport::transports.GetRandomPeer ();
if (r && !r->GetProfile ()->IsBad ())
{
prevHop = r;
peers.push_back (r->GetRouterIdentity ());
numHops--;
}
}
for (int i = 0; i < numHops; i++)
{
auto hop = SelectNextHop (prevHop);
if (!hop)
{
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
return false;
}
prevHop = hop;
hops.push_back (hop);
peers.push_back (hop->GetRouterIdentity ());
}
std::reverse (hops.begin (), hops.end ());
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops), outboundTunnel);
tunnel->SetTunnelPool (this);
}
void TunnelPool::RecreateInboundTunnel (InboundTunnel * tunnel)
return true;
}
bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
int size = m_ExplicitPeers->size ();
std::vector<int> peerIndicies;
for (int i = 0; i < size; i++) peerIndicies.push_back(i);
std::random_shuffle (peerIndicies.begin(), peerIndicies.end());
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
for (int i = 0; i < numHops; i++)
{
auto& ident = (*m_ExplicitPeers)[peerIndicies[i]];
auto r = i2p::data::netdb.FindRouter (ident);
if (r)
peers.push_back (r->GetRouterIdentity ());
else
{
LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
i2p::data::netdb.RequestDestination (ident);
return false;
}
}
return true;
}
void TunnelPool::CreateInboundTunnel ()
{
auto outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint ("Re-creating destination inbound tunnel...");
auto * newTunnel = tunnels.CreateTunnel<InboundTunnel> (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel);
newTunnel->SetTunnelPool (this);
LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (peers, true))
{
std::reverse (peers.begin (), peers.end ());
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig> (peers), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
else
LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available");
}
void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
{
auto outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(tunnel->GetPeers ()), outboundTunnel);
newTunnel->SetTunnelPool (shared_from_this());
}
void TunnelPool::CreateOutboundTunnel ()
{
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel)
inboundTunnel = tunnels.GetNextInboundTunnel ();
if (inboundTunnel)
{
LogPrint ("Creating destination outbound tunnel...");
const i2p::data::RouterInfo * prevHop = &i2p::context.GetRouterInfo ();
std::vector<const i2p::data::RouterInfo *> hops;
for (int i = 0; i < m_NumHops; i++)
{
auto hop = SelectNextHop (prevHop);
prevHop = hop;
hops.push_back (hop);
LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (peers, false))
{
auto tunnel = tunnels.CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
tunnel->SetTunnelPool (shared_from_this ());
}
auto * tunnel = tunnels.CreateTunnel<OutboundTunnel> (
new TunnelConfig (hops, inboundTunnel->GetTunnelConfig ()));
tunnel->SetTunnelPool (this);
else
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
}
else
LogPrint ("Can't create outbound tunnel. No inbound tunnels found");
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found");
}
void TunnelPool::RecreateOutboundTunnel (OutboundTunnel * tunnel)
void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
{
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel)
inboundTunnel = tunnels.GetNextInboundTunnel ();
if (inboundTunnel)
{
LogPrint ("Re-creating destination outbound tunnel...");
auto * newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
tunnel->GetTunnelConfig ()->Clone (inboundTunnel->GetTunnelConfig ()));
newTunnel->SetTunnelPool (this);
LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (tunnel->GetPeers (),
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
newTunnel->SetTunnelPool (shared_from_this ());
}
else
LogPrint ("Can't re-create outbound tunnel. No inbound tunnels found");
}
LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found");
}
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
{
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
}
}

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