Compare commits

..

828 Commits

Author SHA1 Message Date
MXPLRS | Kirill
3643d6b5d5 Update changelog 2016-10-17 07:37:40 +03:00
orignal
2edd64470b Update changelog 2016-10-16 09:19:48 -04:00
l-n-s
c42e2fe02d Update i2pd.conf 2016-10-16 13:17:00 +00:00
orignal
12c67b5db4 2.10.0 2016-10-16 08:35:48 -04:00
orignal
a943cc09fe 2.10.0 2016-10-16 07:58:26 -04:00
orignal
1ceda52f59 2.10.0 2016-10-16 07:52:45 -04:00
l-n-s
04ee419951 small fixes for docs 2016-10-16 11:04:59 +00:00
l-n-s
f687728c3a edit link to usage documentation 2016-10-16 10:59:48 +00:00
l-n-s
bde5d27a20 Update README.md 2016-10-13 16:56:23 +00:00
l-n-s
07a1651fa2 Update usage.md
fix for readthedocs
2016-10-13 07:45:30 +00:00
l-n-s
32b47bee2c Update README.md
Add link to Russian docs
2016-10-12 17:52:07 +00:00
orignal
fbf75ea3b9 check if signer/verifier is set already 2016-10-12 13:28:22 -04:00
orignal
a157aba74f Merge pull request #671 from atnaguzin/patch-2
Update installer.iss
2016-10-12 12:32:12 -04:00
orignal
e45e5df377 openssl 1.1 DSA functions 2016-10-12 12:31:27 -04:00
MXPLRS | Kirill
85c7bfa160 Update installer.iss 2016-10-12 19:30:20 +03:00
orignal
8182f97c15 Merge pull request #670 from atnaguzin/patch-2
script for inno setup
2016-10-12 12:09:03 -04:00
MXPLRS | Kirill
eba824f5d0 script for inno setup 2016-10-12 19:03:35 +03:00
orignal
99983798a4 configurable netid 2016-10-12 11:26:48 -04:00
orignal
93ed032015 avoid potential deadlock 2016-10-12 09:39:16 -04:00
l-n-s
9359f5b296 Update README.md
Less complicated description
2016-10-12 12:09:58 +00:00
orignal
3b467c19cb Merge pull request #668 from l-n-s/addressbook_configurable
Add addressbook options + improved docs
2016-10-12 07:08:55 -04:00
l-n-s
470a6f0ab2 Add addressbook options + improved docs 2016-10-12 10:23:43 +00:00
orignal
fe8a0c1a6b #622. Force SU3 verification by reseed.verify 2016-10-11 15:02:23 -04:00
orignal
f0d098d0ef use shared local destination for lookup if destination is not set 2016-10-11 13:39:07 -04:00
orignal
f17df1f16d spinlock added 2016-10-11 12:06:40 -04:00
orignal
b1f8f9830b fixed another cases for #651 2016-10-11 10:18:42 -04:00
orignal
e78ccc6bec fixed #651. check is destination is set 2016-10-11 07:31:16 -04:00
orignal
b54892a783 Merge pull request #664 from brain5lug/tag-cleanup
Tag class clean-up
2016-10-10 18:13:37 -04:00
brain5lug
16c37a0f3d indentation fix for missed Fill function 2016-10-11 00:46:18 +03:00
brain5lug
141fb78237 Tag class clean-up 2016-10-11 00:19:34 +03:00
orignal
eb31b9a4d6 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-10-10 16:34:08 -04:00
orignal
f10d9e1332 update reseed lists 2016-10-10 16:31:26 -04:00
orignal
6d63521622 update miniupnpc instructions 2016-10-10 11:46:52 -04:00
l-n-s
e1aa066489 Merge pull request #663 from l-n-s/update_readme
Update readme
2016-10-10 15:17:10 +00:00
l-n-s
c78ec12e99 Add specs link 2016-10-10 15:07:22 +00:00
l-n-s
3fa4e2f58d Update README.md 2016-10-10 15:00:36 +00:00
Jeff
c64aaade70 Merge pull request #662 from majestrate/merge-recent-udp-tunnel
Merge recent udp tunnel changes
2016-10-10 09:13:57 -04:00
Jeff Becker
e8d8b290a6 rename 2016-10-10 09:07:49 -04:00
Jeff Becker
456d9e79e6 Revert "minimize count of errors "I2CP: Failed to send message. No outbound tunnels""
This reverts commit 8ff2627e8e.
2016-10-10 09:06:32 -04:00
Jeff Becker
3095e14247 undo weird mutex changes 2016-10-10 09:04:24 -04:00
Jeff Becker
a332d68704 Revert "fix f79ad91"
This reverts commit e8e3db6888.
2016-10-10 09:02:39 -04:00
Jeff Becker
84ca992e91 Revert "Unused parameter warnings removal"
This reverts commit 5350078543.
2016-10-10 08:59:48 -04:00
orignal
f91f3796a8 make sure verifier gets created once 2016-10-10 08:59:45 -04:00
Jeff Becker
22250ae552 Merge remote-tracking branch 'purple/openssl' into merge-recent-udp-tunnel 2016-10-10 08:36:06 -04:00
Jeff Becker
8a95b5b5b0 tabify 2016-10-10 08:30:33 -04:00
Jeff Becker
7506619f4c add minimum path lifetime 2016-10-10 08:25:07 -04:00
Jeff Becker
577d9ddf65 fix memory leak with udp tunnel 2016-10-10 08:23:54 -04:00
Jeff Becker
43c3bdf7c5 fix 2016-10-10 08:21:47 -04:00
Jeff Becker
8ba142eb45 increase datagram session switching interval 2016-10-10 08:21:33 -04:00
Jeff Becker
0fc4e01b1e remove tools 2016-10-10 08:18:54 -04:00
orignal
f83ebbcd3a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-10-09 15:45:54 -04:00
orignal
77ec4b5cad added warning 2016-10-09 14:57:15 -04:00
orignal
9a687976bc Merge pull request #659 from brain5lug/thread-sanitizer
thread sanitizer configuration option have been added
2016-10-09 14:52:42 -04:00
brain5lug
5a796a86d7 thread sanitizer configuration option have been added 2016-10-06 22:49:44 +03:00
Jeff Becker
71d4221af2 add keyinfo tool 2016-10-05 11:40:25 -04:00
Jeff Becker
526ba37435 Merge branch 'merge-upd-tunnel-fix' into openssl 2016-10-05 11:36:15 -04:00
Jeff Becker
f3c080f8a4 Merge branch 'merge-650' into openssl 2016-10-05 11:29:55 -04:00
Jeff Becker
09a0cf07e4 Merge branch 'check_pr_653' into upstream-openssl 2016-10-05 11:24:44 -04:00
Jeff Becker
4a3bf46c30 Revert "try fixing appveyor"
This reverts commit 30dfe12910.
2016-10-05 11:03:51 -04:00
Jeff Becker
30dfe12910 try fixing appveyor 2016-10-05 10:46:15 -04:00
Jeff Becker
ae2b5dfd3e fix udp tunnel route switching logic 2016-10-05 07:42:00 -04:00
alexandr
cb0f968467 Added building option "USE_ASLR" 2016-10-05 06:45:41 +05:00
alexandr
e8e3db6888 fix f79ad91 2016-10-05 01:20:43 +05:00
Pavel Melkozerov
012ade5000 Added extra-cmake-modules 2016-10-04 18:13:45 +03:00
brain5lug
5350078543 Unused parameter warnings removal 2016-10-04 00:24:42 +03:00
brain5lug
404715e02d thread sanitizer configuration option have been added 2016-10-03 23:24:22 +03:00
alexandr
31dde394eb remove unnecessary brackets 2016-10-03 20:20:45 +05:00
alexandr
8ff2627e8e minimize count of errors "I2CP: Failed to send message. No outbound tunnels" 2016-10-03 20:06:10 +05:00
alexandr
f79ad91a9a probably fix hanging of call I2CP-SendMsgTo-FindLeaseSet 2016-10-03 20:01:31 +05:00
alexandr
ff6a79bca3 Merge remote-tracking branch 'origin' into openssl 2016-10-02 03:22:50 +05:00
orignal
e4d6092939 copy addresses list atomically 2016-10-01 15:05:35 -04:00
alexandr
9d998d27c5 Merge remote-tracking branch 'origin' into openssl 2016-09-29 20:58:53 +05:00
orignal
d6aca6fa00 always send reply 2016-09-29 11:24:52 -04:00
alexandr
dd15472da7 Merge remote-tracking branch 'origin' into openssl 2016-09-26 02:45:44 +05:00
orignal
b03712a30e correct outbound tunnel selection 2016-09-25 17:23:21 -04:00
alexandr
d025ba2793 Fixed visibility of variable outboundTunnel 2016-09-26 01:37:00 +05:00
orignal
e5e09c9b51 check for boost version 2016-09-24 08:29:08 -04:00
orignal
5b8d1df349 Merge pull request #643 from atnaguzin/patch-1
Fixed links and msys2 example filename
2016-09-24 07:32:51 -04:00
MXPLRS | Kirill
fa092c0162 Fixed links 2016-09-24 14:26:59 +03:00
MXPLRS | Kirill
08c1359a27 fixed MiniUPnP link 2016-09-24 13:50:14 +03:00
orignal
dba355eccd use atomic_store for addresses' list re-assignment 2016-09-23 13:15:08 -04:00
orignal
2ad927b677 NTP request 2016-09-21 16:18:51 -04:00
orignal
315f672254 Timestamp.cpp added 2016-09-21 12:02:52 -04:00
orignal
7a51407f6d show error message in the web-console 2016-09-19 21:37:04 -04:00
orignal
783c2b6b03 Merge pull request #640 from atnaguzin/patch-1
add sliders for LeaseSets
2016-09-19 13:24:32 -04:00
MXPLRS | Kirill
a64e1b2aa6 add sliders for LeaseSets 2016-09-19 20:22:15 +03:00
orignal
440516e95f detect clock skew 2016-09-18 18:42:21 -04:00
orignal
dc6108575c Merge pull request #638 from brain5lug/openssl
address sanitizer configuration option have been added
2016-09-17 07:42:54 -04:00
brain5lug
6c7316408b address sanitizer configuration option have been added 2016-09-17 11:01:01 +03:00
orignal
9aecc69461 Merge pull request #636 from l-n-s/reseeds_config
Adding option to configure reseed URLs
2016-09-16 19:11:08 -04:00
l-n-s
949be436a6 Adding option to configure reseed URLs 2016-09-16 22:56:51 +00:00
orignal
cb91891f22 check buffer size 2016-09-16 16:18:50 -04:00
orignal
8795f0c8c4 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-09-16 10:31:45 -04:00
orignal
fbb5bb2f05 fix #634.don't create timer in constructor 2016-09-16 10:31:11 -04:00
orignal
ba309fe6e5 Merge pull request #633 from brain5lug/openssl
perfect forwarding for logging arguments
2016-09-15 21:27:30 -04:00
brain5lug
fee5f959fd perfect forwarding for logging arguments 2016-09-16 01:47:53 +03:00
orignal
f9a5f4955c check RI signture before processing 2016-09-12 21:37:43 -04:00
orignal
325b362727 show UDP tunnels 2016-09-12 12:05:01 -04:00
orignal
75065f29f7 check if field is incomplete 2016-09-12 11:39:33 -04:00
orignal
ed874fe3ea check if RouterInfo has been decompressed completely 2016-09-11 21:36:17 -04:00
orignal
6885761f87 check if sessions list is empty 2016-09-08 10:56:22 -04:00
orignal
a4762fe65c remove expired session through one pass 2016-09-08 10:46:13 -04:00
orignal
bee407ea34 clean-up datagram session toghters with leasesets and tags 2016-09-08 10:16:42 -04:00
orignal
db71673722 fixed #629. catch HTTPServer exceptions 2016-09-08 09:19:30 -04:00
orignal
9ecbbf09cc Merge pull request #628 from majestrate/merge_udp_tunnel
Merge Recent Changes
2016-09-07 15:27:54 -04:00
Jeff Becker
b6b14f4957 Merge remote-tracking branch 'purple/openssl' into merge_udp_tunnel 2016-09-07 15:08:45 -04:00
orignal
6e0d6dcac5 reselect tunnels if LeaseSet delivery was not confirmed 2016-09-07 13:25:11 -04:00
orignal
47a0ebdc91 Merge pull request #627 from vovasty/ios-build
build instruction for iOS
2016-09-06 16:01:27 -04:00
Solomenchuk, Vlad
f3a61007a7 build instruction for iOS 2016-09-06 12:26:59 -07:00
orignal
4a56d6bf1c Merge pull request #626 from majestrate/webui-freeze-fix
Webui freeze fix
2016-09-04 18:47:09 -04:00
Jeff Becker
517d4dc6f5 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into webui-freeze-fix 2016-09-04 16:43:55 -04:00
Jeff Becker
722f1c4430 try fixing webui freeze 2016-09-04 16:43:34 -04:00
Jeff Becker
f4d1b87f73 expire tags 2016-09-03 18:34:18 -04:00
Jeff Becker
f64f875806 don't show udp tunnels in ui yet 2016-09-03 18:04:54 -04:00
Jeff Becker
7ae09fa1fe try fixing memory errors 2016-09-03 17:58:16 -04:00
Jeff Becker
8a29dfc3fa tabify and use shared pointers 2016-09-03 17:53:46 -04:00
Jeff Becker
1015188c4e use shared pointers 2016-09-03 16:54:39 -04:00
Jeff Becker
f0bc2a3645 add null checks 2016-09-03 16:43:02 -04:00
Jeff Becker
82f46464f3 prevent double free 2016-09-03 16:29:50 -04:00
Jeff Becker
d336d920e8 fix typo 2016-09-03 16:16:16 -04:00
Jeff Becker
2f61dd1c41 fix double free 2016-09-03 16:12:43 -04:00
Jeff Becker
10ffd5c1ab don't check for expired lease 2016-09-03 16:06:14 -04:00
Jeff Becker
7e99be12b0 fix typo 2016-09-03 16:03:38 -04:00
Jeff Becker
d37a790b57 fix another typo 2016-09-03 15:58:52 -04:00
Jeff Becker
7ea8509dfe fix typo 2016-09-03 15:54:22 -04:00
Jeff Becker
783c0c7c7b update datagram lease selection 2016-09-03 15:53:28 -04:00
Jeff Becker
68b0775e4b update datagram path logic 2016-09-03 15:35:42 -04:00
Jeff Becker
682334d844 fix typo 2016-09-03 15:35:32 -04:00
libre-net-society
75981491a7 adding usage docs 2016-09-03 22:23:48 +03:00
Jeff Becker
7cc805b203 update datagram session logic 2016-09-03 14:56:51 -04:00
Jeff Becker
8cdd3a0abb update routing path when we get a new lease set 2016-09-03 14:42:27 -04:00
Jeff Becker
571c630d93 try creating routing session if not present 2016-09-03 14:37:36 -04:00
Jeff Becker
fa1021df59 add webui for udp tunnels 2016-09-03 13:58:34 -04:00
Jeff Becker
9acbb2203c Update Indentation and don't spam route changes in datagram sessions 2016-09-03 11:46:47 -04:00
Jeff Becker
c770bcbf96 prevent race condition in datagram destination
clean up style a bit
2016-09-03 10:24:06 -04:00
Jeff Becker
caace05ba6 fix up compiler warnings 2016-09-03 10:01:23 -04:00
Jeff Becker
c65dc44f20 Fix up I2PTunnel UDP tunnels 2016-09-03 09:38:53 -04:00
Jeff Becker
3ea624e1db cosmetic fix 2016-09-01 15:54:48 -04:00
Jeff Becker
c9c58074fa Merge remote-tracking branch 'purple/openssl' 2016-09-01 11:28:04 -04:00
orignal
aa687afd37 updated LeaseSet must be sent after completion 2016-09-01 09:48:04 -04:00
orignal
8cb69c1482 fixed #624. correct v6 address size 2016-08-31 22:47:32 -04:00
Jeff Becker
a68326490d fix 2016-08-30 20:02:27 -04:00
Jeff Becker
ab763c38d9 use shared pointers 2016-08-30 19:59:24 -04:00
Jeff Becker
970557660e Add NetDb::VisitRandomRouterInfos 2016-08-30 15:54:53 -04:00
Jeff Becker
fa8548fe34 implement SetCustomPeerSelector and ensure locking is good 2016-08-30 15:11:39 -04:00
Jeff Becker
ac88c1a8f1 add ClientDestination::Ready 2016-08-30 13:27:57 -04:00
xcps
f2893097a7 check before bind to 127.x.x.x 2016-08-30 02:53:26 +05:00
Jeff Becker
c0cba7b376 move ready to run 2016-08-29 16:59:17 -04:00
Jeff Becker
87d1058de3 fix 2016-08-29 16:57:34 -04:00
xcps
85e65da492 server tunnel on linux binds on 127.x.x.x 2016-08-30 01:48:47 +05:00
Jeff Becker
ce97fa87e7 don't use std::promise * 2016-08-29 15:34:59 -04:00
Jeff Becker
10ffdb2766 add NetDb::WaitForReady 2016-08-29 15:26:19 -04:00
Jeff Becker
fec49e5609 add hooks for visiting netdb 2016-08-29 14:16:29 -04:00
Jeff Becker
28fdd992c9 add hooks for custom peer selection 2016-08-29 12:09:37 -04:00
Jeff Becker
048d3c8386 Merge remote-tracking branch 'purple/openssl' 2016-08-29 10:56:27 -04:00
orignal
50e3d6ff37 Merge pull request #623 from majestrate/fix_streaming_hang
make sure m_RTO > 0 in Streaming.cpp so it doesn't hang
2016-08-29 10:48:15 -04:00
Jeff Becker
37b80f0ce3 make sure m_RTO > 0 in Streaming.cpp so it doesn't hang 2016-08-29 10:44:54 -04:00
Jeff Becker
7d37b02cff datagram fixes 2016-08-29 10:42:51 -04:00
Jeff Becker
c6556b8442 make sure m_RTO > 0 in Streaming.cpp so it doesn't hang 2016-08-29 10:41:15 -04:00
Jeff Becker
5685c376cb fix broken build 2016-08-27 16:13:11 -04:00
Jeff Becker
2ce64e1bf5 fix typo 2016-08-27 16:10:18 -04:00
Jeff Becker
7d03a41e3e try manual expiration of tags 2016-08-27 16:09:02 -04:00
Jeff Becker
35b68db847 schedule cleanup again and add logging 2016-08-27 15:45:56 -04:00
Jeff Becker
0b21fce94e try adding garlic and session tags to datagram destination 2016-08-27 13:17:34 -04:00
Jeff Becker
abaf36a2de try unbreaking static build 2016-08-27 09:29:18 -04:00
hagen
26440d94f1 * HTTPServer : keep response data for async_write() 2016-08-26 14:42:34 +00:00
hagen
205b61e4cf * HTTPServer : fix tag 2016-08-26 14:42:34 +00:00
orignal
fc5fc5bbee don't throw exception if connection failed 2016-08-26 10:06:28 -04:00
orignal
c4171a01bd fix #622. extract correct CN 2016-08-26 09:48:19 -04:00
orignal
32669cb07f stop termination timer on shutdown 2016-08-24 12:34:18 -04:00
Jeff Becker
7018c381ee Merge remote-tracking branch 'purple/openssl' 2016-08-24 11:42:35 -04:00
orignal
b02677ee21 common termination timer for all SSU sessions 2016-08-24 11:21:49 -04:00
Jeff Becker
065d01bcf6 logging update 2016-08-22 18:29:12 -04:00
Jeff Becker
42b15e8bbe fix 2016-08-22 17:31:23 -04:00
Jeff Becker
e8195b78ba fix 2016-08-22 17:26:43 -04:00
Jeff Becker
1d7d7cf9a0 more changes 2016-08-22 17:19:22 -04:00
Jeff Becker
979575c311 fix 2016-08-22 13:59:51 -04:00
Jeff Becker
be12739342 fix 2016-08-22 13:55:44 -04:00
Jeff Becker
7f7acd8bde fixes 2016-08-22 13:54:00 -04:00
Jeff Becker
f5e2899275 post work to io service 2016-08-22 13:04:36 -04:00
Jeff Becker
bee34a3222 fix 2016-08-21 22:54:06 -04:00
Jeff Becker
5b00cb1e64 osx fix 2016-08-21 22:38:41 -04:00
Jeff Becker
6bb9de5a96 osx fix 2016-08-21 22:34:48 -04:00
Jeff Becker
b977050caf osx fix 2016-08-21 22:29:55 -04:00
Jeff Becker
3f63732c31 osx fix 2016-08-21 22:26:30 -04:00
Jeff Becker
211660eb3d osx fix 2016-08-21 22:23:27 -04:00
Jeff Becker
0c709f431f osx fix 2016-08-21 22:18:59 -04:00
Jeff Becker
9062bf14b6 osx fix 2016-08-21 22:16:35 -04:00
Jeff Becker
47ebb6ae6c osx fix 2016-08-21 22:11:41 -04:00
Jeff Becker
b1e3f88704 osx fix 2016-08-21 22:00:31 -04:00
Jeff Becker
bc439cc47f osx fix 2016-08-21 21:57:36 -04:00
Jeff Becker
1bba5d5c94 osx fix 2016-08-21 21:55:00 -04:00
Jeff Becker
d159d49700 os x fix 2016-08-21 21:51:32 -04:00
Jeff Becker
7ef7ef03dd fix 2016-08-21 21:47:56 -04:00
Jeff Becker
808b758cd7 fix 2016-08-21 21:45:08 -04:00
Jeff Becker
ff6d66b96e init addressbook first 2016-08-21 21:40:21 -04:00
Jeff Becker
da82b14307 changes 2016-08-21 21:28:24 -04:00
Jeff Becker
7b5e18d94b changes 2016-08-21 21:17:09 -04:00
Jeff Becker
72974c85c8 try fix 2016-08-21 20:23:39 -04:00
Jeff Becker
28627a81dc update 2016-08-21 20:18:41 -04:00
Jeff Becker
bbfe6b66ef fix 2016-08-21 19:48:47 -04:00
Jeff Becker
bce0e3ebf6 fix 2016-08-21 19:45:32 -04:00
Jeff Becker
bf46c241d0 fixes 2016-08-21 19:38:12 -04:00
Jeff Becker
287e32aaed logging 2016-08-21 19:33:33 -04:00
Jeff Becker
aa11a5deb8 fix 2016-08-21 19:27:01 -04:00
Jeff Becker
194d63acd8 fixes 2016-08-21 19:17:08 -04:00
Jeff Becker
46d640cd86 fixes 2016-08-21 18:46:34 -04:00
Jeff Becker
51783a45e6 set last activity 2016-08-21 15:56:52 -04:00
Jeff Becker
2679c58892 logging and ip checks 2016-08-21 15:51:39 -04:00
Jeff Becker
2a5af37075 retrgiger expiration 2016-08-21 15:47:00 -04:00
Jeff Becker
e529d3ecc9 fixes 2016-08-21 15:39:11 -04:00
Jeff Becker
e8f9ecc7d9 fixes 2016-08-21 15:33:19 -04:00
Jeff Becker
aa3723d2bd udp tunnels 2016-08-21 15:02:17 -04:00
hagen
bbbda44218 * HTTPServer : show termination time in main page (closes #506) 2016-08-21 13:49:11 +00:00
hagen
f99aea5cb1 * Makefile.linux : use linker flags instead full paths to obj files (#602) 2016-08-21 13:49:08 +00:00
orignal
65c003eef8 Merge pull request #615 from AMDmi3/miniupnpc-library-detection
Find miniupnpc library as well as header
2016-08-19 07:01:35 -04:00
Dmitry Marakasov
959843ee9c Find miniupnpc library as well as header 2016-08-19 12:16:28 +03:00
Jeff Becker
c16632d99a Merge remote-tracking branch 'purple/master' 2016-08-18 18:37:32 -04:00
orignal
3d066ea1b8 common termination timer for all NTCP sessions 2016-08-17 10:58:57 -04:00
orignal
e163730118 Merge pull request #614 from PurpleI2P/openssl
recent changes
2016-08-17 10:37:09 -04:00
Jeff Becker
3c8838af08 Merge branch 'upstream-master' 2016-08-16 10:55:54 -04:00
orignal
ac5394a1dc Merge pull request #613 from majestrate/fix-static-cmake
Fix static cmake
2016-08-16 10:51:26 -04:00
Jeff Becker
2e74d91ddc try fixing https://github.com/PurpleI2P/i2pd/issues/612 2016-08-16 10:27:33 -04:00
Jeff Becker
2d82c4ada4 try fixing https://github.com/PurpleI2P/i2pd/issues/612 2016-08-16 10:25:56 -04:00
orignal
03f0ca965e fixed race condition 2016-08-15 22:36:58 -04:00
orignal
a527dcd95b moved HTTP to libi2pd 2016-08-15 14:01:57 -04:00
orignal
de29abb05c check string buffer size 2016-08-15 13:12:56 -04:00
orignal
cb7efcb188 add 'O' to extra bandwidth for flooadfill 2016-08-14 17:58:50 -04:00
orignal
bf4f22b203 add 'O' to extra bandwidth 2016-08-14 17:52:11 -04:00
orignal
7f3a467a66 Merge pull request #608 from atnaguzin/patch-1
Updated debian/changelog
2016-08-13 14:47:28 -04:00
MXPLRS | Kirill
72ef621f9d Update changelog 2016-08-13 16:52:51 +03:00
MXPLRS | Kirill
73452f758c Update changelog 2016-08-13 16:52:27 +03:00
MXPLRS | Kirill
049e1b2679 Update changelog 2016-08-13 16:50:08 +03:00
orignal
4631123231 reseed-ru.lngserv.ru added 2016-08-13 09:05:35 -04:00
orignal
c86bcb4dd6 r4sas_at_mail.i2p.crt added 2016-08-13 08:23:32 -04:00
orignal
a6280661ee Merge pull request #607 from atnaguzin/patch-1
Updated i2pd.conf
2016-08-12 16:29:04 -04:00
MXPLRS | Kirill
ca7709a284 Update i2pd.conf 2016-08-12 23:23:10 +03:00
orignal
384c06f2e9 Merge pull request #606 from atnaguzin/patch-1
Updates in debian part.
2016-08-12 16:20:30 -04:00
MXPLRS | Kirill
774c11781d Update changelog 2016-08-12 22:47:20 +03:00
MXPLRS | Kirill
7a692898e4 moved tunnels.conf 2016-08-12 22:36:31 +03:00
MXPLRS | Kirill
2f1971ea8f moved tunnels.conf 2016-08-12 22:36:17 +03:00
MXPLRS | Kirill
ce13de7d6c moved subscriptions.txt 2016-08-12 22:35:09 +03:00
MXPLRS | Kirill
d51ad77ab4 moved subscriptions.txt 2016-08-12 22:34:29 +03:00
MXPLRS | Kirill
a9b289626e Update logrotate 2016-08-12 22:30:26 +03:00
MXPLRS | Kirill
8a542f2ce8 Update i2pd.upstart 2016-08-12 22:29:32 +03:00
MXPLRS | Kirill
14a2c9d48f Update i2pd.install 2016-08-12 22:28:35 +03:00
orignal
37fef7e4f8 Update ChangeLog 2016-08-12 15:21:55 -04:00
MXPLRS | Kirill
b83ab85fd9 Update i2pd.init 2016-08-12 22:21:33 +03:00
orignal
d424e1e9ff Update ChangeLog 2016-08-12 15:19:45 -04:00
MXPLRS | Kirill
aaa52bd767 Update i2pd.default 2016-08-12 22:17:25 +03:00
Jeff Becker
56254e728c Merge tag 'tags/2.9.0' 2016-08-12 13:25:25 -04:00
orignal
284159aadc Merge pull request #605 from manasb/manasb-patch-docs
fix a typo
2016-08-12 12:56:33 -04:00
Manas B
29593f0161 fix a typo 2016-08-12 12:43:59 -04:00
orignal
deca217544 don't always set port 4567 2016-08-12 11:07:00 -04:00
orignal
c09212de81 Merge pull request #604 from PurpleI2P/openssl
recent changes
2016-08-12 10:44:02 -04:00
orignal
db8d93d308 2.9.0 2016-08-12 10:43:06 -04:00
orignal
4c96106666 reseed.file added 2016-08-12 10:37:03 -04:00
orignal
8e849ea6f8 reseed from file 2016-08-12 10:33:53 -04:00
orignal
82d80d2ead moved Config.cpp to libi2pd 2016-08-12 10:28:36 -04:00
orignal
a5da55d0f7 Update build_notes_android.md 2016-08-12 09:38:31 -04:00
orignal
702e6c8080 buld instruction without QT 2016-08-12 09:30:35 -04:00
orignal
46e957ab7e Merge pull request #603 from atnaguzin/patch-2
Replaced arrows to HTML code. Deleted tab spaces.
2016-08-11 07:20:54 -04:00
MXPLRS | Kirill
c27f8a5c1e Replaced arrows to HTML code. Deleted tab spaces. 2016-08-11 12:25:26 +03:00
orignal
8e835f2f6b fixed race condition 2016-08-09 20:51:54 -04:00
orignal
35e8a027ad Merge pull request #600 from brain5lug/openssl
copy elimination for ranges #part4
2016-08-09 20:50:44 -04:00
brain5lug
94642f9066 copy elimination for ranges #part4 2016-08-10 01:16:24 +03:00
orignal
2dd5de4373 handle default subscription in separate thread 2016-08-09 10:17:40 -04:00
orignal
793e80490c Merge pull request #598 from brain5lug/openssl
copy elimination for ranges #part3
2016-08-09 07:25:24 -04:00
orignal
727a1f4ddd Merge pull request #599 from atnaguzin/patch-1
Update HTTPServer.cpp
2016-08-09 07:22:34 -04:00
MXPLRS | Kirill
6a752a56ff Update HTTPServer.cpp 2016-08-09 13:54:47 +03:00
orignal
32466e3804 don't notify before wait 2016-08-08 22:15:09 -04:00
brain5lug
a530503c0c copy elimination for ranges #part3 2016-08-09 01:53:37 +03:00
orignal
7ba4af7e2e fixed build error 2016-08-08 17:31:32 -04:00
orignal
c9c05ad2a8 Merge pull request #596 from atnaguzin/patch-2
"SAM sessions" link check added
2016-08-08 17:23:58 -04:00
MXPLRS | Kirill
ab1df3a1d0 Update HTTPServer.cpp 2016-08-08 23:37:16 +03:00
orignal
56a60772a4 fixed potential deadlock 2016-08-08 11:53:38 -04:00
orignal
63e6731207 Merge pull request #595 from brain5lug/openssl
copy elimination for ranges #part2
2016-08-07 18:31:32 -04:00
brain5lug
8b53ded53a copy elimination for ranges #part2 2016-08-08 00:52:48 +03:00
orignal
d5075d706c eliminate decay timer 2016-08-07 16:27:36 -04:00
Jeff Becker
03927b0a68 Merge branch 'upstream-openssl' 2016-08-06 20:27:57 -04:00
orignal
0d88b8012b Merge pull request #591 from brain5lug/openssl
The temporary object elemination part#1
2016-08-05 19:33:19 -04:00
brain5lug
a583c21136 merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-08-06 01:26:34 +03:00
brain5lug
b8ec63cf8c copy ellimination for ranges #part1 2016-08-06 01:03:13 +03:00
l-n-s
4b9afdf53a fix typo 2016-08-05 18:06:06 +00:00
orignal
788d1650a2 Merge pull request #590 from brain5lug/openssl
tiny commit to check pulling
2016-08-04 17:31:52 -04:00
brain5lug
8f58886a21 tiny commit to check pulling 2016-08-04 23:27:07 +03:00
orignal
94b3bb2391 adjust termination timeout 2016-08-04 10:26:50 -04:00
Jeff Becker
c8f5fb4d03 close duplicate ntcp sessions 2016-08-03 10:40:30 -04:00
Jeff Becker
070a21a9eb Merge remote-tracking branch 'purple/openssl' 2016-07-31 19:29:12 -04:00
orignal
5698ff9c4c wait for UPnP discovery during startup 2016-07-31 10:22:41 -04:00
orignal
47b562b032 temporary disable OS X 2016-07-30 18:22:14 -04:00
orignal
69234db848 Merge pull request #588 from atnaguzin/patch-1
Update 01-tune-build-opts.patch
2016-07-30 14:58:09 -04:00
MXPLRS | Kirill
ebc132ea65 Update 01-tune-build-opts.patch 2016-07-30 21:27:17 +03:00
orignal
0899eeddc0 UPnP for x86_64 2016-07-29 10:59:44 -04:00
Jeff Becker
cf8ff2cf86 make it compile 2016-07-28 15:35:13 -04:00
Jeff Becker
ee9dc789af change scope of Stream::Terminate 2016-07-28 15:34:32 -04:00
Jeff Becker
570598f556 abruptly close 2016-07-28 15:33:03 -04:00
Jeff Becker
e8c3546433 try fixing 2016-07-28 13:27:51 -04:00
orignal
1062776762 cleanup router's tags 2016-07-28 13:24:25 -04:00
Jeff Becker
584379b502 fix 2016-07-28 12:48:32 -04:00
Jeff Becker
87e1c45c05 fug 2016-07-28 12:34:33 -04:00
Jeff Becker
9447afe49c try changing i2cp option 2016-07-28 12:32:05 -04:00
Jeff Becker
fc5b1ae3e2 fug 2016-07-28 12:28:18 -04:00
Jeff Becker
13735d0475 move setting 2016-07-28 12:26:05 -04:00
Jeff Becker
6b3a783ce9 change type 2016-07-28 12:21:59 -04:00
Jeff Becker
3b66bba92e more fixes 2016-07-28 12:17:24 -04:00
Jeff Becker
a2e01f8a53 more tweaks 2016-07-28 11:42:31 -04:00
Jeff Becker
34da9a9655 streaming limiting tweaks 2016-07-28 11:37:33 -04:00
Jeff Becker
df8d73ae43 typo 2016-07-28 11:20:24 -04:00
Jeff Becker
aa3a93b6a0 implement streaming limiting (initial) 2016-07-28 11:16:29 -04:00
Jeff Becker
17bfa35f77 don't use warning for no tags 2016-07-28 10:02:26 -04:00
Jeff Becker
59797a5c9a streaming log tweaks and dont set RTT to 0 2016-07-28 10:01:20 -04:00
Jeff Becker
61fe2923e4 don't set socket option for closed sockets 2016-07-28 09:53:39 -04:00
Jeff Becker
50b9eca34c check for bogus times in packets 2016-07-28 09:50:19 -04:00
Jeff Becker
f5684eba90 color log messages for warn and error 2016-07-28 09:49:43 -04:00
Jeff Becker
f32510e10a set socket options 2016-07-28 09:25:05 -04:00
Jeff Becker
4fb0eeda37 Merge remote-tracking branch 'purple/openssl' 2016-07-28 09:06:58 -04:00
hagen
b1aeae6772 * util.{cpp,h} : kill with fire i2p::util::http (#314, closes #432) 2016-07-27 13:10:29 +00:00
hagen
5a6bd38d22 * docs/configuration.md 2016-07-27 13:10:29 +00:00
hagen
97da8e2f2e * HTTPServer.cpp : true random password 2016-07-27 13:10:28 +00:00
orignal
10be150503 invoke GracefulShutdown for Win32 2016-07-26 12:11:52 -04:00
orignal
36aa248556 Graceful shutdown 2016-07-26 11:52:44 -04:00
orignal
183c22cc84 rollback 2016-07-26 11:22:53 -04:00
orignal
63f4cf3d07 graceful shutdown for windows 2016-07-26 11:10:10 -04:00
Jeff Becker
eeeae12639 check for correctly loaded privatekeys 2016-07-25 11:13:54 -04:00
Jeff Becker
0d854c6ea6 Merge remote-tracking branch 'purple/master' 2016-07-25 10:29:10 -04:00
Jeff Becker
1e1c4d159b do reload 2016-07-25 10:28:20 -04:00
Jeff Becker
4dc9f6948d bounds checks 2016-07-25 09:57:35 -04:00
orignal
c06e739c9b Merge pull request #583 from PurpleI2P/openssl
recent changes
2016-07-25 09:43:18 -04:00
orignal
061720bcf0 handle'\r\n' terminated address from Transmission 2016-07-24 10:20:37 -04:00
orignal
11585327bf correct status response 2016-07-24 09:24:20 -04:00
orignal
2793eeb10a Merge pull request #581 from PurpleI2P/reloadconfig_on_SIGHUP
Reload client config on SIGHUP
2016-07-24 08:15:00 -04:00
Mikal Villa
8fb093b272 Reload client config on SIGHUP 2016-07-24 17:58:26 +08:00
Jeff Becker
c90c008f65 Merge remote-tracking branch 'purple/openssl' 2016-07-23 21:22:02 -04:00
orignal
3d1a7f173c select Charlie based on Alice's address type 2016-07-22 13:08:41 -04:00
orignal
ba078f3ff5 support peer test for ipv6 2016-07-22 12:50:03 -04:00
Jeff Becker
6d54401d7c Merge remote-tracking branch 'devnull/linux_tunnel_reload_on_hup' 2016-07-22 12:42:59 -04:00
Jeff Becker
c9a0897208 Merge remote-tracking branch 'purple/openssl' 2016-07-22 11:57:48 -04:00
orignal
6a1049bfb7 override address if v6 only 2016-07-22 10:34:56 -04:00
Jeff Becker
5995ab3f4c Merge branch 'upstream-openssl' 2016-07-22 10:18:58 -04:00
orignal
a4112ebed2 add both v4 and v6 addresses 2016-07-22 10:16:57 -04:00
Jeff Becker
289cae4213 Merge remote-tracking branch 'purple/openssl' 2016-07-22 09:59:56 -04:00
Jeff Becker
eaac21cda1 * check router info addresses for nullptr
* Request LS before expiration for smoother handover
2016-07-22 09:56:17 -04:00
orignal
3b9b827ebf getnick doean't return error if was set before 2016-07-21 20:42:40 -04:00
/dev/null
c908beade2 Added client tunnel reload on SIGHUP for Linux 2016-07-21 17:57:43 -06:00
orignal
abcf030181 more BOB error messages 2016-07-21 14:02:13 -04:00
orignal
22a16da09e fixed android build 2016-07-20 21:44:17 -04:00
orignal
0e31da5e51 Merge pull request #578 from PurpleI2P/openssl
recent changes
2016-07-20 18:33:43 -04:00
orignal
61e6a03d70 check correct #ifdef for windows 2016-07-20 09:33:50 -04:00
orignal
3f2119556f upnp.name 2016-07-19 12:05:32 -04:00
orignal
77493d0d09 configurable UPnP name 2016-07-19 12:03:03 -04:00
orignal
340c73cbdf send actual status back 2016-07-19 11:08:28 -04:00
hagen
ea7f4447b2 * Reseed.cpp : check response code 2016-07-19 00:59:16 +00:00
hagen
19f3c75a8d * Reseed.cpp : use new response parsing 2016-07-19 00:59:15 +00:00
hagen
25ba08abcf * Reseed.cpp : use new http classes 2016-07-19 00:59:13 +00:00
hagen
26be0c7c82 * move i2p::util::config::GetHost to RouterContext.cpp 2016-07-19 00:59:11 +00:00
orignal
b7c5e3b5d5 correct termination of UPnP 2016-07-18 14:01:58 -04:00
Jeff
701653a6bd Merge pull request #576 from rjmalagon/patch-1
Update BOB.cpp
2016-07-16 12:18:51 -04:00
orignal
2c099c7f0e Update BOB.cpp
fixed typo
2016-07-16 10:21:07 -04:00
Ricardo Jesus Malagon Jerez
fba53117d8 Update BOB.cpp
Fix a little typo
2016-07-16 09:21:01 -05:00
orignal
e1bf53d90a handle status command 2016-07-16 09:31:33 -04:00
hagen
ecc82739d8 * HTTP.cpp : fix is_gzipped() 2016-07-16 12:13:00 +00:00
hagen
d16afa9692 * Addressbook.cpp : tune logs 2016-07-16 12:11:23 +00:00
hagen
9f3ce09e88 * Addressbook.{cpp,h} : show new hosts 2016-07-16 12:11:23 +00:00
hagen
cbfb1edb79 * Addressbook.{cpp,h}:
* Request() now renamed and returns value
  * move spawning download thread to Addressbook class
  * CheckSubscription() renamed and handles return value of Request()
2016-07-16 12:11:10 +00:00
hagen
b6c336bf72 * Addressbook.cpp : use HTTPRes class for response parsing 2016-07-16 12:10:43 +00:00
hagen
403e34506e * Addressbook.cpp : use HTTPReq class for building request 2016-07-16 12:10:37 +00:00
hagen
83db868542 * Addressbook.cpp : use new URL class, unwrap some conditions 2016-07-16 12:10:34 +00:00
orignal
1b6e673b50 Merge pull request #574 from majestrate/master
Floodfill fixes, testnet fixes, add lease set view page in HTTPServer
2016-07-15 14:52:12 -04:00
Jeff Becker
a0144f093f fix typo 2016-07-15 13:59:56 -04:00
Jeff Becker
a5d84bf8a9 pedantic whitespace fix 2016-07-15 13:54:34 -04:00
Jeff Becker
84bb740e62 clean up code 2016-07-15 13:52:55 -04:00
Jeff Becker
d37482ada1 IT WORKS, floodfill confirmed working on test network 2016-07-15 13:31:31 -04:00
Jeff Becker
75fc8202ab fix off by ones 2016-07-15 12:49:45 -04:00
Jeff Becker
24aff15752 off by one 2016-07-15 10:55:02 -04:00
Jeff Becker
338b9928f0 repeat correction 2016-07-15 10:45:38 -04:00
Jeff Becker
b9607b4b8e correct last commit 2016-07-15 10:44:43 -04:00
Jeff Becker
586f241074 OFF BY ONE 2016-07-15 10:44:08 -04:00
orignal
e298987d9e fixed build error 2016-07-15 09:45:54 -04:00
Jeff Becker
71cc4b5bf2 add lease set page in http server 2016-07-15 09:38:21 -04:00
Jeff Becker
4fdd9feddc don't try to republish forever 2016-07-14 16:59:26 -04:00
orignal
caf7da1053 set reachable/unreachable for v4 only 2016-07-14 14:29:45 -04:00
Jeff Becker
7d04ba0fc3 CXXFLAGS -> NEEDED_CXXFLAGS 2016-07-14 14:25:20 -04:00
orignal
d98d091c43 use list instead vector for addresses 2016-07-14 14:10:38 -04:00
Jeff Becker
3ad196c4c7 fix meshnet mode:
* don't default to ipv4 when creating router.info
* add i2p::util::config::GetHost for getting host to use from config
* proper check for no transports in Transports.cpp on startup
2016-07-14 09:23:33 -04:00
orignal
562f320198 load libstd 2016-07-13 22:34:53 -04:00
orignal
3e5581e094 create addresses in defualt constructor 2016-07-13 22:33:39 -04:00
hagen
c4721e1020 + HTTP.{cpp,h} : add HTTPRes::is_gzipped() 2016-07-14 00:34:42 +00:00
hagen
728f2670f3 * HTTPServer.cpp : drop jumpservices : now handled by HTTPProxy itself 2016-07-14 00:34:38 +00:00
hagen
8dd157d2eb * HTTPProxy.cpp : html error messages 2016-07-14 00:32:41 +00:00
orignal
2d40d69fa2 fixed race condition 2016-07-13 12:56:23 -04:00
orignal
f2f0d69bce Update configuration.md 2016-07-13 11:14:06 -04:00
orignal
812f5045b0 enable UPnP for windows and android by default 2016-07-13 11:12:51 -04:00
orignal
9a8e7b11e5 detect network status at android 2016-07-13 10:09:22 -04:00
orignal
e213e695c8 Merge pull request #572 from hypnosis-i2p/openssl
reworked android. added a build script.
2016-07-13 08:46:23 -04:00
hypnosis-i2p
e24eea313c updated 2016-07-13 19:40:09 +08:00
hypnosis-i2p
a3286ebac3 updated 2016-07-13 19:36:18 +08:00
hypnosis-i2p
95ae23a32c gitignore 2016-07-13 11:08:35 +08:00
hypnosis-i2p
d240f3242c gitignore 2016-07-13 11:08:35 +08:00
hypnosis-i2p
814f60a512 reworked android. added a build script. 2016-07-13 11:08:35 +08:00
hagen
fac6229e43 * cmake debug (closes #562) 2016-07-13 01:01:47 +00:00
hagen
c528d739c8 Merge branch 'new-proxy' into openssl 2016-07-13 00:49:00 +00:00
orignal
c664be52d7 limit outgoing queue size 2016-07-12 16:26:36 -04:00
orignal
4ac4f44ba7 limit delayed messages queue size 2016-07-12 12:37:39 -04:00
hagen
174430e3b5 * HTTPServer.cpp : rename command 2016-07-12 02:30:39 +00:00
hagen
762b21f809 * Streaming.cpp : tune log messages 2016-07-12 02:23:24 +00:00
hagen
9340bf385e * Daemon.cpp : make upnp configurable via options 2016-07-12 02:21:52 +00:00
hagen
9f5be52a97 * UPnP.cpp : tune log messages 2016-07-12 02:21:52 +00:00
hagen
9dc5a4fce3 * UPnP.{cpp,h} : cleanup & add class stub if opt-out 2016-07-12 02:21:52 +00:00
hagen
fda3cd5fe7 * Config.cpp : add --upnp.enabled option 2016-07-12 02:21:52 +00:00
hagen
6b8469e9a3 * docs/configuration.md : fix markdown 2016-07-12 02:21:52 +00:00
orignal
7dbbe5a7d8 wait until tunnels get created 2016-07-11 14:35:59 -04:00
orignal
2d6fdeb7ad Merge pull request #567 from hypnosis-i2p/openssl
android without qt — initial push
2016-07-09 21:50:33 -04:00
hypnosis-i2p
4d3a01a5fe android ported all + isConnected notif 2016-07-10 09:42:42 +08:00
orignal
8cc6756815 Merge pull request #566 from PurpleI2P/openssl
recent changes
2016-07-09 21:41:43 -04:00
hypnosis-i2p
5967ab75b1 Merge branch 'openssl' of https://github.com/hypnosis-i2p/i2pd into openssl 2016-07-10 04:54:15 +08:00
hypnosis-i2p
69c954760a android without qt initial commit 2016-07-10 04:54:11 +08:00
hypnosis-i2p
40a4c3ccbd junk 2016-07-10 00:19:20 +08:00
hypnosis-i2p
aacb9d9570 merged 2016-07-10 00:19:20 +08:00
orignal
9b6c229b71 remember tunnels selection for following messages 2016-07-08 14:17:41 -04:00
orignal
1da5be2871 clean up unconfirmed tags faster 2016-07-07 22:39:20 -04:00
orignal
66dafca61a select existing connection for first hop of a tunnel 2016-07-06 22:34:24 -04:00
hypnosis-i2p
1f22b5b083 junk 2016-07-07 02:46:11 +08:00
hypnosis-i2p
5be0b7a731 merged 2016-07-07 02:45:13 +08:00
l-n-s
b64b5d9103 Update build_notes_android.md 2016-07-06 17:55:36 +00:00
l-n-s
953d78da9e Update build_notes_android.md 2016-07-06 16:10:30 +00:00
l-n-s
ce9e0981a2 Update index.rst 2016-07-06 16:03:54 +00:00
orignal
76e1114a1f Update build_notes_android.md 2016-07-06 11:30:11 -04:00
orignal
cfc80b491f Update build_notes_android.md 2016-07-06 11:29:43 -04:00
orignal
43ed05d3c2 Create build_notes_android.md 2016-07-06 11:29:00 -04:00
orignal
4cf5ce871f destroy socket upon receive an ack for close 2016-07-05 17:52:11 -04:00
orignal
91ec08df4e wait for close from other side 2016-07-05 09:52:18 -04:00
orignal
c79363ef63 Merge pull request #563 from PurpleI2P/openssl
recent changes
2016-07-05 09:40:17 -04:00
orignal
cc6672198a Merge pull request #560 from atnaguzin/openssl
updated appveyor.yml
2016-07-02 07:14:21 -04:00
MXPLRS | Kirill
17cdf7c79d Merge pull request #7 from PurpleI2P/openssl
fixed VS build error
2016-07-02 13:52:18 +03:00
orignal
725f939f35 fixed VS build error 2016-07-02 06:45:15 -04:00
MXPLRS | Kirill
e2df00bb2e Merge pull request #6 from PurpleI2P/openssl
Upstream pull
2016-07-01 15:34:01 +03:00
orignal
a9a33c6179 fixed build error 2016-07-01 08:31:27 -04:00
orignal
8e7eb87a2d Merge pull request #558 from atnaguzin/patch-1
Update i2pd_qt.pro
2016-07-01 08:10:25 -04:00
MXPLRS | Kirill
217004e7a5 Update appveyor.yml 2016-07-01 13:22:47 +03:00
MXPLRS | Kirill
4d10848984 Update i2pd_qt.pro 2016-07-01 13:07:24 +03:00
hagen
da2c04f681 * HTTPProxy.cpp : show created stream IDs in log 2016-07-01 00:39:07 +00:00
hagen
8deb327b3b * HTTPProxy.cpp :
* migrate to HTTPReq
  * change work with buffers
  * code cleanup
2016-07-01 00:38:55 +00:00
hagen
642b01bf0d * HTTPProxy.cpp : add SanitizeHTTPRequest() 2016-07-01 00:38:31 +00:00
hagen
9fd78b1eb1 * HTTPProxy.cpp : rename variable 2016-07-01 00:38:31 +00:00
hagen
66c09fc44c * HTTPProxy.cpp : HandleJumpServices() -> ExtractAddressHelper() 2016-07-01 00:38:31 +00:00
hagen
2cb5e1a6c2 * HTTPProxy.cpp : kill ExtractRequest() 2016-07-01 00:38:31 +00:00
hagen
02ac638bd4 * HTTP.cpp : add comments, update test case 2016-07-01 00:34:31 +00:00
hagen
323f74c43a * HTTP.cpp : fuck the "special cases", use nginx rewriting frontend or some 2016-07-01 00:34:28 +00:00
MXPLRS | Kirill
1e56d17d39 Merge pull request #5 from PurpleI2P/openssl
upstream pull
2016-07-01 02:12:09 +03:00
orignal
16af8e082b Merge pull request #557 from majestrate/merge-testnet-changes
Merge testnet changes
2016-06-30 18:24:49 -04:00
Jeff Becker
9215a54c23 revert 2016-06-30 18:05:41 -04:00
Jeff Becker
fab34d3dbb clean up identation 2016-06-30 17:59:14 -04:00
orignal
03d7330af5 Merge pull request #556 from PurpleI2P/openssl
floodfill fixes
2016-06-30 17:51:26 -04:00
Jeff Becker
346b0c9d68 disable testnet by default 2016-06-30 17:50:47 -04:00
Jeff Becker
c29359e7a8 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 17:48:46 -04:00
Jeff Becker
4b903931bc update i2pd testnet addressbook url 2016-06-30 17:34:16 -04:00
Jeff Becker
4e0929e71a don't print out junk when logging 2016-06-30 17:24:33 -04:00
Jeff Becker
1bad097a13 don't check for LS expired for FF 2016-06-30 17:21:18 -04:00
Jeff Becker
331065eec6 remove expired LS 2016-06-30 17:01:00 -04:00
orignal
f51ba499d5 Merge pull request #555 from majestrate/fix-ff-off-by-one
off by one?
2016-06-30 15:07:26 -04:00
Jeff Becker
f62ccc2d48 off by one? 2016-06-30 14:26:05 -04:00
orignal
cf485aa62e Merge pull request #554 from atnaguzin/openssl
added hiding information in webconsole
2016-06-30 14:25:39 -04:00
Jeff Becker
6b16a48568 revert 2016-06-30 14:24:55 -04:00
MXPLRS|Kirill
ff7cf503ae added hiding information in webconsole 2016-06-30 21:21:37 +03:00
Jeff Becker
2412a0d502 off by one? 2016-06-30 13:35:36 -04:00
MXPLRS | Kirill
b1612bb1ed Merge pull request #4 from PurpleI2P/openssl
Upstream pull
2016-06-30 20:30:45 +03:00
Jeff Becker
b8a205f755 netdb.cpp:
* explicitly define replyIdent

Daemon.cpp:
* wait for 1 second before checking if transports are bound to wait for transports to bind
2016-06-30 13:25:20 -04:00
orignal
eab08ea78c don't accept our own RouterInfo 2016-06-30 13:15:36 -04:00
Jeff Becker
d7653769b4 more logging 2016-06-30 12:57:20 -04:00
Jeff Becker
c72d9695da testnet changes 2016-06-30 12:27:23 -04:00
orignal
0493f00a7a pass null tunnel config for zero hops tunnel 2016-06-30 12:24:26 -04:00
Jeff Becker
ff757ddc88 don't check for exact LS 2016-06-30 11:27:40 -04:00
Jeff Becker
c4c495948a don't flood if older 2016-06-30 11:10:01 -04:00
Jeff Becker
986ee6bac3 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 11:03:15 -04:00
orignal
6ab7e79987 send own copy of a message during flood 2016-06-30 11:01:21 -04:00
Jeff Becker
c6e35876fa if LS is older, assume updated so we reply 2016-06-30 10:51:39 -04:00
Jeff Becker
eb31accf20 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 10:25:44 -04:00
orignal
ea1ba0f09b don't flood to itself 2016-06-30 10:21:53 -04:00
Jeff Becker
10911f5b64 make it compile 2016-06-30 09:52:37 -04:00
Jeff Becker
0eab8e9322 more pedantic logging changes 2016-06-30 09:49:28 -04:00
Jeff Becker
fb2602716e add floodfill to exclude 2016-06-30 09:48:08 -04:00
Jeff Becker
be68906ae9 logging tweaks 2016-06-30 09:45:06 -04:00
Jeff Becker
c53d6d80ba Merge branch 'master' into meshnet 2016-06-30 09:15:30 -04:00
Jeff Becker
1362177352 Merge remote-tracking branch 'purple/openssl' 2016-06-30 08:57:59 -04:00
xcps
28ab1230e2 '@' can exist in url path 2016-06-30 08:00:18 -04:00
orignal
0784934520 Merge pull request #551 from majestrate/http-router-ident
add RouterInfo hash to web ui
2016-06-30 07:46:28 -04:00
Jeff Becker
79fbf9d47f add RouterInfo hash to web ui 2016-06-30 07:41:40 -04:00
Jeff Becker
dabdc8e4c5 Merge branch 'upstream-openssl' 2016-06-30 07:40:15 -04:00
Jeff Becker
466ad192b0 add RouterInfo hash to web ui 2016-06-30 07:36:35 -04:00
Jeff Becker
ac59d9e1b6 Merge branch 'upstream-openssl' 2016-06-30 07:36:14 -04:00
Jeff Becker
d44245e1e9 add RouterInfo hash to web ui 2016-06-30 07:35:30 -04:00
orignal
c50105493a fixed zero-hops tunnels 2016-06-29 21:37:17 -04:00
Jeff Becker
2768a62f92 more logging 2016-06-29 18:05:08 -04:00
Jeff Becker
5841d0d87d add more specific logging 2016-06-29 17:59:56 -04:00
Jeff Becker
2a796051bf try fixing LS 2016-06-29 17:42:26 -04:00
Jeff Becker
50286fd173 use inet_ntop properly 2016-06-29 16:10:43 -04:00
Jeff Becker
44f0bad2a6 fug 2016-06-29 15:52:32 -04:00
Jeff Becker
611b9c4fd1 ugh 2016-06-29 15:50:39 -04:00
Jeff Becker
3f50776062 ugh 2016-06-29 15:48:02 -04:00
Jeff Becker
c70d2ad6fa try fixing 2016-06-29 15:42:03 -04:00
Jeff Becker
9dd0bd604c fix 2016-06-29 15:37:37 -04:00
Jeff Becker
0565519509 don't print out raw ident 2016-06-29 15:15:33 -04:00
Jeff Becker
c282d95be1 undo change 2016-06-29 15:10:33 -04:00
Jeff Becker
766286b8bc undo pedantic change 2016-06-29 14:57:42 -04:00
Jeff Becker
f405c62f1e pedantic style and logging changes 2016-06-29 14:56:00 -04:00
Jeff Becker
93fbd7b3ba Merge branch 'upstream-openssl' into meshnet 2016-06-29 13:32:39 -04:00
Jeff Becker
ae5cea7f36 change order of initialization 2016-06-29 11:59:43 -04:00
Jeff Becker
8b7b6cfbc5 try fixing segfault 2016-06-29 11:57:44 -04:00
orignal
f6e988d6fd support zero-hops tunnels for destinations 2016-06-29 11:26:46 -04:00
Jeff Becker
f88f68f248 Add bind to network interface option 2016-06-29 11:06:51 -04:00
Jeff Becker
14f2b24b16 update logging 2016-06-29 10:11:14 -04:00
Jeff Becker
f2dde98e2f add meshnet option to cmake build files 2016-06-29 09:37:38 -04:00
Jeff Becker
90d8ec0e81 add more logging 2016-06-29 09:37:21 -04:00
hagen
14cdb531c8 * Streaming.cpp : tune logs 2016-06-29 02:14:39 +00:00
Jeff Becker
3a50320f79 add more logging 2016-06-28 15:35:58 -04:00
Jeff Becker
597b5e6cfb use different constants for now in meshnet mode 2016-06-28 14:50:25 -04:00
Jeff Becker
2cd056cfb3 try banning non responsive routers 2016-06-28 14:43:55 -04:00
Jeff Becker
be6aab4c40 revert 2016-06-28 14:24:18 -04:00
Jeff Becker
d030df925f Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-28 14:21:09 -04:00
Jeff Becker
50756eb94a Merge branch 'restricted_routes' into meshnet 2016-06-28 13:28:57 -04:00
Jeff Becker
21b3576b66 stop using auto 2016-06-28 12:20:18 -04:00
hagen
2e5226356b * Tag.h : add (c) header 2016-06-28 14:31:40 +00:00
hagen
21b76d3d2b * Config.cpp : drop compat parser 2016-06-28 14:31:40 +00:00
hagen
2d252e6459 * HTTP.cpp : rename method 2016-06-28 14:31:40 +00:00
Jeff Becker
43be363542 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-28 09:34:38 -04:00
orignal
8cb3e3418a send garlic cloves directly if garlic was received derectly 2016-06-28 09:31:41 -04:00
Jeff Becker
cd47ddd539 default to USE_MESHNET=yes 2016-06-28 07:57:48 -04:00
orignal
2be1c10522 Tag.h and Gzip.h/.cpp added 2016-06-28 07:11:55 -04:00
hagen
7f26670173 + tests/test-base-64.cpp 2016-06-28 01:45:14 +00:00
hagen
d838ce85c3 * Base.h : extract Tag template class to separate header 2016-06-28 01:45:11 +00:00
hagen
6350f5e6e8 * Base.cpp : extract gzip classes to separate file 2016-06-28 01:45:07 +00:00
orignal
10f3690ede Merge pull request #547 from atnaguzin/openssl
fixed #546
2016-06-27 20:30:31 -04:00
Jeff Becker
06daa8bb0e try shooting in the dark for workarround 2016-06-27 17:39:13 -04:00
Jeff Becker
34a90f442e try shooting in the dark for workarround 2016-06-27 17:37:31 -04:00
Jeff Becker
afe81dcdbe add logging 2016-06-27 17:25:29 -04:00
Jeff Becker
24d616672b revert daemon.cpp change 2016-06-27 17:11:03 -04:00
MXPLRS|Kirill
9cfc61cd45 fixed #546 2016-06-28 00:00:54 +03:00
MXPLRS | Kirill
487df84b90 Merge pull request #3 from PurpleI2P/openssl
Upstream pull
2016-06-27 23:54:53 +03:00
Jeff Becker
614c1306f6 use correct netid when using separate test network 2016-06-27 16:40:46 -04:00
Jeff Becker
07dca9bd16 tweak ssu mtu again for meshnet 2016-06-27 15:28:26 -04:00
Jeff Becker
1ebcbd5b0e use smaller mtu for meshnet mode 2016-06-27 15:06:15 -04:00
Jeff Becker
32644ddada try fixing duplicate Routers In tunnel path 2016-06-27 14:16:29 -04:00
Jeff Becker
cf3bab996e when routers < 5 and in meshnet mode do not select random peers 2016-06-27 14:00:04 -04:00
Jeff Becker
926ffe2581 change default addressbook for meshnet mode 2016-06-27 13:46:14 -04:00
Jeff Becker
fff3587d99 only set as testing when not in meshnet mode 2016-06-27 13:20:21 -04:00
Jeff Becker
866cf940da make always reachable when in meshnet mode 2016-06-27 13:15:05 -04:00
Jeff Becker
7868e1527e try fixing ipv6 ssu 2016-06-27 11:08:23 -04:00
Jeff Becker
556bfb752a disable meshnet by default, use make USE_MESHNET=yes to build for cjdns 2016-06-27 10:33:14 -04:00
Jeff Becker
a3b08654b4 try adding ipv6 only mode for ssu 2016-06-27 10:24:37 -04:00
Jeff Becker
4cf4436169 initial meshnet mode 2016-06-27 09:47:53 -04:00
hagen
e2acc55819 * HTTPProxy.cpp : unwrap HandleStreamRequestComplete() 2016-06-27 13:14:42 +00:00
hagen
6b29d6b8dc * HTTPProxy.cpp : unwrap AsyncSockRead() 2016-06-27 13:14:39 +00:00
hagen
b668c4c302 * add global switch USE_UPNP to makefile 2016-06-27 13:12:01 +00:00
hagen
646778227a * tune log messages 2016-06-27 13:11:10 +00:00
hagen
a973630cb4 * fix tests 2016-06-27 13:11:10 +00:00
hagen
4e7375c09c * Addressbook.cpp : move storage creation to Start() 2016-06-27 13:11:10 +00:00
hagen
881d0652e7 * update debian package defaults 2016-06-27 13:11:10 +00:00
orignal
0c46993baa Merge pull request #544 from atnaguzin/openssl
Updating i2pd_qt.pro
2016-06-27 06:54:49 -04:00
MXPLRS|Kirill
09b15f4940 edited i2pd_qt.pro 2016-06-27 05:55:07 +03:00
MXPLRS | Kirill
ebf7be56bb Merge pull request #2 from PurpleI2P/openssl
upstream pull
2016-06-27 05:52:56 +03:00
hagen
727068cc4b * HTTPProxy.cpp : migrate HTTPRequestFailed() to new http classes 2016-06-27 02:37:33 +00:00
hagen
6f77c6f3f4 * HTTPProxy.cpp : don't reuse part of httppserver, addresshelpers handling will be moved to proxy in future 2016-06-27 02:37:32 +00:00
hagen
c5e3e17eae * HTTPProxy.cpp : extract IsI2PAddress() from class 2016-06-27 02:37:30 +00:00
Jeff Becker
effdb70417 Merge branch 'upstream-openssl' into restricted_routes 2016-06-26 21:53:13 -04:00
hagen
e28f910c88 * enable travis for UPNP=ON back 2016-06-27 01:30:02 +00:00
hagen
c84468dbed * fix cmake build with upnp=on 2016-06-27 01:12:20 +00:00
orignal
4b9e39ac64 limit SSU outgoing windows 2016-06-26 17:03:04 -04:00
orignal
6b0c05ee7c Merge pull request #542 from majestrate/fix-rpi-static
fix static build for rpi linux
2016-06-26 13:31:14 -04:00
Jeff Becker
5c6ec70126 fix static build for rpi linux 2016-06-26 11:17:05 -04:00
orignal
2757ef94c9 don't include UPNP twice for android 2016-06-26 08:37:40 -04:00
orignal
9ba7120011 fixed build error 2016-06-26 08:32:36 -04:00
MXPLRS | Kirill
124f9ec44e Merge pull request #1 from PurpleI2P/openssl
upstream pull
2016-06-26 15:29:43 +03:00
orignal
0d229175cd Merge pull request #539 from hypnosis-i2p/openssl
added tray icon to linux and windows versions
2016-06-25 21:01:54 -04:00
hypnosis-i2p
10638b6e40 fixed unnecessary resources setting 2016-06-26 02:48:13 +08:00
hypnosis-i2p
134baad56d added tray icon to linux and windows versions 2016-06-26 02:32:54 +08:00
Jeff Becker
9a5984d750 Merge branch 'upstream-openssl' into restricted_routes 2016-06-25 10:55:22 -04:00
Jeff Becker
073f42e64a Merge branch 'upstream-openssl' into restricted_routes 2016-06-25 10:54:41 -04:00
orignal
096927beed don't sedn explicit Ack if no NACKs only 2016-06-24 21:54:58 -04:00
xcps
4bc76995d1 docs: default httpproxy.port changed to actual 4444 2016-06-24 19:29:59 -04:00
xcps
9f41151156 HTTP proxy redirects to 0.0.0.0:7070/?page=jumpservices 2016-06-24 19:25:48 -04:00
xcps
6b3bd755b0 fixtypo 2016-06-24 19:07:47 -04:00
xcps
7e580e6a0b Update HTTPServer.cpp 2016-06-24 17:58:46 -04:00
orignal
047c8eda22 stop accepting tunnels by graceful shutdown 2016-06-24 16:26:13 -04:00
orignal
f22e5c209c fixed QT linux build 2016-06-24 16:05:03 -04:00
orignal
8cc3a08871 Merge pull request #536 from hypnosis-i2p/openssl
Added graceful quit button; the code for stopping tunnels pending
2016-06-24 15:43:12 -04:00
hypnosis-i2p
35f6c6cb98 graceful quit button added 2016-06-25 03:38:50 +08:00
hypnosis-i2p
814b174f25 android version code bump 2016-06-25 03:38:50 +08:00
orignal
5fbaf0bc7d disabled UPNP=ON 2016-06-24 15:29:36 -04:00
orignal
ba772ab481 static miniupnpc 2016-06-24 14:20:35 -04:00
orignal
0a4888a18f link with miniupnp 2016-06-24 14:18:50 -04:00
orignal
fedbf2cc44 link UPnP with app if USE_UPNP is set 2016-06-24 13:15:51 -04:00
orignal
0f68bbac8e single #ifdef for protocol type 2016-06-23 14:01:41 -04:00
orignal
13e965096b UPnP for android 2016-06-23 12:57:36 -04:00
orignal
92961bb7bf i2cp for android 2016-06-23 11:23:06 -04:00
hagen
340686ba06 * HTTPProxy.{cpp,h} : rename classes, drop typedef 2016-06-23 13:24:44 +00:00
hagen
d8906f508c * HTTPProxy.cpp : HTTP error message cleanup 2016-06-23 13:24:44 +00:00
hagen
dde53ea4ba * HTTPProxy.cpp : HTTPRequestFailed() now responds with error message 2016-06-23 13:24:44 +00:00
hagen
225ed5b662 * HTTPProxy.{cpp,h} : move & sort headers 2016-06-23 13:24:44 +00:00
hagen
02857cf2b5 * Base.cpp : drop logger dependency 2016-06-23 13:24:44 +00:00
hagen
118a771980 * update changelog 2016-06-23 13:24:37 +00:00
orignal
359123564f Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-06-22 15:49:02 -04:00
orignal
49d2878938 use local sockets for android 2016-06-22 15:48:36 -04:00
orignal
f08ea4a9a3 Merge pull request #535 from PurpleI2P/openssl
2.8.0
2016-06-22 15:47:22 -04:00
Jeff Becker
f2bc71bc2a Merge branch 'upstream-openssl' 2016-06-22 13:19:00 -04:00
orignal
2b1e40c6c6 Update build_notes_windows.md 2016-06-22 11:15:40 -04:00
orignal
105c2deeb3 Merge pull request #534 from atnaguzin/patch-2
markup update
2016-06-22 11:06:44 -04:00
MXPLRS | Kirill
c3dbbc9144 Update build_notes_windows.md 2016-06-22 18:04:11 +03:00
orignal
ca55a9a8a6 build with UPnP 2016-06-22 10:53:29 -04:00
orignal
7ea5af448e UPNP support from windows 2016-06-22 09:41:01 -04:00
orignal
2f6898142e Merge pull request #533 from atnaguzin/openssl
updated config files
2016-06-22 07:13:23 -04:00
MXPLRS | Kirill
02e0b8cc32 Update i2pd.conf 2016-06-22 11:49:22 +03:00
MXPLRS|Kirill
b962a4c69b updated config files 2016-06-22 11:46:37 +03:00
orignal
ed3e83df67 android is supported now 2016-06-21 12:34:20 -04:00
orignal
b07bff61f0 set target SDK to 23 2016-06-21 09:09:31 -04:00
Jeff Becker
a7c955055c Merge branch 'upstream-openssl' into restricted_routes 2016-06-21 08:15:26 -04:00
orignal
13f33a9d19 2.8.0 2016-06-20 21:39:47 -04:00
orignal
be3fa6091d app appears as 'i2pd' 2016-06-20 15:28:25 -04:00
orignal
8ffddf06e4 fixed memory leak 2016-06-20 12:15:15 -04:00
orignal
9b7f583b2b icon and external storage permissions 2016-06-20 11:31:27 -04:00
orignal
9fe4f3adea teminate NTCP session on close completely 2016-06-19 21:05:48 -04:00
orignal
74d9f89c09 Merge pull request #529 from majestrate/fix-long-uptime-memleak
close ntcp sessions to prevent memory leaks
2016-06-19 21:00:25 -04:00
Jeff Becker
63c36e917e Merge branch 'upstream-openssl' into restricted_routes 2016-06-19 16:50:51 -04:00
Jeff Becker
184c6ee252 close ntcp sessions to prevent memory leaks 2016-06-19 16:48:03 -04:00
orignal
1c024afc1b Merge pull request #528 from hypnosis-i2p/openssl
fixed #519
2016-06-19 15:09:10 -04:00
hypnosis-i2p
51519361e2 various + fixed #519 2016-06-20 02:50:37 +08:00
hypnosis-i2p
bd092295a4 fixed #519 2016-06-20 02:50:37 +08:00
orignal
153c275d74 use /sdcard for android only if available 2016-06-19 09:58:29 -04:00
orignal
db0dfa1bf1 Merge pull request #526 from hypnosis-i2p/openssl
Android setForeground fix + notific.icon + fs crash fix + backg.thread restored
2016-06-19 09:44:59 -04:00
hypnosis-i2p
debd13d8c3 fixes: icon + localservice.java 2016-06-19 21:13:21 +08:00
hypnosis-i2p
4b8466e5e5 restored backg. thread; removed deinit() 2016-06-19 20:44:23 +08:00
hypnosis-i2p
9d9793e1af fs fixed. need another solution 2016-06-19 20:35:37 +08:00
hypnosis-i2p
46fafebade setForeground works. 2016-06-19 20:35:17 +08:00
hypnosis-i2p
1dae3d951a added qt java wrapper 2016-06-19 17:26:34 +08:00
orignal
fb213a1efd Merge pull request #523 from PurpleI2P/openssl
android build
2016-06-18 21:26:40 -04:00
Jeff Becker
93deb37c94 add primordial goo tier "hidden mode" (aka toy feature needs more work doesn't do it correctly all the way) 2016-06-18 11:10:58 -04:00
Jeff Becker
004a93a841 Merge branch 'transport_failsafe' into restricted_routes 2016-06-18 09:01:53 -04:00
orignal
da5be9f01d temporary disable background thread 2016-06-17 16:37:08 -04:00
orignal
344d0ae3ec some cleanup 2016-06-17 16:30:50 -04:00
orignal
3639c86adf some cleanup 2016-06-17 12:04:05 -04:00
orignal
4fc80fd366 eliminated DaemonQTImpl singleton 2016-06-17 11:25:28 -04:00
Jeff Becker
336ab2d82a Merge remote-tracking branch 'purple/openssl' into restricted_routes 2016-06-17 11:04:46 -04:00
Jeff Becker
74a7e67002 implement restricted routes (initial) 2016-06-17 11:03:33 -04:00
orignal
f76c04b7a6 fixed build errors 2016-06-17 10:26:51 -04:00
orignal
8130487b18 Merge pull request #522 from hypnosis-i2p/openssl
Qt: (1) Daemon now operates in backgr.mode, (2) added GUI Quit button
2016-06-17 10:06:33 -04:00
hypnosis-i2p
83790a5a73 qt: daemon now operates in the background thread; added Quit GUI button 2016-06-17 21:54:22 +08:00
hypnosis-i2p
1b35f68de9 qt: daemon now operates in the background thread; added Quit GUI button 2016-06-17 21:54:22 +08:00
hypnosis-i2p
3e912c6198 qt: daemon now operates in the background thread; added Quit GUI button 2016-06-17 21:52:14 +08:00
orignal
f995595202 Merge pull request #521 from PurpleI2P/openssl
android
2016-06-17 09:41:35 -04:00
orignal
6264569ca0 use /sdcard/i2pd at android 2016-06-17 09:10:11 -04:00
Jeff Becker
e868d427dd add options to not use ntcp or ssu 2016-06-17 09:02:12 -04:00
orignal
b9cbdb2dc4 Removed dependancy from stdafx 2016-06-16 18:20:07 -04:00
orignal
7ae563867c mainfest 2016-06-16 16:22:14 -04:00
orignal
f2f760bda4 link against correct openssl libs 2016-06-16 14:29:32 -04:00
orignal
ed561ad86b x86 build added 2016-06-16 11:42:34 -04:00
xcps
675861c323 Auto webconsole page refresh commented 2016-06-16 09:18:46 -04:00
xcps
ba330a42d6 Auto webconsole page refresh removed 2016-06-16 09:15:30 -04:00
orignal
2a9727c6b7 Merge pull request #520 from l-n-s/pretty_docs
More informative README and docs index
2016-06-15 19:55:33 -04:00
libre-net-society
5be147e8cc More informative README and docs index 2016-06-16 01:11:42 +03:00
orignal
eb96edbd31 separate DaemonQT and DaemonQTImpl 2016-06-15 14:43:29 -04:00
orignal
14c85fa975 configurable pathes to dependancies 2016-06-15 13:18:04 -04:00
orignal
b0e3339370 DaemonQT 2016-06-15 12:20:31 -04:00
orignal
70e502e55d QT doesn't depend on Linux daemon anymore 2016-06-15 11:28:59 -04:00
orignal
ff38a3bbfe don't demonize 2016-06-15 10:17:02 -04:00
orignal
b5723a6c18 use QT's main loop 2016-06-15 09:31:52 -04:00
orignal
89012cd73b Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-06-14 14:37:52 -04:00
orignal
756e86662b fixed android build 2016-06-14 14:37:22 -04:00
orignal
27ca3b4b01 Enable C++11 2016-06-14 13:21:22 -04:00
orignal
a5be4c9d0e moved std::to_string to util.h from android 2016-06-14 11:55:44 -04:00
orignal
07cc547bab Merge pull request #517 from anon5/openssl
Android i2pd armeabi-v7a with Qt
2016-06-14 11:23:49 -04:00
anon5
f672af9706 .apk runs on emulator 2016-06-14 23:17:37 +08:00
anon5
58b058ab3a .apk builds. untested 2016-06-14 23:17:37 +08:00
anon5
28c2ca8bf8 gitignore improved - added various generated files 2016-06-14 23:17:37 +08:00
orignal
d7a06dc7a9 Merge pull request #516 from PurpleI2P/openssl
recent changes
2016-06-14 11:10:20 -04:00
orignal
8970e6a48a Merge pull request #515 from vampik/patch-1
Fix html attributes
2016-06-14 07:21:02 -04:00
Andrey Tikhomirov
b8eef181b9 Fix html attributes 2016-06-14 11:25:51 +03:00
orignal
fb94d6ae2b read header and payload separately 2016-06-13 13:20:21 -04:00
Jeff Becker
fa68e392c8 don't abort when ntcp fails to bind 2016-06-13 11:34:44 -04:00
Jeff Becker
9eaa51442f update comment 2016-06-13 09:01:38 -04:00
orignal
68482d712b Merge pull request #513 from majestrate/fix_http_proxy_500
fix 500 response in http proxy
2016-06-13 08:54:06 -04:00
Jeff Becker
09fc767bb0 fix another typo 2016-06-13 08:53:35 -04:00
Jeff Becker
ea7e6615f2 fix typo 2016-06-13 08:52:54 -04:00
Jeff Becker
a183ca8661 fix special case 2016-06-13 08:52:21 -04:00
Jeff Becker
05939a2bbc special case for i2p.rocks in proxy 2016-06-13 08:50:53 -04:00
Jeff Becker
543a372435 Merge branch 'fix_http_proxy_500' 2016-06-13 07:51:16 -04:00
Jeff Becker
e795de5562 fix 500 response in http proxy 2016-06-13 07:48:20 -04:00
orignal
ae6877ce2f handle incomplete message header 2016-06-12 08:22:55 -04:00
orignal
a0f3e81b11 Merge pull request #512 from D504/openssl
Remove unused assigning (success is assinged anyway)
2016-06-11 08:18:24 -04:00
Osipov Kirill
88f52c4902 Remove unused assigning (success is assinged anyway) 2016-06-11 11:20:20 +03:00
orignal
bf8db7725f set -1 as default session id 2016-06-10 22:13:20 -04:00
orignal
f4d8c3304a execute lookup wothout session 2016-06-10 18:43:35 -04:00
orignal
44556b7f5e correct string size for mapping 2016-06-10 15:25:30 -04:00
orignal
2e1e95d483 pass URL params 2016-06-10 15:12:50 -04:00
orignal
ccc24337be Merge pull request #511 from PurpleI2P/openssl
recent changes
2016-06-10 14:46:23 -04:00
orignal
b15b38868d rolled back to previous implementation 2016-06-10 14:01:39 -04:00
orignal
8feca6874a process complete message 2016-06-10 12:18:19 -04:00
orignal
ecd3a49d48 handle DestroySession properly 2016-06-10 11:47:22 -04:00
orignal
6de7cd5063 don't send 'accepted' if not requested 2016-06-10 11:39:20 -04:00
orignal
f6d7f7d984 set port to 80 is not specified 2016-06-09 15:48:31 -04:00
orignal
d5d501875e send correct ackThrough 2016-06-09 14:56:12 -04:00
orignal
88561c22d3 make sure ackThrough is correct 2016-06-09 14:34:38 -04:00
hagen
b786576bcb * HTTPProxy.cpp : always set dest_port 2016-06-09 14:30:36 +00:00
orignal
21b5f2c96a fixed crash upon I2CP session disconnect 2016-06-08 14:14:19 -04:00
orignal
d8f24b442b fixed mapping 2016-06-08 14:05:20 -04:00
Jeff Becker
a1c81a63dd Merge remote-tracking branch 'purple/openssl'
Conflicts:
	I2CP.cpp
2016-06-08 10:37:51 -04:00
orignal
4d2b535b04 correct concatenation of long I2CP messages 2016-06-08 10:29:48 -04:00
Jeff Becker
3cfbc05bf9 set pointer to null after delete 2016-06-08 09:56:13 -04:00
Jeff Becker
895820f14c Merge branch 'upstream-openssl' 2016-06-08 09:55:55 -04:00
orignal
37fc21f3cf always assume 20 bytes for signing private key 2016-06-08 09:33:25 -04:00
orignal
5e068c3af5 0.9.26 2016-06-07 13:05:44 -04:00
orignal
e2c192d254 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-06-06 15:37:10 -04:00
orignal
e481ed37ce ReconfigureSessionMessage 2016-06-06 15:36:02 -04:00
orignal
4d7c089b09 I2CP config 2016-06-05 10:31:55 -04:00
hagen
a4dc67cba0 * HTTP.{cpp,h} : drop HTTPReq.host 2016-06-04 14:44:26 +00:00
hagen
03973cc6d4 * HTTPProxy.cpp : drop X-Forwarded-*, Proxy-*, Via headers from request 2016-06-04 14:01:37 +00:00
hagen
66c301c031 * HTTPProxy.cpp : allow "tranparent" proxy (#508) 2016-06-04 13:07:39 +00:00
hagen
e4edc59689 * HTTPProxy.cpp : force clean recv buffer (#508) 2016-06-04 13:07:37 +00:00
Jeff Becker
f2d9d38c6f Merge remote-tracking branch 'purple/openssl' 2016-06-03 14:07:02 -04:00
orignal
667ea43b3c GetBandwidthLimitMessage 2016-06-03 13:48:21 -04:00
Jeff Becker
f3856819fe Merge remote-tracking branch 'purple/openssl' 2016-06-03 13:01:49 -04:00
orignal
d6bfe7810a skip SigningPrivateKey 2016-06-03 13:01:12 -04:00
Jeff Becker
7a52ae18f1 Merge remote-tracking branch 'purple/openssl' 2016-06-03 12:13:08 -04:00
orignal
444539b826 SendMessageExpires 2016-06-03 12:03:36 -04:00
orignal
c8d6425123 DestLookupMessage 2016-06-03 11:49:39 -04:00
hagen
e50c35d38c * fix mistype 2016-06-03 01:16:29 +00:00
hagen
aa764fbd1c * HTTPProxy: fix converted request (#508)
* I2PService: reword log message, to avoid ambiguity
2016-06-03 01:09:08 +00:00
hagen
2628426084 * http proxy : fix converted request (#508) 2016-06-03 00:05:38 +00:00
orignal
aa6bc8042a address lookup 2016-06-02 15:49:14 -04:00
orignal
26a6c9e932 procee session options 2016-06-02 13:26:41 -04:00
Jeff Becker
0f9376e959 Merge remote-tracking branch 'purple/openssl' 2016-06-02 07:53:10 -04:00
orignal
ace3e86546 MessageStatusMessage 2016-06-01 15:30:57 -04:00
orignal
d79c6b8f06 MessagePayloadMessage 2016-06-01 14:38:13 -04:00
orignal
6538a2e673 HostLookupMessage 2016-06-01 11:11:18 -04:00
orignal
153d883aeb SessionDestoryedMessage 2016-06-01 10:05:40 -04:00
orignal
689432f627 fixed typo 2016-05-31 21:37:32 -04:00
hagen
cd237219e4 * extract unused image to separate file 2016-06-01 00:14:46 +00:00
hagen
8589493581 * add test for MergeChunkedResponse() (#432) 2016-06-01 00:14:40 +00:00
hagen
ca2e148ad7 * enable -Wextra for linux builds 2016-06-01 00:14:34 +00:00
hagen
f7ca44cad8 * fix compile warnings: reopen() usage 2016-06-01 00:14:28 +00:00
hagen
1b2ac38a50 * fix compilation warnings 2016-06-01 00:14:15 +00:00
hagen
f62d25fa5f * Config.cpp : fix wrong group for options & code style 2016-06-01 00:09:04 +00:00
orignal
025eec1782 I2CP configuration 2016-05-31 11:54:45 -04:00
orignal
846ff46b2e fixed build error 2016-05-30 21:42:25 -04:00
hagen
f9718bccb9 * update debian changelog (closes #502) 2016-05-31 00:34:33 +00:00
hagen
f66f4ffee6 * add generic changelog (#502) 2016-05-31 00:34:31 +00:00
hagen
a47417ff49 * I2PService.cpp: tune logs 2016-05-31 00:34:29 +00:00
hagen
c9836cf0f7 * fix doxygen warnings 2016-05-31 00:34:26 +00:00
hagen
289b679e3c * add doxygen support 2016-05-31 00:34:24 +00:00
hagen
23e019ec83 * debian/i2pd.openrc (working version) 2016-05-31 00:34:14 +00:00
orignal
eeffcea69e CreateSessionMessage 2016-05-30 15:19:22 -04:00
orignal
ae10793d0f SendMessageMessage 2016-05-30 14:31:56 -04:00
orignal
a062bca431 CreateLeaseSetMessage 2016-05-30 12:56:42 -04:00
orignal
6a453bcc8a check for null pointer 2016-05-30 12:08:20 -04:00
orignal
5a2c4919c6 close previous file first upon repon 2016-05-30 09:41:45 -04:00
orignal
09a80ed654 RequestVariableLeaseSetMessage 2016-05-29 16:35:57 -04:00
Jeff Becker
72e954b78f Merge remote-tracking branch 'purple/openssl' 2016-05-29 09:39:23 -04:00
Jeff Becker
a1c27aed6a Merge remote-tracking branch 'purple/master' 2016-05-29 09:36:34 -04:00
Jeff Becker
0b3a719a95 Merge branch 'master' of github.com:majestrate/i2pd
Fixed Conflicts:
	SAM.cpp
2016-05-29 09:36:01 -04:00
orignal
6c9b4a8c5d moved LeaseSet creating away from LeaseSetDestination 2016-05-29 09:33:50 -04:00
hagen
d9babda1b8 + debian/i2pd.openrc (experimental) 2016-05-29 01:31:38 +00:00
hagen
ea8e1be294 * update default init-script : make --port optional 2016-05-29 01:31:36 +00:00
hagen
44eccd85fd * HTTPServer.cpp :
* autorefresh for status page
  * autoreturn to commands list
2016-05-28 01:49:37 +00:00
hagen
a62720b9d8 Merge branch 'new-proxy' into openssl 2016-05-28 00:22:49 +00:00
orignal
1a9422c3f9 send SetDateMessage 2016-05-27 16:22:42 -04:00
orignal
5e52b3609c Merge pull request #503 from PurpleI2P/openssl
recent changes
2016-05-27 14:31:03 -04:00
orignal
8622385e88 I2CPDestination added 2016-05-27 13:46:28 -04:00
hagen
d0ffaab339 * HTTPProxy:
* use new http classes instead homemade parser
  * proper error handling for "address not found", "addresshelper" and "not .i2p domain" cases
  * use std::vector instead uint8_t[] for buffers
  * general code cleanup
2016-05-27 01:32:58 +00:00
hagen
347157b999 * HTTPProxy.cpp : direct use of parsed url parts in CreateHTTPRequest() 2016-05-27 01:32:15 +00:00
hagen
a9f3235fd3 * HTTPProxy.cpp : unwrap HandleStreamRequestComplete() 2016-05-27 01:32:12 +00:00
hagen
4098a5c08e * HTTPProxy.cpp : rename variable 2016-05-27 01:32:10 +00:00
hagen
dba7a2ee4f * HTTPProxy.cpp : HandleJumpServices() -> ExtractAddressHelper() 2016-05-27 01:32:07 +00:00
hagen
a5f49550b3 * HTTPProxy.cpp : unwrap AsyncSockRead() 2016-05-27 01:32:05 +00:00
hagen
5c9a69e0e8 * drop boost_regex from build deps 2016-05-27 01:32:03 +00:00
hagen
2bf32fb3fa * HTTPProxy.cpp : kill ExtractRequest(), drop boost::regex 2016-05-27 01:32:01 +00:00
hagen
0de1e2c6fc * HTTPProxy.cpp : extract IsI2PAddress() from class and generalize 2016-05-27 01:31:57 +00:00
hagen
61868d97c4 * HTTPProxy.cpp : migrate HTTPRequestFailed(), RedirectToJumpService() to new http classes 2016-05-27 01:31:55 +00:00
hagen
c994c11d8c * HTTPProxy.{cpp,h} : rename classes, drop typedef 2016-05-27 01:31:51 +00:00
orignal
5ad10955be use m_Response field for HTTP proxy response 2016-05-26 16:27:53 -04:00
orignal
95f100f378 HTTP error message cleanup 2016-05-26 16:21:27 -04:00
orignal
3d6c93cd6b moved transient encryption keys to LeaseSetDestination 2016-05-26 15:53:32 -04:00
orignal
fc25da37c5 removed GetPrivateKeys from LocalDestination 2016-05-26 14:54:33 -04:00
hagen
896bb2187e * HTTPProxy.cpp : HTTPRequestFailed() now responds with error message 2016-05-26 00:17:25 +00:00
hagen
99398bf0da * HTTPProxy.{cpp,h} : move & sort headers 2016-05-26 00:17:23 +00:00
hagen
827a54435d * Tunnel.cpp : tune log messages 2016-05-26 00:17:20 +00:00
hagen
3c9459e489 * fix mistype in log message 2016-05-26 00:10:01 +00:00
hagen
9291f5c9c6 * I2PControl.cpp :
* unwrap big else {} block
  * smaller try {} block, only for json parsing & request handling
  * respond with valid error message on exception
2016-05-26 00:09:44 +00:00
hagen
0ab5f993c7 * I2PControl.cpp :
* use new http classes for parsing request
  * implement correct reading rest of json data if HTTP/Content-length is used
  * general cleanup
2016-05-26 00:09:25 +00:00
hagen
4f8db487e7 * I2PControl.{cpp,h} : add BuildErrorResponse() 2016-05-26 00:09:25 +00:00
hagen
0e1765e045 * I2PControl.cpp : SendResponse() third arg now std::string & 2016-05-26 00:09:25 +00:00
hagen
ebc411bbbd * I2PControl.cpp :
* use new http classes for building HTTP response
  * drop boost::lexical_cast & boost::local_time deps
2016-05-26 00:09:13 +00:00
hagen
a76d8f0f9f * HTTP.{cpp,h} : add add_header() variant with std::string 2016-05-26 00:08:04 +00:00
hagen
f245feb0b0 * HTTP.h : export MergeChunkedResponse() 2016-05-26 00:08:02 +00:00
hagen
43a90d7b98 * HTTP.cpp : fix parse_header_line (#501) 2016-05-26 00:08:00 +00:00
hagen
2e1a9a8df9 * HTTP.{cpp,h} : move length() method to base class 2016-05-26 00:07:57 +00:00
orignal
57bb0da1d6 correct LeaseSet message size 2016-05-25 18:47:16 -04:00
orignal
0d2df22074 fixed crash 2016-05-25 17:41:24 -04:00
orignal
c7173d5e1c use shared ClientDestination 2016-05-25 16:18:02 -04:00
orignal
789eb48698 removed deprecated constructor 2016-05-25 15:30:04 -04:00
orignal
e686fad546 rmoved deprecated constructor 2016-05-25 15:18:21 -04:00
orignal
4e4f9b6f8b use LocalLeaseSet for own LeaseSets 2016-05-25 15:10:28 -04:00
orignal
f2292fd618 LocalLeaseSet added 2016-05-25 14:17:34 -04:00
orignal
7035ead9e7 provide reply tunnel expcilitly for LeaseSet 2016-05-25 12:55:58 -04:00
orignal
f01f6e94d1 fix #500. check result of readline 2016-05-24 16:27:34 -04:00
hagen
f10064ce39 * HTTPServer.cpp : update response building 2016-05-24 12:40:24 +00:00
hagen
b68f06ca83 * update tests 2016-05-24 12:40:24 +00:00
hagen
2ce61402bb * HTTP.{cpp,h} * add 'body' member ot HTTPRes * change HTTPRes::to_string() to add 'Date', 'Content-Length' headers and body 2016-05-24 12:40:24 +00:00
hagen
70e9d85a75 * HTTP.cpp : add internal function gen_rfc1123_date() 2016-05-24 12:40:23 +00:00
hagen
a461f462d2 * HTTP.{cpp,h} : add HTTPMsg::{add,del}_header() helpers 2016-05-24 12:40:23 +00:00
hagen
50ff0d251a * HTTP.h : add base class HTTPMsg 2016-05-24 12:40:23 +00:00
orignal
f6103d3841 moved streaming and datagram destination from LeaseSetDestination to ClientDestination 2016-05-23 14:31:22 -04:00
orignal
cb68d19bed ClientDestination/LeaseSetDestination split 2016-05-23 10:33:01 -04:00
hagen
89d2505a7c * fix time in webconsole (#496) 2016-05-19 14:15:55 +00:00
hagen
9ddfc750e5 * update manpage: add --logfile description (#495) 2016-05-19 14:15:52 +00:00
orignal
3b80de1747 Merge pull request #494 from PurpleI2P/openssl
2.7.0
2016-05-18 09:23:55 -04:00
orignal
0291cc2ef4 Merge pull request #493 from PurpleI2P/openssl
recent changes
2016-05-17 15:06:39 -04:00
Jeff Becker
9c1a6d042e Merge remote-tracking branch 'purple/openssl' 2016-05-16 16:04:48 -04:00
Jeff
d21043802e Merge branch 'upstream' 2016-05-11 10:02:49 -04:00
Jeff
47ce2398a4 fix http unit test SIGBUS in os x 2016-05-11 08:41:32 -04:00
Jeff
0a83d8e6a0 Merge branch 'openssl' 2016-05-11 08:19:51 -04:00
Jeff Becker
392f5f914a Merge remote-tracking branch 'purple/openssl' 2016-04-23 08:38:34 -04:00
orignal
799d25925a Merge pull request #478 from PurpleI2P/openssl
recent changes
2016-04-22 12:49:42 -04:00
orignal
f9b6b1bf76 Merge pull request #473 from PurpleI2P/openssl
precalculate elgamal
2016-04-13 15:10:55 -04:00
orignal
eefff148e9 Merge pull request #470 from PurpleI2P/openssl
recent changes
2016-04-08 09:41:46 -04:00
Jeff Becker
05f7578928 Merge remote-tracking branch 'purple/openssl' 2016-04-08 09:39:09 -04:00
orignal
41da48f5ff Merge pull request #468 from PurpleI2P/openssl
recent changes
2016-04-06 12:27:15 -04:00
Jeff Becker
9ae9ea18e1 Merge remote-tracking branch 'purple/openssl' 2016-04-02 10:13:53 -04:00
Jeff Becker
924f281536 * Don't set m_Session to nullptr in SAMSocket::Terminate
* check for null localDestination in SAMSocket::Terminate
2016-04-02 08:05:14 -04:00
orignal
b8a6946661 Merge pull request #462 from PurpleI2P/openssl
2.6.0
2016-03-31 10:34:08 -04:00
214 changed files with 11854 additions and 3421 deletions

15
.gitignore vendored
View File

@@ -5,12 +5,16 @@ router.keys
i2p
libi2pd.so
netDb
/i2pd
/libi2pd.a
/libi2pdclient.a
# Autotools
autom4te.cache
.deps
stamp-h1
Makefile
#Makefile
config.h
config.h.in~
config.log
@@ -233,3 +237,12 @@ pip-log.txt
# Sphinx
docs/_build
/androidIdea/
# emacs files
*~
*\#*
# gdb files
.gdb_history

View File

@@ -3,7 +3,6 @@ cache:
apt: true
os:
- linux
- osx
sudo: required
dist: trusty
addons:
@@ -17,7 +16,6 @@ addons:
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-program-options-dev
- libboost-regex-dev
- libboost-system-dev
- libboost-thread-dev
- libminiupnpc-dev

View File

@@ -5,16 +5,18 @@
#include <fstream>
#include <chrono>
#include <condition_variable>
#include <boost/lexical_cast.hpp>
#include <openssl/rand.h>
#include <boost/algorithm/string.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
#include "FS.h"
#include "Log.h"
#include "HTTP.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
#include "Config.h"
namespace i2p
{
@@ -160,7 +162,7 @@ namespace client
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
{
if (addresses.size() == 0) {
if (addresses.empty()) {
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
return 0;
}
@@ -173,7 +175,7 @@ namespace client
return 0;
}
for (auto it: addresses) {
for (const auto& it: addresses) {
f << it.first << "," << it.second.ToBase32 () << std::endl;
num++;
}
@@ -204,7 +206,7 @@ namespace client
}
//---------------------------------------------------------------------
AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false),
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{
}
@@ -216,6 +218,8 @@ namespace client
void AddressBook::Start ()
{
if (!m_Storage)
m_Storage = new AddressBookFilesystemStorage;
m_Storage->Init();
LoadHosts (); /* try storage, then hosts.txt, then download */
StartSubscriptions ();
@@ -250,7 +254,7 @@ namespace client
}
LogPrint (eLogError, "Addressbook: subscription download timeout");
m_IsDownloading = false;
}
}
if (m_Storage)
{
m_Storage->Save (m_Addresses);
@@ -258,8 +262,6 @@ namespace client
m_Storage = nullptr;
}
m_DefaultSubscription = nullptr;
for (auto it: m_Subscriptions)
delete it;
m_Subscriptions.clear ();
}
@@ -338,12 +340,12 @@ namespace client
std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode
if (f.is_open ())
{
LoadHostsFromStream (f);
LoadHostsFromStream (f, false);
m_IsLoaded = true;
}
}
bool AddressBook::LoadHostsFromStream (std::istream& f)
bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update)
{
std::unique_lock<std::mutex> l(m_AddressBookMutex);
int numAddresses = 0;
@@ -364,17 +366,18 @@ namespace client
std::string addr = s.substr(pos);
auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (ident->FromBase64(addr))
{
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
numAddresses++;
}
else
{
if (!ident->FromBase64(addr)) {
LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name);
incomplete = f.eof ();
continue;
}
numAddresses++;
if (m_Addresses.count(name) > 0)
continue; /* already exists */
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
if (is_update)
LogPrint(eLogInfo, "Addressbook: added new host: ", name);
}
else
incomplete = f.eof ();
@@ -400,12 +403,24 @@ namespace client
{
getline(f, s);
if (!s.length()) continue; // skip empty line
m_Subscriptions.push_back (new AddressBookSubscription (*this, s));
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, s));
}
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
LogPrint (eLogWarning, "Addressbook: subscriptions.txt usage is deprecated, use config file instead");
}
else
LogPrint (eLogWarning, "Addressbook: subscriptions.txt not found in datadir");
else if (!i2p::config::IsDefault("addressbook.subscriptions"))
{
// using config file items
std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs);
std::vector<std::string> subsList;
boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on);
for (size_t i = 0; i < subsList.size (); i++)
{
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, subsList[i]));
}
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
}
}
else
LogPrint (eLogError, "Addressbook: subscriptions already loaded");
@@ -415,7 +430,7 @@ namespace client
{
std::map<std::string, i2p::data::IdentHash> localAddresses;
m_Storage->LoadLocal (localAddresses);
for (auto it: localAddresses)
for (const auto& it: localAddresses)
{
auto dot = it.first.find ('.');
if (dot != std::string::npos)
@@ -459,7 +474,7 @@ namespace client
int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT;
if (success)
{
if (m_DefaultSubscription) m_DefaultSubscription.reset (nullptr);
if (m_DefaultSubscription) m_DefaultSubscription = nullptr;
if (m_IsLoaded)
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT;
else
@@ -510,19 +525,22 @@ namespace client
{
if (!m_IsLoaded)
{
// download it from http://i2p-projekt.i2p/hosts.txt
// download it from default subscription
LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription.");
std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL);
if (!m_DefaultSubscription)
m_DefaultSubscription.reset (new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS));
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, defaultSubURL);
m_IsDownloading = true;
m_DefaultSubscription->CheckSubscription ();
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
load_hosts.detach(); // TODO: use join
}
else if (!m_Subscriptions.empty ())
{
// pick random subscription
auto ind = rand () % m_Subscriptions.size();
m_IsDownloading = true;
m_Subscriptions[ind]->CheckSubscription ();
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind]));
load_hosts.detach(); // TODO: use join
}
}
else
@@ -543,8 +561,8 @@ namespace client
auto datagram = dest->GetDatagramDestination ();
if (!datagram)
datagram = dest->CreateDatagramDestination ();
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
ADDRESS_RESPONSE_DATAGRAM_PORT);
}
}
@@ -555,8 +573,7 @@ namespace client
if (dest)
{
auto datagram = dest->GetDatagramDestination ();
if (datagram)
datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
if (datagram) datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
}
}
@@ -568,7 +585,7 @@ namespace client
ident = FindAddress (address.substr (dot + 1));
if (!ident)
{
LogPrint (eLogError, "AddressBook: Can't find domain for ", address);
LogPrint (eLogError, "Addressbook: Can't find domain for ", address);
return;
}
@@ -584,7 +601,7 @@ namespace client
std::unique_lock<std::mutex> l(m_LookupsMutex);
m_Lookups[nonce] = address;
}
LogPrint (eLogDebug, "AddressBook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
size_t len = address.length () + 9;
uint8_t * buf = new uint8_t[len];
memset (buf, 0, 4);
@@ -601,11 +618,11 @@ namespace client
{
if (len < 44)
{
LogPrint (eLogError, "AddressBook: Lookup response is too short ", len);
LogPrint (eLogError, "Addressbook: Lookup response is too short ", len);
return;
}
uint32_t nonce = bufbe32toh (buf + 4);
LogPrint (eLogDebug, "AddressBook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce);
LogPrint (eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce);
std::string address;
{
std::unique_lock<std::mutex> l(m_LookupsMutex);
@@ -628,167 +645,154 @@ namespace client
{
}
void AddressBookSubscription::CheckSubscription ()
void AddressBookSubscription::CheckUpdates ()
{
std::thread load_hosts(&AddressBookSubscription::Request, this);
load_hosts.detach(); // TODO: use join
bool result = MakeRequest ();
m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified);
}
void AddressBookSubscription::Request ()
bool AddressBookSubscription::MakeRequest ()
{
i2p::http::URL url;
// must be run in separate thread
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
bool success = false;
i2p::util::http::url u (m_Link);
i2p::data::IdentHash ident;
if (m_Book.GetIdentHash (u.host_, ident))
{
if (!m_Etag.length ())
{
// load ETag
m_Book.GetEtag (ident, m_Etag, m_LastModified);
LogPrint (eLogInfo, "Addressbook: set ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
}
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
if (!leaseSet)
{
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident,
[&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (ident);
}
}
if (leaseSet)
{
std::stringstream request, response;
// standard header
request << "GET " << u.path_ << " HTTP/1.1\r\n"
<< "Host: " << u.host_ << "\r\n"
<< "Accept: */*\r\n"
<< "User-Agent: Wget/1.11.4\r\n"
//<< "Accept-Encoding: gzip\r\n"
<< "X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n"
<< "Connection: close\r\n";
if (m_Etag.length () > 0) // etag
request << i2p::util::http::IF_NONE_MATCH << ": " << m_Etag << "\r\n";
if (m_LastModified.length () > 0) // if-modfief-since
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n";
request << "\r\n"; // end of header
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_);
stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ());
uint8_t buf[4096];
bool end = false;
while (!end)
{
stream->AsyncReceive (boost::asio::buffer (buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (bytes_transferred)
response.write ((char *)buf, bytes_transferred);
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
end = true;
newDataReceived.notify_all ();
},
30); // wait for 30 seconds
std::unique_lock<std::mutex> l(newDataReceivedMutex);
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired");
}
// process remaining buffer
while (size_t len = stream->ReadSome (buf, 4096))
response.write ((char *)buf, len);
// parse response
std::string version;
response >> version; // HTTP version
int status = 0;
response >> status; // status
if (status == 200) // OK
{
bool isChunked = false, isGzip = false;
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: received ", 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");
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link);
if (!url.parse(m_Link)) {
LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link);
return false;
}
else
LogPrint (eLogError, "Addressbook: Can't resolve ", u.host_);
if (!success)
LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed");
m_Book.DownloadComplete (success, ident, m_Etag, m_LastModified);
}
bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip)
{
if (isGzip)
if (!m_Book.GetIdentHash (url.host, m_Ident)) {
LogPrint (eLogError, "Addressbook: Can't resolve ", url.host);
return false;
}
/* this code block still needs some love */
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident);
if (!leaseSet)
{
std::stringstream uncompressed;
i2p::data::GzipInflator inflator;
inflator.Inflate (s, uncompressed);
if (!uncompressed.fail ())
return m_Book.LoadHostsFromStream (uncompressed);
else
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident,
[&newDataReceived, &leaseSet, &newDataReceivedMutex](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
std::unique_lock<std::mutex> l1(newDataReceivedMutex);
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident, false); // don't notify, because we know it already
return false;
}
else
return m_Book.LoadHostsFromStream (s);
}
}
if (!leaseSet) {
/* still no leaseset found */
LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found");
return false;
}
if (m_Etag.empty() && m_LastModified.empty()) {
m_Book.GetEtag (m_Ident, m_Etag, m_LastModified);
LogPrint (eLogDebug, "Addressbook: loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
}
/* save url parts for later use */
std::string dest_host = url.host;
int dest_port = url.port ? url.port : 80;
/* create http request & send it */
i2p::http::HTTPReq req;
req.add_header("Host", dest_host);
req.add_header("User-Agent", "Wget/1.11.4");
req.add_header("Connection", "close");
if (!m_Etag.empty())
req.add_header("If-None-Match", m_Etag);
if (!m_LastModified.empty())
req.add_header("If-Modified-Since", m_LastModified);
/* convert url to relative */
url.schema = "";
url.host = "";
req.uri = url.to_string();
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
std::string request = req.to_string();
stream->Send ((const uint8_t *) request.data(), request.length());
/* read response */
std::string response;
uint8_t recv_buf[4096];
bool end = false;
while (!end) {
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (bytes_transferred)
response.append ((char *)recv_buf, bytes_transferred);
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
end = true;
newDataReceived.notify_all ();
},
30); // wait for 30 seconds
std::unique_lock<std::mutex> l(newDataReceivedMutex);
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired");
}
// process remaining buffer
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) {
response.append ((char *)recv_buf, len);
}
/* parse response */
i2p::http::HTTPRes res;
int res_head_len = res.parse(response);
if (res_head_len < 0) {
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host);
return false;
}
if (res_head_len == 0) {
LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout");
return false;
}
/* assert: res_head_len > 0 */
response.erase(0, res_head_len);
if (res.code == 304) {
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304");
return false;
}
if (res.code != 200) {
LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code);
return false;
}
int len = res.content_length();
if (response.empty()) {
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes");
return false;
}
if (len > 0 && len != (int) response.length()) {
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes");
return false;
}
/* assert: res.code == 200 */
auto it = res.headers.find("ETag");
if (it != res.headers.end()) {
m_Etag = it->second;
}
it = res.headers.find("If-Modified-Since");
if (it != res.headers.end()) {
m_LastModified = it->second;
}
if (res.is_chunked()) {
std::stringstream in(response), out;
i2p::http::MergeChunkedResponse (in, out);
response = out.str();
} else if (res.is_gzipped()) {
std::stringstream out;
i2p::data::GzipInflator inflator;
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out);
if (out.fail()) {
LogPrint(eLogError, "Addressbook: can't gunzip http response");
return false;
}
response = out.str();
}
std::stringstream ss(response);
LogPrint (eLogInfo, "Addressbook: got update from ", dest_host);
m_Book.LoadHostsFromStream (ss, true);
return true;
}
AddressResolver::AddressResolver (std::shared_ptr<ClientDestination> destination):
@@ -819,7 +823,7 @@ namespace client
{
if (len < 9 || len < buf[8] + 9U)
{
LogPrint (eLogError, "AddressBook: Address request is too short ", len);
LogPrint (eLogError, "Addressbook: Address request is too short ", len);
return;
}
// read requested address
@@ -827,7 +831,7 @@ namespace client
char address[255];
memcpy (address, buf + 9, l);
address[l] = 0;
LogPrint (eLogDebug, "AddressBook: Address request ", address);
LogPrint (eLogDebug, "Addressbook: Address request ", address);
// send response
uint8_t response[44];
memset (response, 0, 4); // reserved

View File

@@ -18,7 +18,6 @@ namespace i2p
{
namespace client
{
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes
const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours)
@@ -66,7 +65,7 @@ namespace client
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
bool LoadHostsFromStream (std::istream& f);
bool LoadHostsFromStream (std::istream& f, bool is_update);
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
//This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
@@ -98,8 +97,8 @@ namespace client
std::map<uint32_t, std::string> m_Lookups; // nonce -> address
AddressBookStorage * m_Storage;
volatile bool m_IsLoaded, m_IsDownloading;
std::vector<AddressBookSubscription *> m_Subscriptions;
std::unique_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
};
@@ -108,17 +107,17 @@ namespace client
public:
AddressBookSubscription (AddressBook& book, const std::string& link);
void CheckSubscription ();
void CheckUpdates ();
private:
void Request ();
bool ProcessResponse (std::stringstream& s, bool isGzip = false);
bool MakeRequest ();
private:
AddressBook& m_Book;
std::string m_Link, m_Etag, m_LastModified;
i2p::data::IdentHash m_Ident;
// m_Etag must be surrounded by ""
};

124
BOB.cpp
View File

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

4
BOB.h
View File

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

104
Base.cpp
View File

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

117
Base.h
View File

@@ -2,15 +2,11 @@
#define BASE_H__
#include <inttypes.h>
#include <string.h>
#include <string>
#include <zlib.h>
#include <iostream>
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase32SubstitutionTable ();
@@ -23,112 +19,7 @@ namespace data
Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
*/
size_t Base64EncodingBufferSize(const size_t input_size);
template<int sz>
class Tag
{
public:
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
Tag (const Tag<sz>& ) = default;
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
Tag (Tag<sz>&& ) = default;
#endif
Tag () = default;
Tag<sz>& operator= (const Tag<sz>& ) = default;
#ifndef _WIN32
Tag<sz>& operator= (Tag<sz>&& ) = default;
#endif
uint8_t * operator()() { return m_Buf; };
const uint8_t * operator()() const { return m_Buf; };
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
const uint64_t * GetLL () const { return ll; };
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
bool IsZero () const
{
for (int i = 0; i < sz/8; i++)
if (ll[i]) return false;
return true;
}
std::string ToBase64 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
std::string ToBase32 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
const size_t GZIP_CHUNK_SIZE = 16384;
class GzipInflator
{
public:
GzipInflator ();
~GzipInflator ();
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
bool Inflate (const uint8_t * in, size_t inLen, std::ostream& s);
// return true when finshed or error, s failbit will be set in case of error
void Inflate (std::istream& in, std::ostream& out);
private:
z_stream m_Inflator;
bool m_IsDirty;
};
class GzipDeflator
{
public:
GzipDeflator ();
~GzipDeflator ();
void SetCompressionLevel (int level);
size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
private:
z_stream m_Deflator;
bool m_IsDirty;
};
}
}
} // data
} // i2p
#endif

101
ChangeLog Normal file
View File

@@ -0,0 +1,101 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.9.0] - 2016-08-12
### Changed
- Proxy refactoring & speedup
- Transmission-I2P support
- Graceful shutdown for Windows
- Android without QT
- Reduced number of timers in SSU
- ipv6 peer test support
- Reseed from SU3 file
## [2.8.0] - 2016-06-20
### Added
- Basic Android support
- I2CP implementation
- 'doxygen' target
### Changed
- I2PControl refactoring & fixes (proper jsonrpc responses on errors)
- boost::regex no more needed
### Fixed
- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional
- properly close NTCP sessions (memleak)
## [2.7.0] - 2016-05-18
### Added
- Precomputed El-Gamal/DH tables
- Configurable limit of transit tunnels
### Changed
- Speed-up of assymetric crypto for non-x64 platforms
- Refactoring of web-console
## [2.6.0] - 2016-03-31
### Added
- Gracefull shutdown on SIGINT
- Numeric bandwidth limits (was: by router class)
- Jumpservices in web-console
- Logging to syslog
- Tray icon for windows application
### Changed
- Logs refactoring
- Improved statistics in web-console
### Deprecated:
- Renamed main/tunnels config files (will use old, if found, but emits warning)
## [2.5.1] - 2016-03-10
### Fixed
- Doesn't create ~/.i2pd dir if missing
## [2.5.0] - 2016-03-04
### Added
- IRC server tunnels
- SOCKS outproxy support
- Support for gzipped addressbook updates
- Support for router families
### Changed
- Shared RTT/RTO between streams
- Filesystem work refactoring
## [2.4.0] - 2016-02-03
### Added
- X-I2P-* headers for server http-tunnels
- I2CP options for I2P tunnels
- Show I2P tunnels in webconsole
### Changed
- Refactoring of cmdline/config parsing
## [2.3.0] - 2016-01-12
### Added
- Support for new router bandwidth class codes (P and X)
- I2PControl supports external webui
- Added --pidfile and --notransit parameters
- Ability to specify signature type for i2p tunnel
### Changed
- Fixed multiple floodfill-related bugs
- New webconsole layout
## [2.2.0] - 2015-12-22
### Added
- Ability to connect to router without ip via introducer
### Changed
- Persist temporary encryption keys for local destinations
- Performance improvements for EdDSA
- New addressbook structure
## [2.1.0] - 2015-11-12
### Added
- Implementation of EdDSA
### Changed
- EdDSA is default signature type for new RouterInfos

View File

@@ -6,6 +6,7 @@
#include "FS.h"
#include "Log.h"
#include "Identity.h"
#include "util.h"
#include "ClientContext.h"
namespace i2p
@@ -38,8 +39,9 @@ namespace client
m_SharedLocalDestination->Start ();
}
m_AddressBook.Start ();
std::shared_ptr<ClientDestination> localDestination;
bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy);
if (httproxy) {
@@ -50,8 +52,10 @@ namespace client
if (httpProxyKeys.length () > 0)
{
i2p::data::PrivateKeys keys;
LoadPrivateKeys (keys, httpProxyKeys);
localDestination = CreateNewLocalDestination (keys, false);
if(LoadPrivateKeys (keys, httpProxyKeys))
localDestination = CreateNewLocalDestination (keys, false);
else
LogPrint(eLogError, "Clients: failed to load HTTP Proxy key");
}
try {
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
@@ -60,7 +64,7 @@ namespace client
LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what());
}
}
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
if (socksproxy) {
std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys);
@@ -82,10 +86,10 @@ namespace client
LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what());
}
}
// I2P tunnels
ReadTunnels ();
// I2P tunnels
ReadTunnels ();
// SAM
bool sam; i2p::config::GetOption("sam.enabled", sam);
if (sam) {
@@ -114,7 +118,32 @@ namespace client
}
}
// I2CP
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
if (i2cp)
{
std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr);
uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort);
LogPrint(eLogInfo, "Clients: starting I2CP at ", i2cpAddr, ":", i2cpPort);
try
{
m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort);
m_I2CPServer->Start ();
}
catch (std::exception& e)
{
LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what());
}
}
m_AddressBook.StartResolvers ();
// start UDP cleanup
if (!m_ServerForwards.empty ())
{
m_CleanupUDPTimer.reset (new boost::asio::deadline_timer(m_SharedLocalDestination->GetService ()));
ScheduleCleanupUDP();
}
}
void ClientContext::Stop ()
@@ -165,21 +194,46 @@ namespace client
m_BOBCommandChannel = nullptr;
}
if (m_I2CPServer)
{
LogPrint(eLogInfo, "Clients: stopping I2CP");
m_I2CPServer->Stop ();
delete m_I2CPServer;
m_I2CPServer = nullptr;
}
LogPrint(eLogInfo, "Clients: stopping AddressBook");
m_AddressBook.Stop ();
for (auto it: m_Destinations)
m_AddressBook.Stop ();
{
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
m_ServerForwards.clear();
m_ClientForwards.clear();
}
if (m_CleanupUDPTimer)
{
m_CleanupUDPTimer->cancel ();
m_CleanupUDPTimer = nullptr;
}
for (auto& it: m_Destinations)
it.second->Stop ();
m_Destinations.clear ();
m_SharedLocalDestination = nullptr;
m_SharedLocalDestination = nullptr;
}
void ClientContext::ReloadConfig ()
{
ReadTunnels (); // TODO: it reads new tunnels only, should be implemented better
std::string config; i2p::config::GetOption("conf", config);
i2p::config::ParseConfig(config);
Stop();
Start();
}
void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
{
bool success = true;
std::string fullPath = i2p::fs::DataDirPath (filename);
std::ifstream s(fullPath, std::ifstream::binary);
if (s.is_open ())
@@ -189,9 +243,14 @@ namespace client
s.seekg (0, std::ios::beg);
uint8_t * buf = new uint8_t[len];
s.read ((char *)buf, len);
keys.FromBuffer (buf, len);
if(!keys.FromBuffer (buf, len))
{
LogPrint (eLogError, "Clients: failed to load keyfile ", filename);
success = false;
}
else
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
delete[] buf;
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
}
else
{
@@ -205,7 +264,31 @@ namespace client
delete[] buf;
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
}
}
return success;
}
std::vector<std::shared_ptr<DatagramSessionInfo> > ClientContext::GetForwardInfosFor(const i2p::data::IdentHash & destination)
{
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
for(const auto & c : m_ClientForwards)
{
if (c.second->IsLocalDestination(destination))
{
for (auto & i : c.second->GetSessions()) infos.push_back(i);
break;
}
}
for(const auto & s : m_ServerForwards)
{
if(std::get<0>(s.first) == destination)
{
for( auto & i : s.second->GetSessions()) infos.push_back(i);
break;
}
}
return infos;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
@@ -266,7 +349,7 @@ namespace client
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));
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
}
template<typename Section>
@@ -310,7 +393,7 @@ namespace client
try
{
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT)
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT)
{
// mandatory params
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
@@ -328,22 +411,43 @@ namespace client
if (keys.length () > 0)
{
i2p::data::PrivateKeys k;
LoadPrivateKeys (k, keys, sigType);
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if(LoadPrivateKeys (k, keys, sigType))
{
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
}
}
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
// udp client
// TODO: hostnames
boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port);
if (!localDestination)
localDestination = CreateNewLocalDestination (k, false, &options);
{
localDestination = m_SharedLocalDestination;
}
auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort);
if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr<I2PUDPClientTunnel>(clientTunnel))).second)
{
clientTunnel->Start();
}
else
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
} else {
// tcp client
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (),
std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
{
clientTunnel->Start ();
numClientTunnels++;
}
else
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists");
}
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (),
std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
{
clientTunnel->Start ();
numClientTunnels++;
}
else
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists");
}
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC)
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{
// mandatory params
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
@@ -356,17 +460,43 @@ namespace client
std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, "");
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true);
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN);
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
// I2CP
std::map<std::string, std::string> options;
ReadI2CPOptions (section, options);
std::shared_ptr<ClientDestination> localDestination = nullptr;
i2p::data::PrivateKeys k;
LoadPrivateKeys (k, keys, sigType);
if(!LoadPrivateKeys (k, keys, sigType))
continue;
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
localDestination = CreateNewLocalDestination (k, true, &options);
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{
// udp server tunnel
// TODO: hostnames
auto localAddress = boost::asio::ip::address::from_string(address);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port);
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
if(m_ServerForwards.insert(
std::make_pair(
std::make_pair(
localDestination->GetIdentHash(), port),
std::unique_ptr<I2PUDPServerTunnel>(serverTunnel))).second)
{
serverTunnel->Start();
LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32());
}
else
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists");
continue;
}
I2PServerTunnel * serverTunnel;
if (type == I2P_TUNNELS_SECTION_TYPE_HTTP)
serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip);
@@ -375,6 +505,10 @@ namespace client
else // regular server tunnel by default
serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip);
LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns);
serverTunnel->SetMaxConnsPerMinute(maxConns);
if (accessList.length () > 0)
{
std::set<i2p::data::IdentHash> idents;
@@ -412,6 +546,26 @@ namespace client
}
LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created");
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
}
}
void ClientContext::ScheduleCleanupUDP()
{
if (m_CleanupUDPTimer)
{
// schedule cleanup in 17 seconds
m_CleanupUDPTimer->expires_from_now (boost::posix_time::seconds (17));
m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1));
}
}
void ClientContext::CleanupUDP(const boost::system::error_code & ecode)
{
if(!ecode)
{
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
for (auto & s : m_ServerForwards ) s.second->ExpireStale();
ScheduleCleanupUDP();
}
}
}
}

View File

@@ -6,6 +6,7 @@
#include <memory>
#include <boost/asio.hpp>
#include "Destination.h"
#include "I2PService.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
#include "I2PTunnel.h"
@@ -23,6 +24,8 @@ namespace client
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc";
const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient";
const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver";
const char I2P_CLIENT_TUNNEL_PORT[] = "port";
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
@@ -38,7 +41,8 @@ namespace client
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
class ClientContext
{
public:
@@ -58,11 +62,13 @@ namespace client
const std::map<std::string, std::string> * params = nullptr);
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
void LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
AddressBook& GetAddressBook () { return m_AddressBook; };
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
private:
void ReadTunnels ();
@@ -71,6 +77,9 @@ namespace client
template<typename Section>
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
void CleanupUDP(const boost::system::error_code & ecode);
void ScheduleCleanupUDP();
private:
std::mutex m_DestinationsMutex;
@@ -83,15 +92,24 @@ namespace client
i2p::proxy::SOCKSProxy * m_SocksProxy;
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // local endpoint->tunnel
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
std::mutex m_ForwardsMutex;
std::map<boost::asio::ip::udp::endpoint, std::unique_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PUDPServerTunnel> > m_ServerForwards; // <destination,port> -> udp tunnel
SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel;
I2CPServer * m_I2CPServer;
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
public:
// for HTTP
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; };
const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; };
const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; }
const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; }
};
extern ClientContext context;

View File

@@ -26,84 +26,12 @@ 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() {
bool nat = true;
#ifdef MESHNET
nat = false;
#endif
options_description general("General options");
general.add_options()
("help", "Show this message")
@@ -116,14 +44,19 @@ namespace config {
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
("ifname", value<std::string>()->default_value(""), "network interface to bind to")
("nat", value<bool>()->zero_tokens()->default_value(nat), "should we assume we are behind NAT?")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", value<bool>()->zero_tokens()->default_value(true), "Enable communication through ipv4")
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", value<bool>()->zero_tokens()->default_value(false), "Router will go to background after start")
("service", value<bool>()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'")
("notransit", value<bool>()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup")
("floodfill", value<bool>()->zero_tokens()->default_value(false), "Router will be floodfill")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in kbps or letters: L (32), O (256), P (2048), X (>9000)")
("ntcp", value<bool>()->zero_tokens()->default_value(true), "enable ntcp transport")
("ssu", value<bool>()->zero_tokens()->default_value(true), "enable ssu transport")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")
@@ -178,6 +111,13 @@ namespace config {
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
;
options_description i2cp("I2CP options");
i2cp.add_options()
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
;
options_description i2pcontrol("I2PControl options");
i2pcontrol.add_options()
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
@@ -188,6 +128,16 @@ namespace config {
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
;
bool upnp_default = false;
#if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
upnp_default = true; // enable UPNP for windows GUI and android by default
#endif
options_description upnp("UPnP options");
upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list")
;
options_description precomputation("Precomputation options");
precomputation.add_options()
("precomputation.elgamal",
@@ -198,7 +148,44 @@ namespace config {
#endif
"Enable or disable elgamal precomputation table")
;
options_description reseed("Reseed options");
reseed.add_options()
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
("reseed.file", value<std::string>()->default_value(""), "Path to .su3 file")
#ifdef MESHNET
("reseed.urls", value<std::string>()->default_value("https://reseed.i2p.rocks:8443/"), "Reseed URLs, separated by comma")
#else
("reseed.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/,"
"https://i2p.mooo.com/netDb/,"
"https://netdb.i2p2.no/,"
"https://us.reseed.i2p2.no:444/,"
"https://uk.reseed.i2p2.no:444/,"
"https://i2p.manas.ca:8443/,"
"https://i2p-0.manas.ca:8443/,"
"https://reseed.i2p.vzaws.com:8443/,"
"https://download.xxlspeed.com/,"
"https://reseed-ru.lngserv.ru/,"
"https://reseed.atomike.ninja/"
), "Reseed URLs, separated by comma")
#endif
;
options_description addressbook("AddressBook options");
addressbook.add_options()
("addressbook.defaulturl", value<std::string>()->default_value(
"http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"
), "AddressBook subscription URL for initial setup")
("addressbook.subscriptions", value<std::string>()->default_value(""),
"AddressBook subscriptions URLs, separated by comma");
options_description trust("Trust options");
trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
("trust.hidden", value<bool>()->default_value(false), "should we hide our router from other routers?");
m_OptionsDesc
.add(general)
.add(limits)
@@ -207,8 +194,13 @@ namespace config {
.add(socksproxy)
.add(sam)
.add(bob)
.add(i2cp)
.add(i2pcontrol)
.add(precomputation)
.add(upnp)
.add(precomputation)
.add(reseed)
.add(addressbook)
.add(trust)
;
}
@@ -217,7 +209,7 @@ namespace config {
auto style = boost::program_options::command_line_style::unix_style
| boost::program_options::command_line_style::allow_long_disguise;
style &= ~ boost::program_options::command_line_style::allow_guessing;
store(parse_command_line(argc, argv, m_OptionsDesc, style, old_syntax_parser), m_Options);
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
} catch (boost::program_options::error& e) {
std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE);

View File

@@ -68,7 +68,7 @@ namespace config {
* @param value Variable where to store option
* @return this function returns false if parameter not found
*
* @example uint16_t port; GetOption("sam.port", port);
* Example: uint16_t port; GetOption("sam.port", port);
*/
template<typename T>
bool GetOption(const char *name, T& value) {
@@ -84,7 +84,7 @@ namespace config {
* @param value New parameter value
* @return true if value set up successful, false otherwise
*
* @example uint16_t port = 2827; SetOption("bob.port", port);
* Example: uint16_t port = 2827; SetOption("bob.port", port);
*/
template<typename T>
bool SetOption(const char *name, const T& value) {

View File

@@ -135,11 +135,8 @@ namespace crypto
DSA * CreateDSA ()
{
DSA * dsa = DSA_new ();
dsa->p = BN_dup (dsap);
dsa->q = BN_dup (dsaq);
dsa->g = BN_dup (dsag);
dsa->priv_key = NULL;
dsa->pub_key = NULL;
DSA_set0_pqg (dsa, BN_dup (dsap), BN_dup (dsaq), BN_dup (dsag));
DSA_set0_key (dsa, NULL, NULL);
return dsa;
}
@@ -298,11 +295,13 @@ namespace crypto
BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits
#endif
// calculate a
a = BN_new ();
if (g_ElggTable)
a = ElggPow (k, g_ElggTable, ctx);
else
{
a = BN_new ();
BN_mod_exp (a, elgg, k, elgp, ctx);
}
BIGNUM * y = BN_new ();
BN_bin2bn (key, 256, y);

View File

@@ -9,7 +9,9 @@
#include <openssl/dsa.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Base.h"
#include "Tag.h"
namespace i2p
{
@@ -277,6 +279,16 @@ namespace crypto
void InitCrypto (bool precomputation);
void TerminateCrypto ();
// take care about openssl version
#include <openssl/opensslv.h>
#if (OPENSSL_VERSION_NUMBER < 0x010100000) // 1.1.0
// define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) { d->p = p; d->q = q; d->g = g; return 1; }
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) { d->pub_key = pub_key; d->priv_key = priv_key; return 1; }
#endif
}
}

View File

@@ -22,10 +22,8 @@
#include "I2PControl.h"
#include "ClientContext.h"
#include "Crypto.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
#include "util.h"
namespace i2p
{
@@ -39,16 +37,13 @@ namespace i2p
std::unique_ptr<i2p::http::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
#ifdef USE_UPNP
i2p::transport::UPnP m_UPnP;
#endif
std::unique_ptr<i2p::transport::UPnP> UPnP;
};
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
Daemon_Singleton::~Daemon_Singleton() {
delete &d;
};
}
bool Daemon_Singleton::IsService () const
{
@@ -114,7 +109,7 @@ namespace i2p
}
i2p::log::Logger().Ready();
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir);
@@ -122,25 +117,23 @@ namespace i2p
i2p::crypto::InitCrypto (precomputation);
i2p::context.Init ();
uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port"))
{
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
std::string host; i2p::config::GetOption("host", host);
if (!i2p::config::IsDefault("host"))
{
LogPrint(eLogInfo, "Daemon: setting address for incoming connections to ", host);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
}
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool transit; i2p::config::GetOption("notransit", transit);
#ifdef MESHNET
// manual override for meshnet
ipv4 = false;
ipv6 = true;
#endif
uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port"))
{
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit);
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
SetMaxNumTransitTunnels (transitTunnels);
@@ -192,31 +185,65 @@ namespace i2p
i2p::context.SetFamily (family);
if (family.length () > 0)
LogPrint(eLogInfo, "Daemon: family set to ", family);
return true;
bool trust; i2p::config::GetOption("trust.enabled", trust);
if (trust)
{
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
std::string fam; i2p::config::GetOption("trust.family", fam);
if (fam.length() > 0)
{
LogPrint(eLogInfo, "Daemon: setting restricted routes to use family ", fam);
i2p::transport::transports.RestrictRoutes({fam});
} else
LogPrint(eLogError, "Daemon: no family specified for restricted routes");
}
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
if (hidden)
{
LogPrint(eLogInfo, "Daemon: using hidden mode");
i2p::data::netdb.SetHidden(true);
}
return true;
}
bool Daemon_Singleton::start()
{
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
bool upnp; i2p::config::GetOption("upnp.enabled", upnp);
if (upnp) {
d.UPnP = std::unique_ptr<i2p::transport::UPnP>(new i2p::transport::UPnP);
d.UPnP->Start ();
}
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
bool ssu; i2p::config::GetOption("ssu", ssu);
LogPrint(eLogInfo, "Daemon: starting Transports");
if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled");
i2p::transport::transports.Start(ntcp, ssu);
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) {
LogPrint(eLogInfo, "Daemon: Transports started");
} else {
LogPrint(eLogError, "Daemon: failed to start Transports");
/** shut down netdb right away */
i2p::transport::transports.Stop();
i2p::data::netdb.Stop();
return false;
}
bool http; i2p::config::GetOption("http.enabled", http);
if (http) {
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
d.httpServer->Start();
}
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: starting UPnP");
d.m_UPnP.Start ();
#endif
LogPrint(eLogInfo, "Daemon: starting Transports");
i2p::transport::transports.Start();
LogPrint(eLogInfo, "Daemon: starting Tunnels");
i2p::tunnel::tunnels.Start();
@@ -232,6 +259,7 @@ namespace i2p
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
d.m_I2PControlService->Start ();
}
return true;
}
@@ -242,10 +270,12 @@ namespace i2p
i2p::client::context.Stop();
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
i2p::tunnel::tunnels.Stop();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: stopping UPnP");
d.m_UPnP.Stop ();
#endif
if (d.UPnP) {
d.UPnP->Stop ();
d.UPnP = nullptr;
}
LogPrint(eLogInfo, "Daemon: stopping Transports");
i2p::transport::transports.Stop();
LogPrint(eLogInfo, "Daemon: stopping NetDB");
@@ -265,5 +295,5 @@ namespace i2p
return true;
}
}
}
}

View File

@@ -1,14 +1,9 @@
#ifndef DAEMON_H__
#define DAEMON_H__
#include <memory>
#include <string>
#ifdef _WIN32
#define Daemon i2p::util::DaemonWin32::Instance()
#else
#define Daemon i2p::util::DaemonLinux::Instance()
#endif
namespace i2p
{
namespace util
@@ -22,9 +17,7 @@ namespace i2p
virtual bool stop();
virtual void run () {};
bool isLogging;
bool isDaemon;
bool running;
protected:
@@ -38,7 +31,36 @@ namespace i2p
Daemon_Singleton_Private &d;
};
#ifdef _WIN32
#if defined(QT_GUI_LIB) // check if QT
#define Daemon i2p::util::DaemonQT::Instance()
// dummy, invoked from RunQT
class DaemonQT: public i2p::util::Daemon_Singleton
{
public:
static DaemonQT& Instance()
{
static DaemonQT instance;
return instance;
}
};
#elif defined(ANDROID)
#define Daemon i2p::util::DaemonAndroid::Instance()
// dummy, invoked from android/jni/DaemonAndroid.*
class DaemonAndroid: public i2p::util::Daemon_Singleton
{
public:
static DaemonAndroid& Instance()
{
static DaemonAndroid instance;
return instance;
}
};
#elif defined(_WIN32)
#define Daemon i2p::util::DaemonWin32::Instance()
class DaemonWin32 : public Daemon_Singleton
{
public:
@@ -54,7 +76,8 @@ namespace i2p
void run ();
};
#else
class DaemonLinux : public Daemon_Singleton
#define Daemon i2p::util::DaemonLinux::Instance()
class DaemonLinux : public Daemon_Singleton
{
public:
static DaemonLinux& Instance()

View File

@@ -13,14 +13,16 @@
#include "FS.h"
#include "Log.h"
#include "RouterContext.h"
#include "ClientContext.h"
void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening logs and tunnel configuration...");
i2p::log::Logger().Reopen ();
i2p::client::context.ReloadConfig();
break;
case SIGINT:
if (i2p::context.AcceptsTunnels () && !Daemon.gracefullShutdownInterval)
@@ -45,7 +47,7 @@ namespace i2p
{
bool DaemonLinux::start()
{
if (isDaemon == 1)
if (isDaemon)
{
pid_t pid;
pid = fork();
@@ -73,10 +75,10 @@ namespace i2p
return false;
}
// close stdin/stdout/stderr descriptors
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
// point std{in,out,err} descriptors to /dev/null
stdin = freopen("/dev/null", "r", stdin);
stdout = freopen("/dev/null", "w", stdout);
stderr = freopen("/dev/null", "w", stderr);
}
// Pidfile

View File

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

View File

@@ -12,74 +12,45 @@ namespace i2p
namespace datagram
{
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner), m_Receiver (nullptr)
m_Owner (owner.get()),
m_Receiver (nullptr)
{
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
}
DatagramDestination::~DatagramDestination ()
{
m_Sessions.clear();
}
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort)
{
uint8_t buf[MAX_DATAGRAM_SIZE];
auto identityLen = m_Owner->GetIdentity ()->ToBuffer (buf, MAX_DATAGRAM_SIZE);
auto owner = m_Owner;
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
uint8_t * buf = v.data();
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen;
auto signatureLen = m_Owner->GetIdentity ()->GetSignatureLen ();
auto signatureLen = m_Identity.GetSignatureLen ();
uint8_t * buf1 = signature + signatureLen;
size_t headerLen = identityLen + signatureLen;
memcpy (buf1, payload, len);
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(buf1, len, hash);
m_Owner->Sign (hash, 32, signature);
owner->Sign (hash, 32, signature);
}
else
m_Owner->Sign (buf1, len, signature);
owner->Sign (buf1, len, signature);
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto remote = m_Owner->FindLeaseSet (ident);
if (remote)
m_Owner->GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote));
else
m_Owner->RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg));
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto session = ObtainSession(ident);
session->SendMsg(msg);
}
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, std::shared_ptr<I2NPMessage> msg)
{
if (remote)
SendMsg (msg, remote);
}
void DatagramDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto outboundTunnel = m_Owner->GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty () && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
uint32_t i = rand () % leases.size ();
auto garlic = m_Owner->WrapMessage (remote, msg, true);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
leases[i]->tunnelGateway, leases[i]->tunnelID,
garlic
});
outboundTunnel->SendTunnelDataMsg (msgs);
}
else
{
if (outboundTunnel)
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
else
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
}
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
{
i2p::data::IdentityEx identity;
size_t identityLen = identity.FromBuffer (buf, len);
@@ -98,25 +69,33 @@ namespace datagram
if (verified)
{
auto it = m_ReceiversByPorts.find (toPort);
if (it != m_ReceiversByPorts.end ())
it->second (identity, fromPort, toPort, buf + headerLen, len -headerLen);
else if (m_Receiver != nullptr)
m_Receiver (identity, fromPort, toPort, buf + headerLen, len -headerLen);
auto r = FindReceiver(toPort);
if(r)
r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
else
LogPrint (eLogWarning, "Receiver for datagram is not set");
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort);
}
else
LogPrint (eLogWarning, "Datagram signature verification failed");
}
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
{
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
Receiver r = m_Receiver;
auto itr = m_ReceiversByPorts.find(port);
if (itr != m_ReceiversByPorts.end())
r = itr->second;
return r;
}
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
// unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen)
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
@@ -137,7 +116,331 @@ namespace datagram
else
msg = nullptr;
return msg;
}
}
void DatagramDestination::CleanUp ()
{
if (m_Sessions.empty ()) return;
auto now = i2p::util::GetMillisecondsSinceEpoch();
LogPrint(eLogDebug, "DatagramDestination: clean up sessions");
std::unique_lock<std::mutex> lock(m_SessionsMutex);
// for each session ...
for (auto it = m_Sessions.begin (); it != m_Sessions.end (); )
{
// check if expired
if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE)
{
LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32());
it = m_Sessions.erase (it); // we are expired
}
else
it++;
}
}
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & ident)
{
std::shared_ptr<DatagramSession> session = nullptr;
std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto itr = m_Sessions.find(ident);
if (itr == m_Sessions.end()) {
// not found, create new session
session = std::make_shared<DatagramSession>(m_Owner, ident);
m_Sessions[ident] = session;
} else {
session = itr->second;
}
return session;
}
std::shared_ptr<DatagramSession::Info> DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash & remote)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( auto & item : m_Sessions)
{
if(item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
}
return nullptr;
}
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination),
m_RemoteIdentity(remoteIdent),
m_LastUse(i2p::util::GetMillisecondsSinceEpoch ()),
m_LastPathChange(0),
m_LastSuccess(0)
{
}
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
{
// we used this session
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
// schedule send
m_LocalDestination->GetService().post(std::bind(&DatagramSession::HandleSend, this, msg));
}
DatagramSession::Info DatagramSession::GetSessionInfo() const
{
if(!m_RoutingSession)
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
if (!routingPath)
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
auto lease = routingPath->remoteLease;
auto tunnel = routingPath->outboundTunnel;
if(lease)
{
if(tunnel)
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess);
else
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse, m_LastSuccess);
}
else if(tunnel)
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess);
else
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
}
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
{
if(!m_RoutingSession)
{
// try to get one
if(m_RemoteLeaseSet) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
else
{
UpdateLeaseSet(msg);
return;
}
}
// do we have a routing session?
if(m_RoutingSession)
{
// should we switch paths?
if(ShouldUpdateRoutingPath ())
{
LogPrint(eLogDebug, "DatagramSession: try getting new routing path");
// try switching paths
auto path = GetNextRoutingPath();
if(path)
UpdateRoutingPath (path);
else
ResetRoutingPath();
}
auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
// make sure we have a routing path
if (routingPath)
{
auto outboundTunnel = routingPath->outboundTunnel;
if (outboundTunnel)
{
if(outboundTunnel->IsEstablished())
{
m_LastSuccess = i2p::util::GetMillisecondsSinceEpoch ();
// we have a routing path and routing session and the outbound tunnel we are using is good
// wrap message with routing session and send down routing path's outbound tunnel wrapped for the IBGW
auto m = m_RoutingSession->WrapSingleMessage(msg);
routingPath->outboundTunnel->SendTunnelDataMsg({i2p::tunnel::TunnelMessageBlock{
i2p::tunnel::eDeliveryTypeTunnel,
routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID,
m
}});
return;
}
}
}
}
auto now = i2p::util::GetMillisecondsSinceEpoch ();
// if this path looks dead reset the routing path since we didn't seem to be able to get a path in time
if (m_LastPathChange && now - m_LastPathChange >= DATAGRAM_SESSION_PATH_TIMEOUT ) ResetRoutingPath();
UpdateLeaseSet(msg);
}
void DatagramSession::UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path)
{
if(m_RoutingSession == nullptr && m_RemoteLeaseSet)
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
if(!m_RoutingSession) return;
// set routing path and update time we last updated the routing path
m_RoutingSession->SetSharedRoutingPath (path);
m_LastPathChange = i2p::util::GetMillisecondsSinceEpoch ();
}
bool DatagramSession::ShouldUpdateRoutingPath() const
{
bool dead = m_RoutingSession == nullptr || m_RoutingSession->GetSharedRoutingPath () == nullptr;
auto now = i2p::util::GetMillisecondsSinceEpoch ();
// we need to rotate paths becuase the routing path is too old
// if (now - m_LastPathChange >= DATAGRAM_SESSION_PATH_SWITCH_INTERVAL) return true;
// too fast switching paths
if (now - m_LastPathChange < DATAGRAM_SESSION_PATH_MIN_LIFETIME ) return false;
// our path looks dead so we need to rotate paths
if (now - m_LastSuccess >= DATAGRAM_SESSION_PATH_TIMEOUT) return !dead;
// if we have a routing session and routing path we don't need to switch paths
return dead;
}
bool DatagramSession::ShouldSwitchLease() const
{
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr;
std::shared_ptr<const i2p::data::Lease> currentLease = nullptr;
if(m_RoutingSession)
routingPath = m_RoutingSession->GetSharedRoutingPath ();
if(routingPath)
currentLease = routingPath->remoteLease;
if(currentLease) // if we have a lease return true if it's about to expire otherwise return false
return currentLease->ExpiresWithin( DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE );
// we have no current lease, we should switch
return currentLease == nullptr;
}
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetNextRoutingPath()
{
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel = nullptr;
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr;
// get existing routing path if we have one
if(m_RoutingSession)
routingPath = m_RoutingSession->GetSharedRoutingPath();
// do we have an existing outbound tunnel and routing path?
if(routingPath && routingPath->outboundTunnel)
{
// is the outbound tunnel we are using good?
if (routingPath->outboundTunnel->IsEstablished())
{
// ya so let's stick with it
outboundTunnel = routingPath->outboundTunnel;
}
else
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels
}
// do we have an outbound tunnel that works already ?
if(!outboundTunnel)
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); // no, let's get a new outbound tunnel as we probably just started
if(outboundTunnel)
{
std::shared_ptr<const i2p::data::Lease> lease = nullptr;
// should we switch leases ?
if (ShouldSwitchLease ())
{
// yes, get next available lease
lease = GetNextLease();
}
else if (routingPath)
{
if(routingPath->remoteLease)
{
if(routingPath->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE))
lease = GetNextLease();
else
lease = routingPath->remoteLease;
}
}
else
lease = GetNextLease();
if(lease)
{
// we have a valid lease to use and an outbound tunnel
// create new routing path
uint32_t now = i2p::util::GetSecondsSinceEpoch();
routingPath = std::make_shared<i2p::garlic::GarlicRoutingPath>(i2p::garlic::GarlicRoutingPath{
outboundTunnel,
lease,
0,
now,
0
});
}
else // we don't have a new routing path to give
routingPath = nullptr;
}
return routingPath;
}
void DatagramSession::ResetRoutingPath()
{
if(m_RoutingSession)
{
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
if(routingPath && routingPath->remoteLease) // we have a remote lease already specified and a routing path
{
// get outbound tunnel on this path
auto outboundTunnel = routingPath->outboundTunnel;
// is this outbound tunnel there and established
if (outboundTunnel && outboundTunnel->IsEstablished())
m_InvalidIBGW.push_back(routingPath->remoteLease->tunnelGateway); // yes, let's mark remote lease as dead because the outbound tunnel seems fine
}
// reset the routing path
UpdateRoutingPath(nullptr);
}
}
std::shared_ptr<const i2p::data::Lease> DatagramSession::GetNextLease()
{
auto now = i2p::util::GetMillisecondsSinceEpoch ();
std::shared_ptr<const i2p::data::Lease> next = nullptr;
if(m_RemoteLeaseSet)
{
std::vector<i2p::data::IdentHash> exclude;
for(const auto & ident : m_InvalidIBGW)
exclude.push_back(ident);
// find get all leases that are not in our ban list and are not going to expire within our lease set handover window + fudge
auto leases = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( [&exclude, now] (const i2p::data::Lease & l) -> bool {
if(exclude.size())
{
auto end = std::end(exclude);
return std::find_if(exclude.begin(), end, [l, now] ( const i2p::data::IdentHash & ident) -> bool {
return ident == l.tunnelGateway;
}) != end;
}
else
return false;
});
if(leases.size())
{
// pick random valid next lease
uint32_t idx = rand() % leases.size();
next = leases[idx];
}
else
LogPrint(eLogWarning, "DatagramDestination: no leases to use");
}
return next;
}
void DatagramSession::UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg)
{
LogPrint(eLogInfo, "DatagramSession: updating lease set");
m_LocalDestination->RequestDestination(m_RemoteIdentity, std::bind(&DatagramSession::HandleGotLeaseSet, this, std::placeholders::_1, msg));
}
void DatagramSession::HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent, std::shared_ptr<I2NPMessage> msg)
{
if(remoteIdent)
{
// update routing session
if(m_RoutingSession)
m_RoutingSession = nullptr;
m_RoutingSession = m_LocalDestination->GetRoutingSession(remoteIdent, true);
// clear invalid IBGW as we have a new lease set
m_InvalidIBGW.clear();
m_RemoteLeaseSet = remoteIdent;
// update routing path
auto path = GetNextRoutingPath();
if (path)
UpdateRoutingPath(path);
else
ResetRoutingPath();
// send the message that was queued if it was provided
if(msg)
HandleSend(msg);
}
}
}
}

View File

@@ -9,6 +9,7 @@
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
#include "Garlic.h"
namespace i2p
{
@@ -18,13 +19,98 @@ namespace client
}
namespace datagram
{
const size_t MAX_DATAGRAM_SIZE = 32768;
// milliseconds for max session idle time
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
// milliseconds for how long we try sticking to a dead routing path before trying to switch
const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000;
// milliseconds interval a routing path is used before switching
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
// milliseconds before lease expire should we try switching leases
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 10 * 1000;
// milliseconds fudge factor for leases handover
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000;
// milliseconds minimum time between path switches
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
class DatagramSession
{
public:
DatagramSession(i2p::client::ClientDestination * localDestination,
const i2p::data::IdentHash & remoteIdent);
/** send an i2np message to remote endpoint for this session */
void SendMsg(std::shared_ptr<I2NPMessage> msg);
/** get the last time in milliseconds for when we used this datagram session */
uint64_t LastActivity() const { return m_LastUse; }
/** get the last time in milliseconds when we successfully sent data */
uint64_t LastSuccess() const { return m_LastSuccess; }
struct Info
{
std::shared_ptr<const i2p::data::IdentHash> IBGW;
std::shared_ptr<const i2p::data::IdentHash> OBEP;
const uint64_t activity;
const uint64_t success;
Info() : IBGW(nullptr), OBEP(nullptr), activity(0), success(0) {}
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a, const uint64_t s) :
activity(a),
success(s) {
if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
else IBGW = nullptr;
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
else OBEP = nullptr;
}
};
Info GetSessionInfo() const;
private:
/** update our routing path we are using, mark that we have changed paths */
void UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path);
/** return true if we should switch routing paths because of path lifetime or timeout otherwise false */
bool ShouldUpdateRoutingPath() const;
/** return true if we should switch the lease for out routing path otherwise return false */
bool ShouldSwitchLease() const;
/** get next usable routing path, try reusing outbound tunnels */
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetNextRoutingPath();
/**
* mark current routing path as invalid and clear it
* if the outbound tunnel we were using was okay don't use the IBGW in the routing path's lease next time
*/
void ResetRoutingPath();
/** get next usable lease, does not fetch or update if expired or have no lease set */
std::shared_ptr<const i2p::data::Lease> GetNextLease();
void HandleSend(std::shared_ptr<I2NPMessage> msg);
void HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent,
std::shared_ptr<I2NPMessage> msg);
void UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg=nullptr);
private:
i2p::client::ClientDestination * m_LocalDestination;
i2p::data::IdentHash m_RemoteIdentity;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
// Ident hash of IBGW that are invalid
std::vector<i2p::data::IdentHash> m_InvalidIBGW;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
uint64_t m_LastUse;
uint64_t m_LastPathChange;
uint64_t m_LastSuccess;
};
const size_t MAX_DATAGRAM_SIZE = 32768;
class DatagramDestination
{
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
public:
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination ();
@@ -34,21 +120,32 @@ namespace datagram
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port) { m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { m_ReceiversByPorts.erase (port); };
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
// clean up stale sessions
void CleanUp ();
private:
void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
Receiver FindReceiver(uint16_t port);
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
i2p::client::ClientDestination * m_Owner;
i2p::data::IdentityEx m_Identity;
Receiver m_Receiver; // default
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<DatagramSession> > m_Sessions;
std::mutex m_ReceiversMutex;
std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator;

View File

@@ -13,17 +13,11 @@ namespace i2p
{
namespace client
{
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),
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
{
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;
@@ -37,7 +31,7 @@ namespace client
{
int len = i2p::util::lexical_cast<int>(it->second, inboundTunnelLen);
if (len > 0)
if (len >= 0)
{
inboundTunnelLen = len;
}
@@ -48,7 +42,7 @@ namespace client
{
int len = i2p::util::lexical_cast<int>(it->second, outboundTunnelLen);
if (len > 0)
if (len >= 0)
{
outboundTunnelLen = len;
}
@@ -103,24 +97,20 @@ namespace client
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers);
if (m_IsPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
}
ClientDestination::~ClientDestination ()
LeaseSetDestination::~LeaseSetDestination ()
{
if (m_IsRunning)
Stop ();
for (auto it: m_LeaseSetRequests)
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);
if (m_DatagramDestination)
delete m_DatagramDestination;
}
void ClientDestination::Run ()
void LeaseSetDestination::Run ()
{
while (m_IsRunning)
{
@@ -135,26 +125,25 @@ namespace client
}
}
void ClientDestination::Start ()
bool LeaseSetDestination::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_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
return true;
}
else
return false;
}
void ClientDestination::Stop ()
bool LeaseSetDestination::Stop ()
{
if (m_IsRunning)
{
@@ -162,16 +151,6 @@ namespace client
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);
@@ -184,16 +163,44 @@ namespace client
delete m_Thread;
m_Thread = 0;
}
return true;
}
else
return false;
}
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
{
if (!it->second->IsExpired ())
return it->second;
std::shared_ptr<i2p::data::LeaseSet> remoteLS;
{
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
remoteLS = it->second;
}
if (remoteLS)
{
if (!remoteLS->IsExpired ())
{
if (remoteLS->ExpiresSoon())
{
LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire");
// update now before expiration for smooth handover
auto s = shared_from_this ();
RequestDestination(ident, [s, ident] (std::shared_ptr<i2p::data::LeaseSet> ls) {
if(ls && !ls->IsExpired())
{
ls->PopulateLeases();
{
std::lock_guard<std::mutex> _lock(s->m_RemoteLeaseSetsMutex);
s->m_RemoteLeaseSets[ident] = ls;
}
}
});
}
return remoteLS;
}
else
LogPrint (eLogWarning, "Destination: remote LeaseSet expired");
}
@@ -203,14 +210,15 @@ namespace client
if (ls && !ls->IsExpired ())
{
ls->PopulateLeases (); // since we don't store them in netdb
m_RemoteLeaseSets[ident] = ls;
std::lock_guard<std::mutex> _lock(m_RemoteLeaseSetsMutex);
m_RemoteLeaseSets[ident] = ls;
return ls;
}
}
return nullptr;
}
}
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::GetLeaseSet ()
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
{
if (!m_Pool) return nullptr;
if (!m_LeaseSet)
@@ -218,12 +226,25 @@ namespace client
return m_LeaseSet;
}
void ClientDestination::UpdateLeaseSet ()
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
{
m_LeaseSet.reset (new i2p::data::LeaseSet (m_Pool));
m_LeaseSet.reset (newLeaseSet);
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
if (m_IsPublic)
{
m_PublishVerificationTimer.cancel ();
Publish ();
}
}
void LeaseSetDestination::UpdateLeaseSet ()
{
int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
CreateNewLeaseSet (m_Pool->GetInboundTunnels (numTunnels));
}
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{
struct
{
@@ -239,17 +260,17 @@ namespace client
return true;
}
void ClientDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, shared_from_this (), msg));
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
}
void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
}
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID)
@@ -272,7 +293,7 @@ namespace client
}
}
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
{
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
@@ -285,6 +306,7 @@ namespace client
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
{
LogPrint (eLogDebug, "Remote LeaseSet");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET);
if (it != m_RemoteLeaseSets.end ())
{
@@ -336,7 +358,7 @@ namespace client
}
}
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
{
i2p::data::IdentHash key (buf);
int num = buf[32]; // num
@@ -379,35 +401,29 @@ namespace client
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
}
void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::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");
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
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,
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1));
}
else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
}
void ClientDestination::SetLeaseSetUpdated ()
{
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
void LeaseSetDestination::SetLeaseSetUpdated ()
{
UpdateLeaseSet ();
if (m_IsPublic)
{
m_PublishVerificationTimer.cancel ();
Publish ();
}
}
void ClientDestination::Publish ()
void LeaseSetDestination::Publish ()
{
if (!m_LeaseSet || !m_Pool)
{
@@ -425,6 +441,12 @@ namespace client
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
return;
}
auto inbound = m_Pool->GetNextInboundTunnel ();
if (!inbound)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
return;
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
if (!floodfill)
{
@@ -435,14 +457,14 @@ namespace client
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));
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken, inbound));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1));
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
}
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
@@ -455,7 +477,7 @@ namespace client
}
}
void ClientDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
@@ -464,25 +486,262 @@ namespace client
// "this" added due to bug in gcc 4.7-4.8
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet)
if (leaseSet && s->m_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;
}
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return;
}
else
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet");
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
// we have to publish again
s->Publish ();
s->Publish ();
});
}
}
bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
if (!m_Pool || !IsReady ())
{
if (requestComplete)
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
return false;
}
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true;
}
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
{
auto s = shared_from_this ();
m_Service.post ([dest, notify, s](void)
{
auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ())
{
auto requestComplete = it->second->requestComplete;
s->m_LeaseSetRequests.erase (it);
if (notify && requestComplete) requestComplete (nullptr);
}
});
}
void LeaseSetDestination::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 LeaseSetDestination::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 (&LeaseSetDestination::HandleRequestTimoutTimer,
shared_from_this (), std::placeholders::_1, dest));
}
else
return false;
return true;
}
void LeaseSetDestination::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 LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
CleanupExpiredTags ();
CleanupRemoteLeaseSets ();
CleanupDestination ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
}
}
void LeaseSetDestination::CleanupRemoteLeaseSets ()
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
{
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
{
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_RemoteLeaseSets.erase (it);
}
else
++it;
}
}
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (isPublic, params),
m_Keys (keys), m_DatagramDestination (nullptr),
m_ReadyChecker(GetService())
{
if (isPublic)
PersistTemporaryKeys ();
else
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
if (isPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
}
ClientDestination::~ClientDestination ()
{
}
bool ClientDestination::Start ()
{
if (LeaseSetDestination::Start ())
{
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Start ();
return true;
}
else
return false;
}
bool ClientDestination::Stop ()
{
if (LeaseSetDestination::Stop ())
{
m_ReadyChecker.cancel();
m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Stop ();
if(m_DatagramDestination)
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
return true;
}
else
return false;
}
void ClientDestination::Ready(ReadyPromise & p)
{
ScheduleCheckForReady(&p);
}
void ClientDestination::ScheduleCheckForReady(ReadyPromise * p)
{
// tick every 100ms
m_ReadyChecker.expires_from_now(boost::posix_time::milliseconds(100));
m_ReadyChecker.async_wait([&, p] (const boost::system::error_code & ecode) {
HandleCheckForReady(ecode, p);
});
}
void ClientDestination::HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p)
{
if(ecode) // error happened
p->set_value(nullptr);
else if(IsReady()) // we are ready
p->set_value(std::shared_ptr<ClientDestination>(this));
else // we are not ready
ScheduleCheckForReady(p);
}
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = bufbe32toh (buf);
@@ -512,8 +771,8 @@ namespace client
default:
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
}
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
{
if (!streamRequestComplete)
@@ -526,7 +785,7 @@ namespace client
streamRequestComplete(CreateStream (leaseSet, port));
else
{
auto s = shared_from_this ();
auto s = GetSharedFromThis ();
RequestDestination (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
@@ -579,7 +838,7 @@ namespace client
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);
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
if (port)
m_StreamingDestinationsByPorts[port] = dest;
else // update default
@@ -587,176 +846,26 @@ namespace client
return dest;
}
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{
if (!m_DatagramDestination)
m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
if (m_DatagramDestination == nullptr)
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
return m_DatagramDestination;
}
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
{
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)
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
if (m_StreamingDestination)
{
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);
for (auto& it: m_StreamingDestination->GetStreams ())
ret.push_back (it.second);
}
for (auto& it: m_StreamingDestinationsByPorts)
for (auto& it1: it.second->GetStreams ())
ret.push_back (it1.second);
return ret;
}
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 ()
{
@@ -780,20 +889,20 @@ namespace client
return;
}
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
}
void ClientDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
{
auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), m_EncryptionPublicKey, tunnels);
// sign
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
SetLeaseSet (leaseSet);
}
void ClientDestination::CleanupDestination ()
{
if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
}
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
{
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
if (m_StreamingDestination)
{
for (auto& it: m_StreamingDestination->GetStreams ())
ret.push_back (it.second);
}
for (auto& it: m_StreamingDestinationsByPorts)
for (auto& it1: it.second->GetStreams ())
ret.push_back (it1.second);
return ret;
}
}
}

View File

@@ -8,6 +8,7 @@
#include <set>
#include <string>
#include <functional>
#include <future>
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
@@ -49,8 +50,8 @@ namespace client
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>
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<LeaseSetDestination>
{
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found
@@ -68,40 +69,21 @@ namespace client
public:
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~LeaseSetDestination ();
virtual void Start ();
virtual void Stop ();
virtual bool Start ();
virtual bool Stop ();
bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<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
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; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
// implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
// implements GarlicDestination
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet ();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@@ -111,9 +93,14 @@ namespace client
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated ();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
protected:
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
private:
void Run ();
@@ -129,29 +116,23 @@ namespace client
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 ();
void CleanupRemoteLeaseSets ();
private:
volatile bool m_IsRunning;
std::thread * m_Thread;
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];
mutable std::mutex m_RemoteLeaseSetsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::shared_ptr<i2p::data::LeaseSet> m_LeaseSet;
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic;
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;
@@ -159,6 +140,75 @@ namespace client
// for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
};
class ClientDestination: public LeaseSetDestination
{
public:
// type for informing that a client destination is ready
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
bool Start ();
bool Stop ();
// informs promise with shared_from_this() when this destination is ready to use
// if cancelled before ready, informs promise with nullptr
void Ready(ReadyPromise & p);
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
// streaming
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; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
// implements LocalDestination
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
protected:
void CleanupDestination ();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
private:
std::shared_ptr<ClientDestination> GetSharedFromThis ()
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
void PersistTemporaryKeys ();
void ScheduleCheckForReady(ReadyPromise * p);
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
private:
i2p::data::PrivateKeys m_Keys;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
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_ReadyChecker;
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
};
}

17
FS.cpp
View File

@@ -55,6 +55,14 @@ namespace fs {
dataDir += "/Library/Application Support/" + appName;
return;
#else /* other unix */
#if defined(ANDROID)
if (boost::filesystem::exists("/sdcard"))
{
dataDir = "/sdcard/" + appName;
return;
}
// otherwise use /data/files
#endif
char *home = getenv("HOME");
if (isService) {
dataDir = "/var/lib/" + appName;
@@ -150,6 +158,13 @@ namespace fs {
}
void HashedStorage::Traverse(std::vector<std::string> & files) {
Iterate([&files] (const std::string & fname) {
files.push_back(fname);
});
}
void HashedStorage::Iterate(FilenameVisitor v)
{
boost::filesystem::path p(root);
boost::filesystem::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end;
@@ -158,7 +173,7 @@ namespace fs {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
files.push_back(t);
v(t);
}
}
} // fs

4
FS.h
View File

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

View File

@@ -94,7 +94,7 @@ namespace data
int numCertificates = 0;
if (!i2p::fs::ReadDir(certDir, files)) {
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir);
return;
}
@@ -114,6 +114,12 @@ namespace data
{
uint8_t buf[50], signatureBuf[64];
size_t len = family.length (), signatureLen = strlen (signature);
if (len + 32 > 50)
{
LogPrint (eLogError, "Family: ", family, " is too long");
return false;
}
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32;

View File

@@ -7,6 +7,7 @@
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Transports.h"
#include "Timestamp.h"
#include "Log.h"
#include "Garlic.h"
@@ -37,9 +38,6 @@ namespace garlic
GarlicRoutingSession::~GarlicRoutingSession ()
{
for (auto it: m_UnconfirmedTagsMsgs)
delete it.second;
m_UnconfirmedTagsMsgs.clear ();
}
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
@@ -93,18 +91,27 @@ namespace garlic
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
{
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
UnconfirmedTags * tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
auto& tags = *it;
if (tags->msgID == msgID)
{
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
it = m_UnconfirmedTagsMsgs.erase (it);
}
m_UnconfirmedTagsMsgs.erase (it);
delete tags;
else if (ts >= tags->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (tags->msgID);
it = m_UnconfirmedTagsMsgs.erase (it);
}
else
++it;
}
}
@@ -116,23 +123,31 @@ namespace garlic
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
it++;
++it;
}
CleanupUnconfirmedTags ();
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
bool GarlicRoutingSession::CleanupUnconfirmedTags ()
{
bool ret = false;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (it->first);
delete it->second;
m_Owner->RemoveDeliveryStatusSession ((*it)->msgID);
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
else
it++;
++it;
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
return ret;
}
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
@@ -163,7 +178,7 @@ namespace garlic
// create message
if (!tagFound) // new session
{
LogPrint (eLogWarning, "Garlic: No tags available, will use ElGamal");
LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal");
if (!m_Destination)
{
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
@@ -232,7 +247,7 @@ namespace garlic
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
size_t size = 0;
@@ -243,9 +258,11 @@ namespace garlic
if (m_Owner)
{
// resubmit non-confirmed LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted &&
i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
}
// attach DeviveryStatus if necessary
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
@@ -257,7 +274,10 @@ namespace garlic
size += cloveSize;
(*numCloves)++;
if (newTags) // new tags created
m_UnconfirmedTagsMsgs[msgID] = newTags;
{
newTags->msgID = msgID;
m_UnconfirmedTagsMsgs.emplace_back (newTags);
}
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
}
else
@@ -268,7 +288,7 @@ namespace garlic
{
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
m_LeaseSetUpdateMsgID = msgID;
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
m_LeaseSetSubmissionTime = ts;
// clove if our leaseSet must be attached
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
size += CreateGarlicClove (payload + size, leaseSet, false);
@@ -285,7 +305,7 @@ namespace garlic
size += 3;
htobe32buf (payload + size, msgID); // MessageID
size += 4;
htobe64buf (payload + size, ts); // Expiration of message
htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec
size += 8;
return size;
}
@@ -514,22 +534,34 @@ namespace garlic
buf += 32;
uint32_t gwTunnel = bufbe32toh (buf);
buf += 4;
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from && from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel) // we have send it through an outbound tunnel
{
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
}
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
if (from) // received through an inbound tunnel
{
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
if (tunnel) // we have send it through an outbound tunnel
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
}
else // received directly
i2p::transport::transports.SendMessage (gwHash, i2p::CreateTunnelGatewayMsg (gwTunnel, msg)); // send directly
break;
}
case eGarlicDeliveryTypeRouter:
LogPrint (eLogWarning, "Garlic: type router not supported");
{
uint8_t * ident = buf;
buf += 32;
break;
if (!from) // received directly
i2p::transport::transports.SendMessage (ident,
CreateI2NPMessage (buf, GetI2NPMessageLength (buf)));
else
LogPrint (eLogWarning, "Garlic: type router for inbound tunnels not supported");
break;
}
default:
LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
}
@@ -585,7 +617,7 @@ namespace garlic
it = m_Tags.erase (it);
}
else
it++;
++it;
}
if (numExpiredTags > 0)
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
@@ -601,10 +633,10 @@ namespace garlic
it = m_Sessions.erase (it);
}
else
it++;
++it;
}
}
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
{
m_DeliveryStatusSessions.erase (msgID);
@@ -613,26 +645,26 @@ namespace garlic
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
{
m_DeliveryStatusSessions[msgID] = session;
}
}
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload ());
{
auto it = m_DeliveryStatusSessions.find (msgID);
if (it != m_DeliveryStatusSessions.end ())
if (it != m_DeliveryStatusSessions.end ())
{
it->second->MessageConfirmed (msgID);
m_DeliveryStatusSessions.erase (it);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
}
}
}
void GarlicDestination::SetLeaseSetUpdated ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it: m_Sessions)
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->SetLeaseSetUpdated ();
}

View File

@@ -83,6 +83,7 @@ namespace garlic
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags () { delete[] sessionTags; };
uint32_t msgID;
int numTags;
SessionTag * sessionTags;
uint32_t tagsCreationTime;
@@ -97,15 +98,17 @@ namespace garlic
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
void SetLeaseSetUpdated ()
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
@@ -123,7 +126,7 @@ namespace garlic
i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags;
int m_NumTags;
std::map<uint32_t, UnconfirmedTags *> m_UnconfirmedTagsMsgs;
std::list<std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs;
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
@@ -163,7 +166,7 @@ namespace garlic
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated ();
virtual std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;

108
Gzip.cpp Normal file
View File

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

44
Gzip.h Normal file
View File

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

View File

@@ -6,8 +6,10 @@
* See full license text in LICENSE file at top of project tree
*/
#include "util.h"
#include "HTTP.h"
#include <algorithm>
#include <ctime>
namespace i2p {
namespace http {
@@ -44,9 +46,10 @@ namespace http {
bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) {
std::size_t pos = 0;
std::size_t len = 2; /* strlen(": ") */
std::size_t max = line.length();
if ((pos = line.find(": ", pos)) == std::string::npos)
return false;
while (isspace(line.at(pos + len)))
while ((pos + len) < max && isspace(line.at(pos + len)))
len++;
std::string name = line.substr(0, pos);
std::string value = line.substr(pos + len);
@@ -54,6 +57,13 @@ namespace http {
return true;
}
void gen_rfc1123_date(std::string & out) {
std::time_t now = std::time(nullptr);
char buf[128];
std::strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&now));
out = buf;
}
bool URL::parse(const char *str, std::size_t len) {
std::string url(str, len ? len : strlen(str));
return parse(url);
@@ -62,7 +72,8 @@ namespace http {
bool URL::parse(const std::string& url) {
std::size_t pos_p = 0; /* < current parse position */
std::size_t pos_c = 0; /* < work position */
if (url.at(0) != '/') {
if(url.at(0) != '/' || pos_p > 0) {
std::size_t pos_s = 0;
/* schema */
pos_c = url.find("://");
if (pos_c != std::string::npos) {
@@ -70,8 +81,9 @@ namespace http {
pos_p = pos_c + 3;
}
/* user[:pass] */
pos_c = url.find('@', pos_p);
if (pos_c != std::string::npos) {
pos_s = url.find('/', pos_p); /* find first slash */
pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */
if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) {
std::size_t delim = url.find(':', pos_p);
if (delim != std::string::npos && delim < pos_c) {
user = url.substr(pos_p, delim - pos_p);
@@ -148,7 +160,7 @@ namespace http {
strsplit(query, tokens, '&');
params.clear();
for (auto it : tokens) {
for (const auto& it : tokens) {
std::size_t eq = it.find ('=');
if (eq != std::string::npos) {
auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
@@ -184,6 +196,25 @@ namespace http {
return out;
}
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) {
add_header(name, value.c_str(), replace);
}
void HTTPMsg::add_header(const char *name, const char *value, bool replace) {
std::size_t count = headers.count(name);
if (count && !replace)
return;
if (count) {
headers[name] = value;
return;
}
headers.insert(std::pair<std::string, std::string>(name, value));
}
void HTTPMsg::del_header(const char *name) {
headers.erase(name);
}
int HTTPReq::parse(const char *buf, size_t len) {
std::string str(buf, len);
return parse(str);
@@ -225,21 +256,12 @@ namespace http {
if (pos >= eoh)
break;
}
auto it = headers.find("Host");
if (it != headers.end ()) {
host = it->second;
} else if (version == "HTTP/1.1") {
return -1; /* 'Host' header required for HTTP/1.1 */
} else if (url.host != "") {
host = url.host;
}
return eoh + strlen(HTTP_EOH);
}
std::string HTTPReq::to_string() {
std::stringstream ss;
ss << method << " " << uri << " " << version << CRLF;
ss << "Host: " << host << CRLF;
for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF;
}
@@ -256,7 +278,16 @@ namespace http {
return false;
}
long int HTTPRes::length() {
bool HTTPRes::is_gzipped() {
auto it = headers.find("Content-Encoding");
if (it == headers.end())
return false; /* no header */
if (it->second.find("gzip") != std::string::npos)
return true; /* gotcha! */
return false;
}
long int HTTPMsg::content_length() {
unsigned long int length = 0;
auto it = headers.find("Content-Length");
if (it == headers.end())
@@ -311,12 +342,24 @@ namespace http {
}
std::string HTTPRes::to_string() {
if (version == "HTTP/1.1" && headers.count("Date") == 0) {
std::string date;
gen_rfc1123_date(date);
add_header("Date", date.c_str());
}
if (status == "OK" && code != 200)
status = HTTPCodeToStatus(code); // update
if (body.length() > 0 && headers.count("Content-Length") == 0)
add_header("Content-Length", std::to_string(body.length()).c_str());
/* build response */
std::stringstream ss;
ss << version << " " << code << " " << status << CRLF;
for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF;
}
ss << CRLF;
if (body.length() > 0)
ss << body;
return ss.str();
}
@@ -366,11 +409,10 @@ namespace http {
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
std::string hexLen;
long int len;
while (!in.eof ()) {
std::getline (in, hexLen);
errno = 0;
len = strtoul(hexLen.c_str(), (char **) NULL, 16);
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
if (errno != 0)
return false; /* conversion error */
if (len == 0)

46
HTTP.h
View File

@@ -38,7 +38,7 @@ namespace http {
* @brief Tries to parse url from string
* @return true on success, false on invalid url
*/
bool parse (const char *str, size_t len = 0);
bool parse (const char *str, std::size_t len = 0);
bool parse (const std::string& url);
/**
@@ -54,12 +54,21 @@ namespace http {
std::string to_string ();
};
struct HTTPReq {
struct HTTPMsg {
std::map<std::string, std::string> headers;
void add_header(const char *name, std::string & value, bool replace = false);
void add_header(const char *name, const char *value, bool replace = false);
void del_header(const char *name);
/** @brief Returns declared message length or -1 if unknown */
long int content_length();
};
struct HTTPReq : HTTPMsg {
std::string version;
std::string method;
std::string uri;
std::string host;
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {};
@@ -75,11 +84,18 @@ namespace http {
std::string to_string();
};
struct HTTPRes {
std::map<std::string, std::string> headers;
struct HTTPRes : HTTPMsg {
std::string version;
std::string status;
unsigned short int code;
/**
* @brief Simplifies response generation
*
* If this variable is set, on @a to_string() call:
* * Content-Length header will be added if missing,
* * contents of @a body will be included in generated response
*/
std::string body;
HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {}
@@ -91,14 +107,20 @@ namespace http {
int parse(const char *buf, size_t len);
int parse(const std::string& buf);
/** @brief Serialize HTTP response to string */
/**
* @brief Serialize HTTP response to string
* @note If @a version is set to HTTP/1.1, and Date header is missing,
* it will be generated based on current time and added to headers
* @note If @a body is set and Content-Length header is missing,
* this header will be added, based on body's length
*/
std::string to_string();
/** @brief Checks that response declared as chunked data */
bool is_chunked();
/** @brief Returns declared response length or -1 if unknown */
long int length();
/** @brief Checks that response contains compressed data */
bool is_gzipped();
};
/**
@@ -115,6 +137,14 @@ namespace http {
* @return Decoded string
*/
std::string UrlDecode(const std::string& data, bool null = false);
/**
* @brief Merge HTTP response content with Transfer-Encoding: chunked
* @param in Input stream
* @param out Output stream
* @return true on success, false otherwise
*/
bool MergeChunkedResponse (std::istream& in, std::ostream& out);
} // http
} // i2p

View File

@@ -1,9 +1,14 @@
#include <cstring>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <string>
#include <atomic>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include "I2PService.h"
#include "Destination.h"
#include "HTTPProxy.h"
#include "util.h"
#include "Identity.h"
@@ -13,72 +18,77 @@
#include "I2PEndian.h"
#include "I2PTunnel.h"
#include "Config.h"
#include "HTTP.h"
namespace i2p
{
namespace proxy
{
static const size_t http_buffer_size = 8192;
class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler>
namespace i2p {
namespace proxy {
std::map<std::string, std::string> jumpservices = {
{ "inr.i2p", "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/search/?q=" },
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
};
static const char *pageHead =
"<head>\r\n"
" <title>I2P HTTP proxy: error</title>\r\n"
" <style type=\"text/css\">\r\n"
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
" </style>\r\n"
"</head>\r\n"
;
bool str_rmatch(std::string & str, const char *suffix) {
auto pos = str.rfind (suffix);
if (pos == std::string::npos)
return false; /* not found */
if (str.length() == (pos + std::strlen(suffix)))
return true; /* match */
return false;
}
class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPReqHandler>
{
private:
enum state
{
GET_METHOD,
GET_HOSTNAME,
GET_HTTPV,
GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed
DONE
};
void EnterState(state nstate);
bool HandleData(uint8_t *http_buff, std::size_t len);
bool HandleRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void AsyncSockRead();
void HTTPRequestFailed(/*std::string message*/);
void RedirectToJumpService();
void ExtractRequest();
bool IsI2PAddress();
bool ValidateHTTPRequest();
void HandleJumpServices();
bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64);
void SanitizeHTTPRequest(i2p::http::HTTPReq & req);
void SentHTTPFailed(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
/* error helpers */
void GenericProxyError(const char *title, const char *description);
void HostNotFound(std::string & host);
void SendProxyError(std::string & content);
uint8_t m_http_buff[http_buffer_size];
uint8_t m_recv_chunk[8192];
std::string m_recv_buf; // from client
std::string m_send_buf; // to upstream
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::string m_request; //Data left to be sent
std::string m_url; //URL
std::string m_method; //Method
std::string m_version; //HTTP version
std::string m_address; //Address
std::string m_path; //Path
int m_port; //Port
state m_state;//Parsing state
public:
HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock)
{ EnterState(GET_METHOD); }
~HTTPProxyHandler() { Terminate(); }
void Handle () { AsyncSockRead(); }
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock) {}
~HTTPReqHandler() { Terminate(); }
void Handle () { AsyncSockRead(); } /* overload */
};
void HTTPProxyHandler::AsyncSockRead()
void HTTPReqHandler::AsyncSockRead()
{
LogPrint(eLogDebug, "HTTPProxy: async sock read");
if(m_sock) {
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
if (!m_sock) {
LogPrint(eLogError, "HTTPProxy: no socket for read");
return;
}
m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)),
std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
void HTTPProxyHandler::Terminate() {
void HTTPReqHandler::Terminate() {
if (Kill()) return;
if (m_sock)
{
@@ -89,213 +99,192 @@ namespace proxy
Done(shared_from_this());
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
{
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
void HTTPReqHandler::GenericProxyError(const char *title, const char *description) {
std::stringstream ss;
ss << "<h1>Proxy error: " << title << "</h1>\r\n";
ss << "<p>" << description << "</p>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
void HTTPProxyHandler::RedirectToJumpService(/*HTTPProxyHandler::errTypes error*/)
{
std::stringstream response;
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response.str (),response.str ().length ()),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
void 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(m_url, m, rHTTP, boost::match_extra))
{
server=m[1].str();
if (m[2].str() != "") port=m[3].str();
path=m[4].str();
void HTTPReqHandler::HostNotFound(std::string & host) {
std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n"
<< "<p>Remote host not found in router's addressbook</p>\r\n"
<< "<p>You may try to find this host on jumpservices below:</p>\r\n"
<< "<ul>\r\n";
for (const auto& js : jumpservices) {
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
}
LogPrint(eLogDebug, "HTTPProxy: server: ", server, ", port: ", port, ", path: ", path);
m_address = server;
m_port = boost::lexical_cast<int>(port);
m_path = path;
ss << "</ul>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
bool HTTPProxyHandler::ValidateHTTPRequest()
void HTTPReqHandler::SendProxyError(std::string & content)
{
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
{
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
HTTPRequestFailed(); //TODO: send right stuff
i2p::http::HTTPRes res;
res.code = 500;
res.add_header("Content-Type", "text/html; charset=UTF-8");
res.add_header("Connection", "close");
std::stringstream ss;
ss << "<html>\r\n" << pageHead
<< "<body>" << content << "</body>\r\n"
<< "</html>\r\n";
res.body = ss.str();
std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(response),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64)
{
const char *param = "i2paddresshelper=";
std::size_t pos = url.query.find(param);
std::size_t len = std::strlen(param);
std::map<std::string, std::string> params;
if (pos == std::string::npos)
return false; /* not found */
if (!url.parse_query(params))
return false;
}
std::string value = params["i2paddresshelper"];
len += value.length();
b64 = i2p::http::UrlDecode(value);
url.query.replace(pos, len, "");
return true;
}
void HTTPProxyHandler::HandleJumpServices()
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
{
static const char * helpermark1 = "?i2paddresshelper=";
static const char * helpermark2 = "&i2paddresshelper=";
size_t addressHelperPos1 = m_path.rfind (helpermark1);
size_t addressHelperPos2 = m_path.rfind (helpermark2);
size_t addressHelperPos;
if (addressHelperPos1 == std::string::npos)
{
if (addressHelperPos2 == std::string::npos)
return; //Not a jump service
else
addressHelperPos = addressHelperPos2;
/* drop common headers */
req.del_header("Referer");
req.del_header("Via");
req.del_header("Forwarded");
/* drop proxy-disclosing headers */
std::vector<std::string> toErase;
for (const auto& it : req.headers) {
if (it.first.compare(0, 12, "X-Forwarded-") == 0) {
toErase.push_back(it.first);
} else if (it.first.compare(0, 6, "Proxy-") == 0) {
toErase.push_back(it.first);
} else {
/* allow */
}
}
else
{
if (addressHelperPos2 == std::string::npos)
addressHelperPos = addressHelperPos1;
else if ( addressHelperPos1 > addressHelperPos2 )
addressHelperPos = addressHelperPos1;
else
addressHelperPos = addressHelperPos2;
for (const auto& header : toErase) {
req.headers.erase(header);
}
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);
/* replace headers */
req.add_header("Connection", "close", true); /* keep-alive conns not supported yet */
req.add_header("User-Agent", "MYOB/6.66 (AN/ON)", true); /* privacy */
}
bool HTTPProxyHandler::IsI2PAddress()
/**
* @brief Try to parse request from @a m_recv_buf
* If parsing success, rebuild request and store to @a m_send_buf
* with remaining data tail
* @return true on processed request or false if more data needed
*/
bool HTTPReqHandler::HandleRequest()
{
auto pos = m_address.rfind (".i2p");
if (pos != std::string::npos && (pos+4) == m_address.length ())
{
i2p::http::HTTPReq req;
i2p::http::URL url;
std::string b64;
int req_len = 0;
req_len = req.parse(m_recv_buf);
if (req_len == 0)
return false; /* need more data */
if (req_len < 0) {
LogPrint(eLogError, "HTTPProxy: unable to parse request");
GenericProxyError("Invalid request", "Proxy unable to parse your request");
return true; /* parse error */
}
/* parsing success, now let's look inside request */
LogPrint(eLogDebug, "HTTPProxy: requested: ", req.uri);
url.parse(req.uri);
if (ExtractAddressHelper(url, b64)) {
i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64);
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host);
std::string full_url = url.to_string();
std::stringstream ss;
ss << "Host " << url.host << " added to router's addressbook from helper. "
<< "Click <a href=\"" << full_url << "\">here</a> to proceed.";
GenericProxyError("Addresshelper found", ss.str().c_str());
return true; /* request processed */
}
SanitizeHTTPRequest(req);
std::string dest_host = url.host;
uint16_t dest_port = url.port;
/* always set port, even if missing in request */
if (!dest_port) {
dest_port = (url.schema == "https") ? 443 : 80;
}
/* detect dest_host, set proper 'Host' header in upstream request */
auto h = req.headers.find("Host");
if (dest_host != "") {
/* absolute url, replace 'Host' header */
std::string h = dest_host;
if (dest_port != 0 && dest_port != 80)
h += ":" + std::to_string(dest_port);
req.add_header("Host", h, true);
} else if (h != req.headers.end()) {
/* relative url and 'Host' header provided. transparent proxy mode? */
i2p::http::URL u;
std::string t = "http://" + h->second;
u.parse(t);
dest_host = u.host;
dest_port = u.port;
} else {
/* relative url and missing 'Host' header */
GenericProxyError("Invalid request", "Can't detect destination host from request");
return true;
}
return false;
}
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
{
ExtractRequest(); //TODO: parse earlier
if (!ValidateHTTPRequest()) return false;
HandleJumpServices();
/* check dest_host really exists and inside I2P network */
i2p::data::IdentHash identHash;
if (IsI2PAddress ())
{
if (!i2p::client::context.GetAddressBook ().GetIdentHash (m_address, identHash)){
RedirectToJumpService();
return false;
if (str_rmatch(dest_host, ".i2p")) {
if (!i2p::client::context.GetAddressBook ().GetIdentHash (dest_host, identHash)) {
HostNotFound(dest_host);
return true; /* request processed */
}
/* TODO: outproxy handler here */
} else {
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet");
std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet";
GenericProxyError("Outproxy failure", message.c_str());
return true;
}
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)
{
*eol = 0; eol++;
if (strncmp ((const char *)http_buff, "Referer", 7) && strncmp ((const char *)http_buff, "Connection", 10)) // strip out referer and connection
{
if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent
m_request.append("User-Agent: MYOB/6.66 (AN/ON)");
else
m_request.append ((const char *)http_buff);
m_request.append ("\r\n");
}
isEndOfHeader = !http_buff[0];
auto l = eol - http_buff;
http_buff = eol;
len -= l;
if (len > 0) // \r
{
http_buff++;
len--;
}
}
}
m_request.append(reinterpret_cast<const char *>(http_buff),len);
/* make relative url */
url.schema = "";
url.host = "";
req.uri = url.to_string();
/* drop original request from recv buffer */
m_recv_buf.erase(0, req_len);
/* build new buffer from modified request and data from original request */
m_send_buf = req.to_string();
m_send_buf.append(m_recv_buf);
/* connect to destination */
LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port);
GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), dest_host, dest_port);
return true;
}
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
/* will be called after some data received from client */
void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, 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");
LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length());
if(ecode)
{
LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
@@ -303,54 +292,45 @@ namespace proxy
return;
}
if (HandleData(m_http_buff, len))
{
if (m_state == DONE)
{
LogPrint(eLogDebug, "HTTPProxy: requested: ", m_url);
GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address, m_port);
}
else
AsyncSockRead();
m_recv_buf.append(reinterpret_cast<const char *>(m_recv_chunk), len);
if (HandleRequest()) {
m_recv_buf.clear();
return;
}
AsyncSockRead();
}
void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
{
if (ecode)
LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (Kill()) return;
LogPrint (eLogInfo, "HTTPProxy: New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
Done(shared_from_this());
}
else
{
if (!stream) {
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
GenericProxyError("Host is down", "Can't create connection to requested host, it may be down");
return;
}
if (Kill())
return;
LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID());
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_send_buf.data()), m_send_buf.length());
Done (shared_from_this());
}
HTTPProxyServer::HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
{
}
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
return std::make_shared<HTTPProxyHandler> (this, socket);
return std::make_shared<HTTPReqHandler> (this, socket);
}
}
}
} // http
} // i2p

View File

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

View File

@@ -1,4 +1,3 @@
#include <ctime>
#include <iomanip>
#include <sstream>
#include <thread>
@@ -22,162 +21,16 @@
#include "ClientContext.h"
#include "HTTPServer.h"
#include "Daemon.h"
#include "util.h"
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#endif
// For image and info
#include "version.h"
namespace i2p {
namespace http {
const char *itoopieImage =
"<img alt=\"ICToopie Icon\" src=\"data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXM"
"AAA3XAAAN1wFCKJt4AAAAB3RJTUUH3ggRChYFXVBoSgAAIABJREFUeNrtnXl8VOX1/9/PvXcmewiQBB"
"J2CKsKihQXkCJuiD8VKyptXejXaikWbe1C1dqi0lpr7UvrgihV64ZCXaqCUBEUQVBAAZUl7EtYEkLIP"
"pmZ+zy/P+6dySx3JgESkpAcX/MiznLvc8/5POc55zznnAfaqFWTaIXPnAt0AzoBqYAB1AAVwAFgF3Ck"
"DQCnBvUHxgEXAMOBLsfw22+BT4ElwGKgrE1ftAwaBswEygFlv+QJvALX2AH8BchrY3HzpF8A+xtA4PU"
"BwxZgUhvLmwf9AfA1suBjgaEK+GWbDdA0dAswC0iwhVEvSk5A9smFThmIjFSUroPHC0cr0HYexNxTiH"
"aMfBFAiT2e99sA0PiUBXwMnFEfwZ/ZB3n1eTDmTDh3IMKdgoYZoi8CXBCABhioA/uRn3+H+OgreGcFq"
"vAoWj15udQ2Oj1tAGgcmgS8WJfgczsif3sd3D4OkZyCZnqPQUWEkKaBlgDbd2PO+gDx5H/B462TZwq4"
"zPYc2gDQgPQmcH084Z/eE/nkHYjRw9H8VQ17c02A5ka99j/kb59DHDgSl3cC+BswrQ0AJ04GsB4YFEv"
"47VJQr/8eNW4kuv8kKF8jEfXSfOSUf6JVe+PydhEwtg0Ax0/Jtv+dHesLU65EPn0Xmt/XJM+ibn0M8+"
"XF6HH4+xVwdhsAjp0Sgb1AB6dxCoH67B+oEaeh+80mVE8GLP0a8+LfI6R05KcA1gFntQHg2GgX0N3pg"
"87tkd/NRktPbj7jr/SghkxG7j7k6DEI23O5uLkxWWumwl8WS/i9OmPueQ3RnIQPkJKI2PUq+jkDgs5l"
"pGdwEfDPNgDUTQ9hbd5EUfds5PZ/owvRPIHr98Oqp9EvHBITBFOBa9qWgNg0FFjrZO1npKIOvgm61my"
"1Vq1d4IbhP0euzo9pE3TAih62ASCCioH2TrNn72vQuUPzF34QBDoqdyLywBHHMa+zwd62BITQX+yZEU"
"X/uR+V04TCN9ygFOaafNTbyzHnLsNc9g3S60ca7hjLgYlY9yxajNjFWcBNbRqgltKBUidmTRiFnPcnd"
"L9DwEUI0JNgz17k5xuRBYfRvX7I6YD8/mBEr+5o/uoTEHwC6vn3UE+9h9qwwxmAw/oh/3or4qJhaE5j"
"fGcF5vUzHH/rtV3dNgBgxfdvcfL1a+YjhIgep6Ej/zYX+eg8tMOlzs/RLQv52M/gujHo/pr6D0bXYG0"
"+5iX3II5W1I9Hlw/HnP8Qhimjtce432N+uDoKBAJ4AJje2gHQDjjqNPtn34265ZJwxmkarMnHvOi3iA"
"pP/cY/5izkx4/UL2CkaTBvGf6Jfw6L7gXus/aCCy4YcujQoZL8/HzdXrKC4x7UHfXdbLTI+1TXINPHO"
"/JbNLUMmoMNMN1J+DkdkLdeGc4cXYO3l+M/ZypaiPAFsHvMmDFFl1122ZoxY8Zsyc7OLgxl7JKv0YZM"
"RhquugezJh8zQvjmpEmT9hUWFuYrpc5etmyZsWXLliylVOLs2bPXCyFKA/fauAcxfjr+SLsgORHtjz+"
"OuYl1F62c/Dhk3My5F7/vQ1Toa8XjmIHPhRAK2L1w4cIDSimPiqCCgoJdI0aM2EtIptAtl+BTH4VfM/"
"SlPkalJ9feIyEhQa5fv36Nik/Fffv2LbHHIwH5v4ejx24uQkLttUNe+1uz8K/CIZUrIxVTLUWGMXAhM"
"tFdK/y8vLzNSimzDuGo++67b37oPdY8HS2cwOuZqWECqtm0adNaVT86AhQEftuvK361NAIAC1G/uc4R"
"AAo4s7UuAT9xUv+/uQ5l1tSqcE3A/f9GeWwru127dnu2bt3auz7jnzFjxriJEyeuEkIIgDufRjm5boY"
"bZn4QHIuYPn367gEDBtTXV2+/atWqI4GlIH8f2uYdhFkCUsG06x1/q2jCBNOmNgKVEwDK/otKctcK10"
"hEuS5G+U3LaNq5c2dhz549s4/hPj4hxFEgE6BoHmSkhj+7pmHqlwXvWaaUcmFtR9ebMjMzNxcXF/cHm"
"DEJNe2GcIAabjhnCuaXW6KAexCrYKVVaQDH2TW8PzItNXxcK9cjbeGTnZ295xiFD+CaMmWKPwD4uZ9G"
"g+7bnbX3vP766w8fq/ABpk2bFrTqV26ytorDjB0v3Oi8H5hje0OtCgCOrJh4ocWoUFqxsXac11xzzXG"
"Nefz48cGrLvsWZUSkcBwuq00RHTNmzHFlGFx55ZU5gb93HUQ6cffakTG17oWtDQDnO6n/K8+JUs1s3x"
"9cT8WgQYNkHdfdiVUVFEaDBw/2Bf7eVgCROTyGXntfl8t1XBmFOTk5e4O+vxflJOrcXLTUxKjdQgWc0"
"9oAcKZT5C+vdzjbBODzhwfqnC722Wef7cnMzNwthOglhEjMzMxct2HDhj1BARtG8CpHK6OF0yWz9u/8"
"/PxOAEoppJSlU6ZM2dipU6cCIcSXEyZM2KaUKncaQ3l5eXrQHkhHd/T8vTDydEctcEZrA0CPyDfOykP"
"hD2eOlJCdEXxPff7551FFmgsWLDg4atSorsXFxd3t2WQUFxcPGTJkSJeFCxceBti2bVtwoyk1CREpnD"
"7dEQGj9IknnvABFBcXl+u6rs+cOXNQYWFhLvC9t956K0/TtIMQvee/fPny4FUHdEcqf/RDmyYM6VN/m"
"+hUBUCa05uDutuhkgjdOLRvSFRvyZLIHcODV1xxRaxqHu3yyy/XgKqXXnopKI7enR3EZyLGnGnBwuPx"
"dP/666935+Xl7QNSIpYqJYToO3Xq1PWRN3vooYeqA98dOwzNdFislILeOTENwVYDAEeXp1uWNUOi7IJ"
"za4VbVFTUafXq1RtCZr+POFnDQIfbb7/962effbZdQDgjT7eyd8IsdB9MqQ09q6FDh3rKysoGOvquSq"
"mnnnoqzGpftGjRVxs3buwf+MrE0bFd7JwOxLJjcloLABz3/TukoTktmwkuxPgRwVmohg8fHtQg+/btK"
"60r1vD888+PCHXrbr7YWTjXjkHLzggKp59SKl5BUW9gD8CKFSu2jh07tm8AYPdMRCkVGwDtU2Omkbca"
"ACThLGhHhvtNeGZqqLEoemVnZx+srKwsGjhwYHo9A04A/L9zUZkZzs/t98D8GfUPjuXn538+ZsyYb0e"
"OHNkXq9sInTKQf/kpuowDHU3EvEdGawGA476cz4zN/OwMtNl3WxaCUkoVFRV1Sk1NTZg5c+aeY4k8vv"
"w7hN8f+wvD+qH9YzL1iQPI/v37T1y6dOnpAYClJKK+eQ7N74v/Q1PGXAJcrQUAjiyqjJO9oxTcOg7jr"
"7eGCSdtzpw5I6ln7eeqf0JaUvwZ7jfhVxMwnrmTuuINQa8By1CVB96AjLS6NUhI0CkKG60FAJVOb+4p"
"wtTjjMjvg2k3YCx6GJmUEK3eY1G3LGT+i6hhfev3vH4f/OwK9J2voEYPiS+UIX2Q707HXDsLPSkBrT7"
"rx/7imOOoONmCMJoIAMWOAChEF5qThx0+Q8eciV71PuqRNzGffg+xtyiaoalJyAuHwE8vR1w1yioaPZ"
"YScSmhayba0sfQjpYhF3yJ2rwXUVqJmdkO47QeyEuGItLSrHzF+qacCQFbC1Ax3NZDJ1sQTbUbmGxrg"
"TCZdEzHPPweRn0TOYUAPQHwYe4uRPj8kJwAudmAjoYv2t07YYYJazk67hnngot+g1yyzjE9zDjZy0BT"
"bgc7bgXXLEBqIqab1OLJSIbkSzCrvVFayw+4W4sNAFbxZxR9/DWnNB04gHQQPlhl5LQmAKx3evO9ldY"
"O4KlK76+KaYqsbG0AWO20BL35CWiJp6bwDRe8sTTmUvxxawOAIytKKtBWf4N5KgLA40EuXR+T5/NbGw"
"A+j/XB0/+1agBONZr5flxtqFobAMBqohRF//4IzedvGoY0mvpPRP15Tkz1/3JTjaupAfCvWK7oA68it"
"VOol/m8j5HFZTHd7tlNNa7mwOJYcT9VMx+haS2/pb2RiOr8A9ShEsdnWYjVXbRVagCAR2IAUdz+BKbR"
"wkNCQsATc5ExhC+AGU06vmbAowSs3rqOa/6GWaiB3WmxJmGlB5lxTUxeb8U61ILWrAFqgEdjgfHSe1C"
"Gq2UK30hAjbsvpvAF8KumHmNzmVnTsGLhUXTwCNqND+NvaSDQNXj4VczPN8bUspuABU0+zmbEs93EaK"
"H2zU60HlmYZ+WhqRbiHK74DnnTIzEnmMCqjDrU1ONsbhb2GuLkxy97DHX+ac0fBNv2Yw68NW73D59t+"
"zQ5NTfjamw8UI76NWLtVqRoxo7hzoP4T7utztYvbqyDrZp+qWpm/KvCSrUeH+sLsz9EDO+PHNANTTYj"
"TaAJWL8D84zb0eKlhIfQ97CaSnzVBoBwWgecS5zj2V5fitAE8sJhCGk2/TJmuOHVxcjL7zvm84ausgG"
"/rs0GAObOhQ8+QLz8Msp2D+Pa/qMGIz/8M8JtNGETSRfqhzMw3/jkuCeTAO4B/tpmBAJCMFIpXsc63r"
"VOJa8J1CvTUD+67OScFhI665evx3/FH9DKqsL4qM7nbDqSIQ9QqK3hm/rwWQBPY5192GoB4BaCuUpxN"
"cexNTq0L2r5P8DVyNrAcMGuA6jJT6AWrQnn37WMlT/kKg2UkCh0NHR01vKt+ojP1CrW1XXO0HvA1a0R"
"AFcC79ZzPMECzsgPrj4P+e4DDX+CSKAl7RfrMR94BSK7fmbTUT3Ar0QmGULGwK6Ojh+/eoV31XyWiDj"
"PtpwY7fJPVQC8BfxACOKWYuaQLccx2ncOZ/o6kam2sUu7h0dTvCFFRmf0Qm6Y7dxXONCvxzTrl9ZtGJ"
"anvnkr5pyl8NwCKyoZ7beOkrfzQ91H/fLPNTQKOCin8VdR41wgJbDyA88/1QEwGPiEOgoiu5Erf8r1n"
"rMY5K+mJmy8bzI/4W0WBlOp774W+eht4YWZhhtmvYf8cDVKSkSfXNSg7ojeOaiMVLT0ZJQmrPMAj1bC"
"7kPIrQVoq7cgF64BUzovKSkkq3uYrAaSp/uPI4Otkmp1O/fidwaOAOZhHZN3SgLgfuDBgBp3KrZIJkl"
"N4UbPBXzP54kQfIDms9T9Mm8HI2oFc1DZIZW/moCH30D+4aWGe84cstRVXMJYRmlefCd0rU1sM6fzRL"
"xw8R3AM41q05xkwacDn2L1BwqKPEL4YjyXem7mB14fPmIJX0Own0NB5o0dhszNQg+tzFWg/vDSiQ+6P"
"e3UBQzjIkbQk66ahxpOVPgAQxio96OXmc9OJxAo2zN4HauZdosHwDXA20RUBIXO/q50lvcztaoD7ZSv"
"DgYnkKDW8m1w/HeOR0SWZb++JLwGbzTnmns5oO2hAB9+R2AlkyS70ln0opsaSB8xmAGiI+21GrwoFB5"
"qGowhXnxcw2XiEZ6N9RUFPAXc2JIB4Lbdm8siLfcQ4Ysfc7XnOsZ5a/Ai6+EF7qZAL6E0cCKHuvz88A"
"JNw4B5n9UCII8e8lf8n2EiMdCRSFVOpfTiFQJBAm6VTpoukbqJiR8TZY+jIYUeSd9jcF3L049bMgBGA"
"EvsiJ5ygncG6eoh7q7sRKaswVtvS/o9/ucOXHPCBSj8EZE4F+r9lbWz/xauFQFB2tpFuHHp7pBgYxXV"
"nGwy0EV72vlLKNXrMJg3NMb9tUYE1hu2T+uKYeKIUWqY/wUeqcimo1THEPvREHzE58HrTr4SEen7L15"
"VO/s7k6UGM6BZppVJJNl0rCuMvKElaYAJwNxYwZoA/VbdVnkeQ81o/1nV6Zx8wJKg8NOTURcNR4SWlB"
"s6vLAo1Pi4tFHV+ImQAlzxxfBhS/IC/g3cHE/wncmSM/h1VRop6niEn0Sieo/FQd//l9egTE+EJtNRc"
"2oLz9TFjBD+ZlptJoA4QSQBvNqY929ItTizLuFfxAjfs8yoSCNF1RWW0NAQCAo4qCXgVoHzIrexWy/m"
"aFBl3j0hOkPovyHG32jORaKaLOCVSALVeKQ7Rum/hkYhxfH6Ec1pCRqgHzA5nvCvZaz3x4yvqcErnFW"
"hItA9TUPjOV5P/IgVLstZEGoU3/MNYZD5DouCxt+lZyPbpYX7/oYBL1rHs+gAlzASWWe/p8aY2YJt7J"
"YzeFJU4RG96Sb/zr1a5GzX0JTtzcRS/6olAOD78f1AF5OY4KmiWsRaCQPCr6BK/IoHU8qoDNn0UXzKl"
"65P+TLMoPzNhGjfH5D/XWmpiySS1Bn016rxnHQAHKRI3sujwefdwV7xPkvkWEaFCXtP7CODBPBcY4+z"
"oZaA5+NFq3T0uDo4FOJT+VOo8IO92CLzANuloi45L9pgeGtZ7VoymnOaxPhLJIFHmBX1/qesUu4Ip2g"
"jW+PN8HdbCgAgTkJnNR7xBesNZ+FLBAINwYv8J6EKjwgLFMW42S+uQpkR5wYaBrywqPYnFzAM1QRFxl"
"vZJQs4GMWLQooJPftaR+drNsYa4OsnY6wNCYAvgHtjgeBv4tmk6Li+InASvBu3WslaV9jMV+ERw9DWM"
"VOvRkQaf6YfteDL4DOp0+jXJMbfmhhueyQYXRis5CvRVOq/MQJBD2PFrsMPfRDgVT5xFw+mxArzSqRI"
"I1XhgCClrGtI25Yb0A3ZKSt67M8tqLX2hjMkZry/MUlHZyf7HD9zYYQ9/Vd8J2NMGA/WplmLA4C1jMP"
"fIx9MAUcpE1P5U6qJiSL02RVevNzFT6rDIgKiFkChdONF0Y0ZjUR44t3ae57DmcJsAt9fR6OcCkfg+U"
"JOw9DR+JgVsS7zwskab2OFR39rxwQEhG/3HqZETOa+1AqqRKTW60GuvIfJ1YrwXUKlwq8xfkT0rFm3G"
"XPL3tr3z2+CAzgkUr3CO3IHex0/r6Raq8KjAEykWs6aWNb/yy0dAACvAGdBtBleQZW4nftSN7FN1yNS"
"6Rdbvn/Y+h+6lAC8+jGyqgYZ6B1gGPDQa7UXGckw5cI4qeq/iCPyRu7mbRaJeJ7HS8yTblx8yCexwp5"
"+2546aZHIBiUFbGCwGMIGFfSKrAcaDCgNEbrdKy5hpHcyP/J48XMXD6QWUiycMoSc3ptwAfLBW6wzhT"
"In1D7L37mHbuSeTACom7hbefE5tX+NMnrGcaFawRpKKXca4zzghhYLgOD6Hf32UwLuUIE0sJDvJuKmM"
"1nmLgr0+gg/8v9Tk5CV1bWnjbzPbGIHnRo+4vcOi8w5vB+qTcsmZVDR1UXKp5Uc+ayKHKxDMlQ95HEX"
"8M8WuQTMJe52zi90xA9DPw58twYvuynQNa3W4g8FqF1rJ2JpglDhA5RSftKcfxcGK1gbVhiyrS/mUzl"
"0mZZJxv960rtyIPLGduyq54Q7cjKXrgYFwAgeZ26Mh7yXnoYf9YaAoQJEQPjBYI/t5gUEnKzhfzKHzS"
"t7oeZ2Y98vO7K/h5viyMJLJx37AUuUOEn5rjp6WDh3eBKHurnoEBiTX4GElOe70PPlLmyvBwgOt0gAf"
"AK8wi/FDaDmhrw/i1xm00esQ8kXEDxiFUL2Ddh0gRkf+i8gHu7EnkkZDDg9Ee3yVLo+lE3u9jwyN+Wx"
"9/I0CoK/dxjLG7wvKqk6KVogAmji0lQSvA539iuY0I4+d3TgmzpAcLBFAmA01llw07GS2QOa4Gfs51v"
"2iwXsls+QIbrSTaym1zYXYriyNUGE8EFAoog+W7BaQVcX3d7uRtdNeRR1dVEYg5ni1/xZSRq/lYSIsK"
"U6GbHz2kwFT+YwECiLc8k9LQ4AS4EPQNwMarptC1xvT843gMeplgB3YfIj9sov0LTpZH/lFlo7oCBU+"
"EKgBKhfH8SbJJz3cf0WELJ29aP9be2d1eoRSsXPuFcVU6Ias9XgTvbJiLHFTe8yFUaqFiNQ0FJtgPsB"
"RY9gHlhoOcvEoFrOEjdRpv5Cd93Axz5d4+IJsqJHD/KASiHANgeEUlCp6DpsJ4UaURGjIFVJ3E/m0Gd"
"GNt85gaCMCjGFP/Im800dXWkNpPAEgkQS1Lfkq9/zSJgDtNWLHg9ufiitkPSOiaeTTKIhZr+HjqKAYv"
"XTGN+5kgzxfxxVW+ijJZPAdo6I6jFKZp93iKLDaLNmcbEQLITa+kBbKwig9I4O+G/MgGGJVBjCPnNYw"
"EEfe5ZXoS2qQH+9FFUl4x68qC5mBOczlNPoRwJuzY9JfcPFOjoJuNjJPrmElfyPzwKuZlixaprGgbKB"
"5FZE6C6XgKMmBefuIHGXz/ngTKz0r5tbFAAA3gHtGpCRLuB0+/U4XfTVpMvz2MFWMrTNJJs3vbJTlJa"
"h3XGHJQEhKFSKzIALGOYOKstWsOko1rk6qdQ2WjrmtT6T9rIX3UQvutGJTNWJTC2NFBJJUAKBDz8VVI"
"rDlMj9HBJb2ckGtigPNYHQZTndkPTAoJCj5NMl4Nnel8XWGdlk+hUFm2vouaSSqldL8a6uJjcOz4WtP"
"OfRUmgW8G8QHzJAADzChVHfeYw8A+AfZGiv0V+MI1sD+N3vLH1805AgQ2YLgRTWul/7r9VLuKlfgWqm"
"EvpRwpWUcCc1/ALFFBQ/Zq/9eeT3Q1/1ucdJpxNKCfsZMJfB2uVsMDeBWMnSsIe4mk5iMO3Mn5OijaC"
"repAj2gIKzUsvRf/7v5A/vxS9x3pLA2ga+UohlLKqdYMbQfFiqvG0mosictERwC4U0LGelxAYlNIZHT"
"DRqKELKXTFSy7J+ElAEd7WsiNdSeMA5XQ+Xo1kz6eTTie0BCwgV4xjv3qZwdzMhmBk7zqgEz3FU+xSk"
"8gWP6VQ/RGrRChAd16A/s/PLOHfMQV95rPcISVPaAIlVVDgIiLCHP85UijhdLycQRIppAeXdwMvGyhm"
"KZmouKAXdOMw15KGP6SPX31ySqup4UU7sh0+VlHP8adgdUlrORpgHPvVJ8BoOwNGBE3Z03Czhz/QWXx"
"qFWKJj6nNzX7sJsQXr1hsnTYNo8SDlJJUzT40Mij8qzmAi1QOotjHUUpIohQFpNm3KyWLJLpSzun4aU"
"+P4MwMTRb14mYAOfSljH/hxU/HGI8kGUcy3uNo4phEAj+nmq8o5BAmAkEqCWThZxUGVTH7IAis+r+qF"
"qcBAjQfxBUhCJ8IooLBKoES8RZ7w5B/xyC0nhmoHpeiCtpBUhJi8mSUYTBL+cVtZuhEuRZBp5CRavYr"
"dE5Jju2oRZMynicZ6eCvp1PCJDpwoodNaiGawwCeZDvK0fUTWI2yf9dUdtwJO8ZzgSsi1NsboJLYpv0"
"nQvgPno22dyOqqBi1Efjr47D4BWsM0i8GmPG0pLIF7QO89svHsZ+zqZPO2BgRxA54G6SEQIYsG5Y6i3"
"XE/RtNKfwGAYBTD5Nr6KLNo0q+ZP//tN7wu3SE2o4amoc6+n2YPh2uGop+9W0BnqlBUbPDy+5Geeq+5"
"JLqcH5xSj3X+2PncCz137WpPbkGzwi6jjOEQZW6DvgJML0DHDyI0HOgSqCOjIO1WxFTf4Lr7AtRN90W"
"nMOZUVngnkaK4fqAc0iI0AKCdNo3+L0q2E3shpcjTzkAzOMbBTkqGM0YiOjTGfHwFtTi3jBnPaJfGVp"
"7N77Jd1rzzdDEwGCMNSzWGzNiduLUz8Ho6tgIVSRVIaDSHTeKup5SALBAsLE2GrgC9ccdlqAPZSB67E"
"XMWYt5ur3lcUMvhKlUXiD6F7bqF1HdaPs4brIhYonJaoQOEV5Sgi5gF6yMuHA6+5QDQPDJIh6tfwGs2"
"YGcPhqu3w6fPoo41AuhFJmOFziA0WjtrCXQJWLvwN0oRYQq5C+N9ChLt+8pC4C1ayE3t/b/P95sPfz0"
"T+BWgbjvPUR5KZLo42Ks0Gg57fFQ0iiDU4BOedh7+2PGB04k0lITtDUUGon4IxzZLqcsAAD2xyh+XeN"
"DLP8MuXYtAEVhnnqot7++Eas7wqOCimWUNnjLjEi7xkVCRFQw7ZQGQCxav8FeC28HYEuYpx66ibKaZF"
"z17B51rCGw0ohedKV0Ib+Bc/IOBw1LgUGNXa4sGjoY1+IAEGIkQWgihAjODs1eDJJZFzeF6vhIx0MZq"
"VE6YSGJeBvIGHRhssIOBen4cJFIDUaEBiht3QB4KfjXUlsEwlacHpKosVVzCnoDLwV7KMHauCECfCm8"
"SPkJc0YDlnGASjIAQXYwLhCph3a0bgDU0pwwdahIJBMdDRNFEkspaDBlqQFrHXoXdgFSUZhk8zrF6Mf"
"ZD1YDNnOIr+kKKFLxkYKLcnwOu5Gr2wBg0b+i1PFhBN0QgORbulLaQD1ziznM7qDraYbxIweNZHwcoS"
"MfUnbMRqEBrGIbi+kEKNz46GTnJRwOb5Nr0xdtAKh1/cJBUI2BH0V7u5Z8Dj70E8ycEVQx116HXUhyQ"
"7Zt/HiQQC4GBpJtdGQ1+49B81TxNkWsIc/WYT664wI0SvDhj2oV9kJTM725nRmUjXWapgpzC/uisxMT"
"PwbZ7OaH9Dgu5awo5jUSKSMZ8NMHHZBstwHREUmGHXoyMdll8+cHFNOZrLjTaC+FfEA6pp0QkoGfLFx"
"IwIdkDypiwgmgE1DYlAxvbsfGVWIdFnVWGHtr8JGDzlEklbSngqP0JbHeO3cGUEARr5OMh2QAeqAF/y"
"ulxj7ixyTN5omGhgs/lRhsQqMPB0iinQMHJYso5nOysGoC/HRB0Q6XvYUt7YBzpPDvp5G7gLZEDRAAZ"
"U0UwzrjRaFxyF6VsyjiCjTS6Ri2/05YGOko24EVlFFK96Bm6YYXt531I4B9gMcWVx4ayr63AA7hpxwd"
"8HIhRxlMeyRuNLx8w2E+IR1JKtauv4+sEDXvR7Eb6SD8X2CdBUAbAJzpOmqLjWupD4rDVFMa3GARJLC"
"fXAyS8JBCd2oopgwfJeiU0t6e/9Z33fjJBfQQ004g2YZJID0uG5O0kM814ACSimCF8mEySeEwEiuDAF"
"z46IwgwW4CJIBKajgQteYLrJPS/9ZcGN2MT+HlQ6wzBmopGS9dSKAUH4WIei5hVgQuE500jChNcRBJO"
"aEF6X76YKAIL1IvwUsxRths1jDJQpJur/UBQB3G5Kij/yBsO6eouTDZaMYAqHJ4x025zfAUFEe/Nz35"
"AAABiUlEQVTwUoHAjJppVk5vMpJ0dNwkhC0TGlCJj8OANyIeoDA4iEnnkJZe1sEGbtojqcCHHz8JGCT"
"jQqIH+13VYHIAiT8uX4cAi9s0QHxKBKqDccGIM4VIwkMSbhLwY+BGpxrwIzAwcKHZwgv9XQ1evAiq0C"
"hH2QEZFZMvafjojIGsg0cC6+yXIkyqo1LCnWgHcc5Fbn0AOA34zjEqeEM9x69C/lVYuwuh28surGNr6"
"pOfH6kffWQCabijMv1N/FQgKMVPTdQOX11jfgbrRLBWTgMdATia+pVSncyyMB8JmCQiSUQFtdOJXfMn"
"bRrAmcqD1vWpTQLoBexqykE0t3N0noCoLdpTlRQnsSFkS9AABlbCtqL1kKDVJ4TU0sWtzAISWAdptmk"
"Am9phNX9QTcwD1cg8K8HqBLYO+FEbAMIpF3gc+AGNv1G1GPgSqzYgkKeTBmTar2ygg22TGHZgqgBYb/"
"+mHGvzKrRS0R/yqsZq++6BRshpPMUDQcfzHFrIsqZHhWqasAtHc6b/D3cbSAuGcmWdAAAAAElFTkSuQmCC\" />";
const char *itoopieFavicon =
"data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv"
@@ -208,48 +61,45 @@ namespace http {
" .tunnel.another { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
" table { width: 100%; border-collapse: collapse; text-align: center; }\r\n"
" .private { background: black; color: black; } .private:hover { background: black; color: white } \r\n"
" .slide p, .slide [type='checkbox']{ display:none; } \r\n"
" .slide [type='checkbox']:checked ~ p { display:block; } \r\n"
"</style>\r\n";
const char HTTP_PAGE_TUNNELS[] = "tunnels";
const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels";
const char HTTP_PAGE_TRANSPORTS[] = "transports";
const char HTTP_PAGE_TRANSPORTS[] = "transports";
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
const char HTTP_PAGE_JUMPSERVICES[] = "jumpservices";
const char HTTP_PAGE_COMMANDS[] = "commands";
const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels";
const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels";
const char HTTP_PAGE_LEASESETS[] = "leasesets";
const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit";
const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit";
const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start";
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
const char HTTP_PARAM_BASE32_ADDRESS[] = "b32";
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
std::map<std::string, std::string> jumpservices = {
{ "inr.i2p", "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/search/?q=" },
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
};
void ShowUptime (std::stringstream& s, int seconds) {
int num;
if ((num = seconds / 86400) > 0) {
s << num << " days, ";
seconds -= num;
seconds -= num * 86400;
}
if ((num = seconds / 3600) > 0) {
s << num << " hours, ";
seconds -= num;
seconds -= num * 3600;
}
if ((num = seconds / 60) > 0) {
s << num << " min, ";
seconds -= num;
seconds -= num * 60;
}
s << seconds << " seconds";
}
@@ -276,9 +126,13 @@ namespace http {
s <<
"<!DOCTYPE html>\r\n"
"<html lang=\"en\">\r\n" /* TODO: Add support for locale */
" <head>\r\n"
" <meta charset=\"UTF-8\">\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
" <link rel='shortcut icon' href='" << itoopieFavicon << "'>\r\n"
" <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
#if (!defined(WIN32))
" <meta charset=\"UTF-8\">\r\n"
#else
" <meta charset=\"windows-1251\">\r\n"
#endif
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
" <title>Purple I2P " VERSION " Webconsole</title>\r\n"
<< cssStyles <<
"</head>\r\n";
@@ -287,15 +141,17 @@ namespace http {
"<div class=header><b>i2pd</b> webconsole</div>\r\n"
"<div class=wrapper>\r\n"
"<div class=left>\r\n"
" <a href=/>Main page</a><br>\r\n<br>\r\n"
" <a href=/?page=" << HTTP_PAGE_COMMANDS << ">Router commands</a><br>\r\n"
" <a href=/?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << ">Local destinations</a><br>\r\n"
" <a href=/?page=" << HTTP_PAGE_TUNNELS << ">Tunnels</a><br>\r\n"
" <a href=/?page=" << HTTP_PAGE_TRANSIT_TUNNELS << ">Transit tunnels</a><br>\r\n"
" <a href=/?page=" << HTTP_PAGE_TRANSPORTS << ">Transports</a><br>\r\n"
" <a href=/?page=" << HTTP_PAGE_I2P_TUNNELS << ">I2P tunnels</a><br>\r\n"
" <a href=/?page=" << HTTP_PAGE_JUMPSERVICES << ">Jump services</a><br>\r\n"
" <a href=/?page=" << HTTP_PAGE_SAM_SESSIONS << ">SAM sessions</a><br>\r\n"
" <a href=\"/\">Main page</a><br>\r\n<br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a><br>\r\n";
if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"/?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a><br>\r\n";
s <<
"</div>\r\n"
"<div class=right>";
}
@@ -318,15 +174,34 @@ namespace http {
s << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n";
s << "<b>Status:</b> ";
s << "<b>Network status:</b> ";
switch (i2p::context.GetStatus ())
{
case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Testing"; break;
case eRouterStatusFirewalled: s << "Firewalled"; break;
case eRouterStatusError:
{
s << "Error";
switch (i2p::context.GetError ())
{
case eRouterErrorClockSkew:
s << "<br>Clock skew";
break;
default: ;
}
break;
}
default: s << "Unknown";
}
s << "<br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
if (auto remains = Daemon.gracefullShutdownInterval) {
s << "<b>Stopping in:</b> ";
s << remains << " seconds";
s << "<br>\r\n";
}
#endif
auto family = i2p::context.GetFamily ();
if (family.length () > 0)
s << "<b>Family:</b> " << family << "<br>\r\n";
@@ -351,8 +226,12 @@ namespace http {
s << numKBytesSent / 1024 / 1024 << " GiB";
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n<br>\r\n";
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n" ;
for (auto address : i2p::context.GetRouterInfo().GetAddresses())
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
{
switch (address->transportStyle)
{
@@ -373,6 +252,7 @@ namespace http {
}
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
}
s << "</p>\r\n</div>\r\n";
s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
@@ -380,32 +260,18 @@ namespace http {
size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels();
clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels();
size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels();
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
}
void ShowJumpServices (std::stringstream& s, const std::string& address)
{
s << "<form type=\"GET\" action=\"/\">";
s << "<input type=\"hidden\" name=\"page\" value=\"jumpservices\">";
s << "<input type=\"text\" name=\"address\" value=\"" << address << "\">";
s << "<input type=\"submit\" value=\"Update\">";
s << "</form><br>\r\n";
s << "<b>Jump services for " << address << "</b>\r\n<ul>\r\n";
for (auto & js : jumpservices) {
s << " <li><a href=\"" << js.second << address << "\">" << js.first << "</a></li>\r\n";
}
s << "</ul>\r\n";
}
void ShowLocalDestinations (std::stringstream& s)
{
s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
for (auto& it: i2p::client::context.GetDestinations ())
{
auto ident = it.second->GetIdentHash ();;
s << "<a href=/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << ">";
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
}
}
@@ -435,18 +301,18 @@ namespace http {
it->Print(s);
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
}
}
}
s << "<br>\r\n";
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
for (auto it: dest->GetSessions ())
for (const auto& it: dest->GetSessions ())
{
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
}
}
s << "<br>" << std::endl;
// s << "<br>\r\n<b>Streams:</b><br>\r\n";
// for (auto it: dest->GetStreamingDestination ()->GetStreams ())
// {
// {
// s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " ";
// s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
// s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
@@ -455,7 +321,7 @@ namespace http {
// s << "[Window:" << it.second->GetWindowSize () << "]";
// s << "[Status:" << (int)it.second->GetStatus () << "]";
// s << "<br>\r\n"<< std::endl;
// }
// }
s << "<br>\r\n<table><caption>Streams</caption><tr>";
s << "<th>StreamID</th>";
s << "<th>Destination</th>";
@@ -469,8 +335,8 @@ namespace http {
s << "<th>Status</th>";
s << "</tr>";
for (auto it: dest->GetAllStreams ())
{
for (const auto& it: dest->GetAllStreams ())
{
s << "<tr>";
s << "<td>" << it->GetSendStreamID () << "</td>";
s << "<td>" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "</td>";
@@ -483,8 +349,42 @@ namespace http {
s << "<td>" << it->GetWindowSize () << "</td>";
s << "<td>" << (int)it->GetStatus () << "</td>";
s << "</tr><br>\r\n" << std::endl;
}
s << "</table>";
}
}
void ShowLeasesSets(std::stringstream& s)
{
s << "<div id='leasesets'><b>LeaseSets (click on to show info):</b></div><br>\r\n";
int counter = 1;
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
// create copy of lease set so we extract leases
i2p::data::LeaseSet ls(leaseSet->GetBuffer(), leaseSet->GetBufferLen());
s << "<div class='leaseset";
if (ls.IsExpired())
s << " expired"; // additional css class for expired
s << "'>\r\n";
if (!ls.IsValid())
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
s << "<b>Expires:</b> " << ls.GetExpirationTime() << "<br>\r\n";
auto leases = ls.GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
{
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << l->endDate << "<br>\r\n";
}
s << "</p>\r\n</div>\r\n</div>\r\n";
}
}
);
// end for each lease set
}
void ShowTunnels (std::stringstream& s)
@@ -503,41 +403,41 @@ namespace http {
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
}
s << "<br>\r\n";
}
}
void ShowCommands (std::stringstream& s)
{
/* commands */
s << "<b>Router Commands</b><br>\r\n";
s << " <a href=/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << ">Run peer test</a><br>\r\n";
//s << " <a href=/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << ">Reload config</a><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "\">Run peer test</a><br>\r\n";
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ())
s << " <a href=/?cmd=" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a><br>\r\n";
else
s << " <a href=/?cmd=" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << ">Start accepting tunnels</a><br>\r\n";
#ifndef WIN32
if (Daemon.gracefullShutdownInterval) {
s << " <a href=/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << ">Cancel gracefull shutdown (";
s << Daemon.gracefullShutdownInterval;
s << " seconds remains)</a><br>\r\n";
} else {
s << " <a href=/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << ">Start gracefull shutdown</a><br>\r\n";
}
s << " <a href=/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << ">Force shutdown</a><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "\">Decline transit tunnels</a><br>\r\n";
else
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "\">Accept transit tunnels</a><br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
if (Daemon.gracefullShutdownInterval)
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "\">Cancel gracefull shutdown</a><br>";
else
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Start gracefull shutdown</a><br>\r\n";
#endif
#ifdef WIN32_APP
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Gracefull shutdown</a><br>\r\n";
#endif
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n";
}
void ShowTransitTunnels (std::stringstream& s)
{
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ())
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " ";
s << it->GetTunnelID () << " &#8658; ";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " " << it->GetTunnelID ();
s << " &#8658; " << it->GetTunnelID ();
else
s << " " << it->GetTunnelID () << " ";
s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
}
}
@@ -547,50 +447,50 @@ namespace http {
s << "<b>Transports:</b><br>\r\n<br>\r\n";
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
if (ntcpServer)
{
{
s << "<b>NTCP</b><br>\r\n";
for (auto it: ntcpServer->GetNTCPSessions ())
for (const auto& it: ntcpServer->GetNTCPSessions ())
{
if (it.second && it.second->IsEstablished ())
{
// incoming connection doesn't have remote RI
if (it.second->IsOutgoing ()) s << " ";
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!it.second->IsOutgoing ()) s << " ";
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>\r\n" << std::endl;
}
}
}
}
auto ssuServer = i2p::transport::transports.GetSSUServer ();
if (ssuServer)
{
s << "<br>\r\n<b>SSU</b><br>\r\n";
for (auto it: ssuServer->GetSessions ())
for (const auto& it: ssuServer->GetSessions ())
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " ";
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << " ";
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
s << "<br>\r\n<b>SSU6</b><br>\r\n";
for (auto it: ssuServer->GetSessionsV6 ())
for (const auto& it: ssuServer->GetSessionsV6 ())
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " ";
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << " ";
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>\r\n" << std::endl;
}
}
}
void ShowSAMSessions (std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
@@ -601,10 +501,10 @@ namespace http {
s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
for (auto& it: sam->GetSessions ())
{
s << "<a href=/?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << ">";
s << "<a href=\"/?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << it.first << "</a><br>\r\n" << std::endl;
}
}
}
}
void ShowSAMSession (std::stringstream& s, const std::string& id)
{
@@ -620,11 +520,11 @@ namespace http {
return;
}
auto& ident = session->localDestination->GetIdentHash();
s << "<a href=/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << ">";
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n";
s << "<br>\r\n";
s << "<b>Streams:</b><br>\r\n";
for (auto it: session->ListSockets())
for (const auto& it: session->ListSockets())
{
switch (it->GetSocketType ())
{
@@ -635,8 +535,8 @@ namespace http {
}
s << " [" << it->GetSocket ().remote_endpoint() << "]";
s << "<br>\r\n";
}
}
}
}
void ShowI2PTunnels (std::stringstream& s)
{
@@ -644,22 +544,48 @@ namespace http {
for (auto& it: i2p::client::context.GetClientTunnels ())
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << ">";
s << it.second->GetName () << "</a> ";
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
}
s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<br>\r\n";
for (auto& it: i2p::client::context.GetServerTunnels ())
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << ">";
s << it.second->GetName () << "</a> ";
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8658; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << ":" << it.second->GetLocalPort ();
s << "</a><br>\r\n"<< std::endl;
}
}
}
auto& clientForwards = i2p::client::context.GetClientForwards ();
if (!clientForwards.empty ())
{
s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<br>\r\n";
for (auto& it: clientForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
}
auto& serverForwards = i2p::client::context.GetServerForwards ();
if (!serverForwards.empty ())
{
s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<br>\r\n";
for (auto& it: serverForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
}
}
HTTPConnection::HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0)
@@ -668,7 +594,7 @@ namespace http {
i2p::config::GetOption("http.auth", needAuth);
i2p::config::GetOption("http.user", user);
i2p::config::GetOption("http.pass", pass);
};
}
void HTTPConnection::Receive ()
{
@@ -749,20 +675,24 @@ namespace http {
if (needAuth && !CheckAuth(req)) {
res.code = 401;
res.headers.insert(std::pair<std::string, std::string>("WWW-Authenticate", "Basic realm=\"WebAdmin\""));
res.add_header("WWW-Authenticate", "Basic realm=\"WebAdmin\"");
SendReply(res, content);
return;
}
// Html5 head start
ShowPageHead (s);
if (req.uri.find("page=") != std::string::npos)
if (req.uri.find("page=") != std::string::npos) {
HandlePage (req, res, s);
else if (req.uri.find("cmd=") != std::string::npos)
} else if (req.uri.find("cmd=") != std::string::npos) {
HandleCommand (req, res, s);
else
} else {
ShowStatus (s);
res.add_header("Refresh", "10");
}
ShowPageTail (s);
res.code = 200;
content = s.str ();
SendReply (res, content);
}
@@ -783,8 +713,6 @@ namespace http {
ShowTunnels (s);
else if (page == HTTP_PAGE_COMMANDS)
ShowCommands (s);
else if (page == HTTP_PAGE_JUMPSERVICES)
ShowJumpServices (s, params["address"]);
else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
ShowTransitTunnels (s);
else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
@@ -797,6 +725,8 @@ namespace http {
ShowSAMSession (s, params["sam_id"]);
else if (page == HTTP_PAGE_I2P_TUNNELS)
ShowI2PTunnels (s);
else if (page == HTTP_PAGE_LEASESETS)
ShowLeasesSets(s);
else {
res.code = 400;
ShowError(s, "Unknown page: " + page);
@@ -818,18 +748,21 @@ namespace http {
i2p::transport::transports.PeerTest ();
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
i2p::client::context.ReloadConfig ();
else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS)
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (true);
else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS)
else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (false);
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
i2p::context.SetAcceptsTunnels (false);
#ifndef WIN32
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
Daemon.gracefullShutdownInterval = 10*60;
#endif
#ifdef WIN32_APP
i2p::win32::GracefulShutdown ();
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
i2p::context.SetAcceptsTunnels (true);
#ifndef WIN32
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
Daemon.gracefullShutdownInterval = 0;
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
@@ -840,31 +773,23 @@ namespace http {
return;
}
s << "<b>SUCCESS</b>:&nbsp;Command accepted<br><br>\r\n";
s << "<a href=\"/?page=commands\">Back to commands list</a>";
}
s << "<a href=\"/?page=commands\">Back to commands list</a><br>\r\n";
s << "<p>You will be redirected in 5 seconds</b>";
res.add_header("Refresh", "5; url=/?page=commands");
}
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
{
std::time_t time_now = std::time(nullptr);
char time_buff[128];
std::strftime(time_buff, sizeof(time_buff), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&time_now));
reply.status = HTTPCodeToStatus(reply.code);
reply.headers.insert(std::pair<std::string, std::string>("Date", time_buff));
reply.headers.insert(std::pair<std::string, std::string>("Content-Type", "text/html"));
reply.headers.insert(std::pair<std::string, std::string>("Content-Length", std::to_string(content.size())));
reply.add_header("Content-Type", "text/html");
reply.body = content;
std::string res = reply.to_string();
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(res));
buffers.push_back(boost::asio::buffer(content));
boost::asio::async_write (*m_Socket, buffers,
m_SendBuffer = reply.to_string();
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
}
HTTPServer::HTTPServer (const std::string& address, int port):
m_Thread (nullptr), m_Work (m_Service),
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port))
{
}
@@ -881,16 +806,19 @@ namespace http {
std::string pass; i2p::config::GetOption("http.pass", pass);
/* generate pass if needed */
if (needAuth && pass == "") {
uint8_t random[16];
char alnum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
pass.resize(16);
for (size_t i = 0; i < pass.size(); i++) {
pass[i] = alnum[rand() % (sizeof(alnum) - 1)];
pass.resize(sizeof(random));
RAND_bytes(random, sizeof(random));
for (size_t i = 0; i < sizeof(random); i++) {
pass[i] = alnum[random[i] % (sizeof(alnum) - 1)];
}
i2p::config::SetOption("http.pass", pass);
LogPrint(eLogInfo, "HTTPServer: password set to ", pass);
}
m_IsRunning = true;
m_Thread = std::unique_ptr<std::thread>(new std::thread (std::bind (&HTTPServer::Run, this)));
m_Acceptor.listen ();
Accept ();
@@ -898,9 +826,11 @@ namespace http {
void HTTPServer::Stop ()
{
m_IsRunning = false;
m_Acceptor.close();
m_Service.stop ();
if (m_Thread) {
if (m_Thread)
{
m_Thread->join ();
m_Thread = nullptr;
}
@@ -908,7 +838,17 @@ namespace http {
void HTTPServer::Run ()
{
m_Service.run ();
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ());
}
}
}
void HTTPServer::Accept ()
@@ -922,7 +862,13 @@ namespace http {
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
{
if (ecode)
{
if(newSocket) newSocket->close();
LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message());
if(ecode != boost::asio::error::operation_aborted)
Accept();
return;
}
CreateConnection(newSocket);
Accept ();
}

View File

@@ -3,7 +3,6 @@
namespace i2p {
namespace http {
extern const char *itoopieImage;
extern const char *itoopieFavicon;
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
@@ -32,6 +31,7 @@ namespace http {
boost::asio::deadline_timer m_Timer;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen;
std::string m_SendBuffer;
bool needAuth;
std::string user;
std::string pass;
@@ -57,6 +57,7 @@ namespace http {
private:
bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;

725
I2CP.cpp
View File

@@ -1,23 +1,173 @@
/*
* 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 <string.h>
#include <stdlib.h>
#include <openssl/rand.h>
#include "I2PEndian.h"
#include "Log.h"
#include "Timestamp.h"
#include "LeaseSet.h"
#include "ClientContext.h"
#include "Transports.h"
#include "Signature.h"
#include "I2CP.h"
namespace i2p
{
namespace client
{
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Owner (owner), m_Socket (socket),
m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0)
I2CPDestination::I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
LeaseSetDestination (isPublic, &params), m_Owner (owner), m_Identity (identity)
{
}
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
{
memcpy (m_EncryptionPrivateKey, key, 256);
}
void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = bufbe32toh (buf);
if (length > len - 4) length = len - 4;
m_Owner->SendMessagePayloadMessage (buf + 4, length);
}
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
{
i2p::data::LocalLeaseSet ls (m_Identity, m_EncryptionPrivateKey, tunnels); // we don't care about encryption key
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
uint8_t * leases = ls.GetLeases ();
leases[-1] = tunnels.size ();
htobe16buf (leases - 3, m_Owner->GetSessionID ());
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size ();
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
}
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
{
auto ls = new i2p::data::LocalLeaseSet (m_Identity, buf, len);
ls->SetExpirationTime (m_LeaseSetExpirationTime);
SetLeaseSet (ls);
}
void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce)
{
auto msg = NewI2NPMessage ();
uint8_t * buf = msg->GetPayload ();
htobe32buf (buf, len);
memcpy (buf + 4, payload, len);
msg->len += len + 4;
msg->FillI2NPMessageHeader (eI2NPData);
auto s = GetSharedFromThis ();
auto remote = FindLeaseSet (ident);
if (remote)
{
GetService ().post (
[s, msg, remote, nonce]()
{
bool sent = s->SendMsg (msg, remote);
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
});
}
else
{
RequestDestination (ident,
[s, msg, nonce](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
{
bool sent = s->SendMsg (msg, ls);
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
}
else
s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet);
});
}
}
bool I2CPDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto remoteSession = GetRoutingSession (remote, true);
if (!remoteSession)
{
LogPrint (eLogError, "I2CP: Failed to create remote session");
return false;
}
auto path = remoteSession->GetSharedRoutingPath ();
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease;
if (path)
{
if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags
{
outboundTunnel = path->outboundTunnel;
remoteLease = path->remoteLease;
}
else
remoteSession->SetSharedRoutingPath (nullptr);
}
else
{
outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty ())
remoteLease = leases[rand () % leases.size ()];
if (remoteLease && outboundTunnel)
remoteSession->SetSharedRoutingPath (std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0, 0})); // 10 secs RTT
else
remoteSession->SetSharedRoutingPath (nullptr);
}
if (remoteLease && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto garlic = remoteSession->WrapSingleMessage (msg);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
remoteLease->tunnelGateway, remoteLease->tunnelID,
garlic
});
outboundTunnel->SendTunnelDataMsg (msgs);
return true;
}
else
{
if (outboundTunnel)
LogPrint (eLogWarning, "I2CP: Failed to send message. All leases expired");
else
LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels");
return false;
}
}
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket):
m_Owner (owner), m_Socket (socket), m_Payload (nullptr),
m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true)
{
}
I2CPSession::~I2CPSession ()
{
delete[] m_Payload;
}
void I2CPSession::Start ()
{
ReadProtocolByte ();
}
I2CPSession::~I2CPSession ()
void I2CPSession::Stop ()
{
delete[] m_NextMessage;
Terminate ();
}
void I2CPSession::ReadProtocolByte ()
@@ -25,88 +175,559 @@ namespace client
if (m_Socket)
{
auto s = shared_from_this ();
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, 1),
m_Socket->async_read_some (boost::asio::buffer (m_Header, 1),
[s](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (!ecode && bytes_transferred > 0 && s->m_Buffer[0] == I2CP_PRTOCOL_BYTE)
s->Receive ();
if (!ecode && bytes_transferred > 0 && s->m_Header[0] == I2CP_PROTOCOL_BYTE)
s->ReceiveHeader ();
else
s->Terminate ();
});
}
}
void I2CPSession::Receive ()
void I2CPSession::ReceiveHeader ()
{
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, I2CP_SESSION_BUFFER_SIZE),
std::bind (&I2CPSession::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Header, I2CP_HEADER_SIZE),
boost::asio::transfer_all (),
std::bind (&I2CPSession::HandleReceivedHeader, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
void I2CPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
void I2CPSession::HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
Terminate ();
else
{
size_t offset = 0;
if (m_NextMessage)
m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET);
if (m_PayloadLen > 0)
{
if (m_NextMessageOffset + bytes_transferred <= m_NextMessageLen)
{
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, bytes_transferred);
m_NextMessageOffset += bytes_transferred;
}
else
{
offset = m_NextMessageLen - m_NextMessageOffset;
memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, offset);
HandleNextMessage (m_NextMessage);
delete[] m_NextMessage;
}
}
while (offset < bytes_transferred)
m_Payload = new uint8_t[m_PayloadLen];
ReceivePayload ();
}
else // no following payload
{
auto msgLen = bufbe32toh (m_Buffer + offset + I2CP_HEADER_LENGTH_OFFSET) + I2CP_HEADER_SIZE;
if (msgLen <= bytes_transferred - offset)
{
HandleNextMessage (m_Buffer + offset);
offset += msgLen;
}
else
{
m_NextMessageLen = msgLen;
m_NextMessageOffset = bytes_transferred - offset;
m_NextMessage = new uint8_t[m_NextMessageLen];
memcpy (m_NextMessage, m_Buffer + offset, m_NextMessageOffset);
offset = bytes_transferred;
}
}
Receive ();
HandleMessage ();
ReceiveHeader (); // next message
}
}
}
void I2CPSession::HandleNextMessage (const uint8_t * buf)
void I2CPSession::ReceivePayload ()
{
auto handler = m_Owner.GetMessagesHandlers ()[buf[I2CP_HEADER_TYPE_OFFSET]];
if (handler)
(this->*handler)(buf + I2CP_HEADER_SIZE, bufbe32toh (buf + I2CP_HEADER_LENGTH_OFFSET));
boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Payload, m_PayloadLen),
boost::asio::transfer_all (),
std::bind (&I2CPSession::HandleReceivedPayload, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
void I2CPSession::HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
Terminate ();
else
LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)buf[I2CP_HEADER_TYPE_OFFSET]);
{
HandleMessage ();
delete[] m_Payload;
m_Payload = nullptr;
m_PayloadLen = 0;
ReceiveHeader (); // next message
}
}
void I2CPSession::HandleMessage ()
{
auto handler = m_Owner.GetMessagesHandlers ()[m_Header[I2CP_HEADER_TYPE_OFFSET]];
if (handler)
(this->*handler)(m_Payload, m_PayloadLen);
else
LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]);
}
void I2CPSession::Terminate ()
{
if (m_Destination)
{
m_Destination->Stop ();
m_Destination = nullptr;
}
if (m_Socket)
{
m_Socket->close ();
m_Socket = nullptr;
}
m_Owner.RemoveSession (GetSessionID ());
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " terminated");
}
void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len)
{
auto socket = m_Socket;
if (socket)
{
auto l = len + I2CP_HEADER_SIZE;
uint8_t * buf = new uint8_t[l];
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len);
buf[I2CP_HEADER_TYPE_OFFSET] = type;
memcpy (buf + I2CP_HEADER_SIZE, payload, len);
boost::asio::async_write (*socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, buf));
}
else
LogPrint (eLogError, "I2CP: Can't write to the socket");
}
void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf)
{
delete[] buf;
if (ecode && ecode != boost::asio::error::operation_aborted)
Terminate ();
}
std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len)
{
uint8_t l = buf[0];
if (l > len) l = len;
return std::string ((const char *)(buf + 1), l);
}
size_t I2CPSession::PutString (uint8_t * buf, size_t len, const std::string& str)
{
auto l = str.length ();
if (l + 1 >= len) l = len - 1;
if (l > 255) l = 255; // 1 byte max
buf[0] = l;
memcpy (buf + 1, str.c_str (), l);
return l + 1;
}
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping)
// TODO: move to Base.cpp
{
size_t offset = 0;
while (offset < len)
{
std::string param = ExtractString (buf + offset, len - offset);
offset += param.length () + 1;
if (buf[offset] != '=')
{
LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead '=' after ", param);
break;
}
offset++;
std::string value = ExtractString (buf + offset, len - offset);
offset += value.length () + 1;
if (buf[offset] != ';')
{
LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead ';' after ", value);
break;
}
offset++;
mapping.insert (std::make_pair (param, value));
}
}
void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len)
{
// get version
auto version = ExtractString (buf, len);
auto l = version.length () + 1 + 8;
uint8_t * payload = new uint8_t[l];
// set date
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
htobe64buf (payload, ts);
// echo vesrion back
PutString (payload + 8, l - 8, version);
SendI2CPMessage (I2CP_SET_DATE_MESSAGE, payload, l);
delete[] payload;
}
I2CPServer::I2CPServer (const std::string& interface, int port)
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
{
RAND_bytes ((uint8_t *)&m_SessionID, 2);
auto identity = std::make_shared<i2p::data::IdentityEx>();
size_t offset = identity->FromBuffer (buf, len);
if (!offset)
{
LogPrint (eLogError, "I2CP: create session maformed identity");
SendSessionStatusMessage (3); // invalid
return;
}
uint16_t optionsSize = bufbe16toh (buf + offset);
offset += 2;
if (optionsSize > len - offset)
{
LogPrint (eLogError, "I2CP: options size ", optionsSize, "exceeds message size");
SendSessionStatusMessage (3); // invalid
return;
}
std::map<std::string, std::string> params;
ExtractMapping (buf + offset, optionsSize, params);
offset += optionsSize; // options
if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false;
offset += 8; // date
if (identity->Verify (buf, offset, buf + offset)) // signature
{
bool isPublic = true;
if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false;
if (!m_Destination)
{
m_Destination = std::make_shared<I2CPDestination>(shared_from_this (), identity, isPublic, params);
SendSessionStatusMessage (1); // created
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created");
m_Destination->Start ();
}
else
{
LogPrint (eLogError, "I2CP: session already exists");
SendSessionStatusMessage (4); // refused
}
}
else
{
LogPrint (eLogError, "I2CP: create session signature verification falied");
SendSessionStatusMessage (3); // invalid
}
}
void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len)
{
SendSessionStatusMessage (0); // destroy
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed");
if (m_Destination)
{
m_Destination->Stop ();
m_Destination = 0;
}
}
void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len)
{
// TODO: implement actual reconfiguration
SendSessionStatusMessage (2); // updated
}
void I2CPSession::SendSessionStatusMessage (uint8_t status)
{
uint8_t buf[3];
htobe16buf (buf, m_SessionID);
buf[2] = status;
SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3);
}
void I2CPSession::SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status)
{
if (!nonce) return; // don't send status with zero nonce
uint8_t buf[15];
htobe16buf (buf, m_SessionID);
htobe32buf (buf + 2, m_MessageID++);
buf[6] = (uint8_t)status;
memset (buf + 7, 0, 4); // size
htobe32buf (buf + 11, nonce);
SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15);
}
void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len)
{
uint16_t sessionID = bufbe16toh (buf);
if (sessionID == m_SessionID)
{
size_t offset = 2;
if (m_Destination)
{
offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key
// we always assume this field as 20 bytes (DSA) regardless actual size
// instead of
//offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen ();
m_Destination->SetEncryptionPrivateKey (buf + offset);
offset += 256;
m_Destination->LeaseSetCreated (buf + offset, len - offset);
}
}
else
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
}
void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len)
{
uint16_t sessionID = bufbe16toh (buf);
if (sessionID == m_SessionID)
{
size_t offset = 2;
if (m_Destination)
{
i2p::data::IdentityEx identity;
size_t identsize = identity.FromBuffer (buf + offset, len - offset);
if (identsize)
{
offset += identsize;
uint32_t payloadLen = bufbe32toh (buf + offset);
if (payloadLen + offset <= len)
{
offset += 4;
uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
if (m_IsSendAccepted)
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce);
}
else
LogPrint(eLogError, "I2CP: cannot send message, too big");
}
else
LogPrint(eLogError, "I2CP: invalid identity");
}
}
else
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
}
void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len)
{
SendMessageMessageHandler (buf, len - 8); // ignore flags(2) and expiration(6)
}
void I2CPSession::HostLookupMessageHandler (const uint8_t * buf, size_t len)
{
uint16_t sessionID = bufbe16toh (buf);
if (sessionID == m_SessionID || sessionID == 0xFFFF) // -1 means without session
{
uint32_t requestID = bufbe32toh (buf + 2);
//uint32_t timeout = bufbe32toh (buf + 6);
i2p::data::IdentHash ident;
switch (buf[10])
{
case 0: // hash
ident = i2p::data::IdentHash (buf + 11);
break;
case 1: // address
{
auto name = ExtractString (buf + 11, len - 11);
if (!i2p::client::context.GetAddressBook ().GetIdentHash (name, ident))
{
LogPrint (eLogError, "I2CP: address ", name, " not found");
SendHostReplyMessage (requestID, nullptr);
return;
}
break;
}
default:
LogPrint (eLogError, "I2CP: request type ", (int)buf[10], " is not supported");
SendHostReplyMessage (requestID, nullptr);
return;
}
std::shared_ptr<LeaseSetDestination> destination = m_Destination;
if(!destination) destination = i2p::client::context.GetSharedLocalDestination ();
if (destination)
{
auto ls = destination->FindLeaseSet (ident);
if (ls)
SendHostReplyMessage (requestID, ls->GetIdentity ());
else
{
auto s = shared_from_this ();
destination->RequestDestination (ident,
[s, requestID](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr);
});
}
}
else
SendHostReplyMessage (requestID, nullptr);
}
else
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
}
void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity)
{
if (identity)
{
size_t l = identity->GetFullLen () + 7;
uint8_t * buf = new uint8_t[l];
htobe16buf (buf, m_SessionID);
htobe32buf (buf + 2, requestID);
buf[6] = 0; // result code
identity->ToBuffer (buf + 7, l - 7);
SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, l);
delete[] buf;
}
else
{
uint8_t buf[7];
htobe16buf (buf, m_SessionID);
htobe32buf (buf + 2, requestID);
buf[6] = 1; // result code
SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, 7);
}
}
void I2CPSession::DestLookupMessageHandler (const uint8_t * buf, size_t len)
{
if (m_Destination)
{
auto ls = m_Destination->FindLeaseSet (buf);
if (ls)
{
auto l = ls->GetIdentity ()->GetFullLen ();
uint8_t * identBuf = new uint8_t[l];
ls->GetIdentity ()->ToBuffer (identBuf, l);
SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
delete[] identBuf;
}
else
{
auto s = shared_from_this ();
i2p::data::IdentHash ident (buf);
m_Destination->RequestDestination (ident,
[s, ident](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet) // found
{
auto l = leaseSet->GetIdentity ()->GetFullLen ();
uint8_t * identBuf = new uint8_t[l];
leaseSet->GetIdentity ()->ToBuffer (identBuf, l);
s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
delete[] identBuf;
}
else
s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, ident, 32); // not found
});
}
}
else
SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, buf, 32);
}
void I2CPSession::GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len)
{
uint8_t limits[64];
memset (limits, 0, 64);
htobe32buf (limits, i2p::transport::transports.GetInBandwidth ()); // inbound
htobe32buf (limits + 4, i2p::transport::transports.GetOutBandwidth ()); // outbound
SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64);
}
void I2CPSession::SendMessagePayloadMessage (const uint8_t * payload, size_t len)
{
// we don't use SendI2CPMessage to eliminate additional copy
auto l = len + 10 + I2CP_HEADER_SIZE;
uint8_t * buf = new uint8_t[l];
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10);
buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE;
htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID);
htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++);
htobe32buf (buf + I2CP_HEADER_SIZE + 6, len);
memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len);
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, buf));
}
I2CPServer::I2CPServer (const std::string& interface, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service,
#ifdef ANDROID
I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address
#else
I2CPSession::proto::endpoint(boost::asio::ip::address::from_string(interface), port))
#endif
{
memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler;
m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler;
m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler;
m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler;
m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler;
m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler;
m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler;
m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler;
m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler;
}
I2CPServer::~I2CPServer ()
{
if (m_IsRunning)
Stop ();
}
void I2CPServer::Start ()
{
Accept ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&I2CPServer::Run, this));
}
void I2CPServer::Stop ()
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto& it: m_Sessions)
it.second->Stop ();
m_Sessions.clear ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void I2CPServer::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2CP: runtime exception: ", ex.what ());
}
}
}
void I2CPServer::Accept ()
{
auto newSocket = std::make_shared<I2CPSession::proto::socket> (m_Service);
m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void I2CPServer::HandleAccept(const boost::system::error_code& ecode,
std::shared_ptr<I2CPSession::proto::socket> socket)
{
if (!ecode && socket)
{
boost::system::error_code ec;
auto ep = socket->remote_endpoint (ec);
if (!ec)
{
LogPrint (eLogDebug, "I2CP: new connection from ", ep);
auto session = std::make_shared<I2CPSession>(*this, socket);
m_Sessions[session->GetSessionID ()] = session;
session->Start ();
}
else
LogPrint (eLogError, "I2CP: incoming connection error ", ec.message ());
}
else
LogPrint (eLogError, "I2CP: accept error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Accept ();
}
void I2CPServer::RemoveSession (uint16_t sessionID)
{
m_Sessions.erase (sessionID);
}
}
}

154
I2CP.h
View File

@@ -1,16 +1,27 @@
/*
* 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 I2CP_H__
#define I2CP_H__
#include <inttypes.h>
#include <string>
#include <memory>
#include <thread>
#include <map>
#include <boost/asio.hpp>
#include "Destination.h"
namespace i2p
{
namespace client
{
const uint8_t I2CP_PRTOCOL_BYTE = 0x2A;
const uint8_t I2CP_PROTOCOL_BYTE = 0x2A;
const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
@@ -18,32 +29,136 @@ namespace client
const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1;
const uint8_t I2CP_GET_DATE_MESSAGE = 32;
const uint8_t I2CP_SET_DATE_MESSAGE = 33;
const uint8_t I2CP_CREATE_SESSION_MESSAGE = 1;
const uint8_t I2CP_RECONFIGURE_SESSION_MESSAGE = 2;
const uint8_t I2CP_SESSION_STATUS_MESSAGE = 20;
const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3;
const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37;
const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4;
const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5;
const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36;
const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31;
const uint8_t I2CP_MESSAGE_STATUS_MESSAGE = 22;
const uint8_t I2CP_HOST_LOOKUP_MESSAGE = 38;
const uint8_t I2CP_HOST_REPLY_MESSAGE = 39;
const uint8_t I2CP_DEST_LOOKUP_MESSAGE = 34;
const uint8_t I2CP_DEST_REPLY_MESSAGE = 35;
const uint8_t I2CP_GET_BANDWIDTH_LIMITS_MESSAGE = 8;
const uint8_t I2CP_BANDWIDTH_LIMITS_MESSAGE = 23;
enum I2CPMessageStatus
{
eI2CPMessageStatusAccepted = 1,
eI2CPMessageStatusGuaranteedSuccess = 4,
eI2CPMessageStatusGuaranteedFailure = 5,
eI2CPMessageStatusNoLeaseSet = 21
};
// params
const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability";
class I2CPSession;
class I2CPDestination: public LeaseSetDestination
{
public:
I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
void SetEncryptionPrivateKey (const uint8_t * key);
void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
// implements LocalDestination
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
protected:
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
private:
std::shared_ptr<I2CPDestination> GetSharedFromThis ()
{ return std::static_pointer_cast<I2CPDestination>(shared_from_this ()); }
bool SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
private:
std::shared_ptr<I2CPSession> m_Owner;
std::shared_ptr<const i2p::data::IdentityEx> m_Identity;
uint8_t m_EncryptionPrivateKey[256];
uint64_t m_LeaseSetExpirationTime;
};
class I2CPServer;
class I2CPSession: public std::enable_shared_from_this<I2CPSession>
{
public:
I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
#ifdef ANDROID
typedef boost::asio::local::stream_protocol proto;
#else
typedef boost::asio::ip::tcp proto;
#endif
I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket);
~I2CPSession ();
void Start ();
void Stop ();
uint16_t GetSessionID () const { return m_SessionID; };
// called from I2CPDestination
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
void SendMessagePayloadMessage (const uint8_t * payload, size_t len);
void SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status);
// message handlers
void GetDateMessageHandler (const uint8_t * buf, size_t len);
void CreateSessionMessageHandler (const uint8_t * buf, size_t len);
void DestroySessionMessageHandler (const uint8_t * buf, size_t len);
void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len);
void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len);
void SendMessageMessageHandler (const uint8_t * buf, size_t len);
void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len);
void HostLookupMessageHandler (const uint8_t * buf, size_t len);
void DestLookupMessageHandler (const uint8_t * buf, size_t len);
void GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len);
private:
void ReadProtocolByte ();
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleNextMessage (const uint8_t * buf);
void ReceiveHeader ();
void HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void ReceivePayload ();
void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleMessage ();
void Terminate ();
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf);
std::string ExtractString (const uint8_t * buf, size_t len);
size_t PutString (uint8_t * buf, size_t len, const std::string& str);
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
void SendSessionStatusMessage (uint8_t status);
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
private:
I2CPServer& m_Owner;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
uint8_t m_Buffer[I2CP_SESSION_BUFFER_SIZE], * m_NextMessage;
size_t m_NextMessageLen, m_NextMessageOffset;
std::shared_ptr<proto::socket> m_Socket;
uint8_t m_Header[I2CP_HEADER_SIZE], * m_Payload;
size_t m_PayloadLen;
std::shared_ptr<I2CPDestination> m_Destination;
uint16_t m_SessionID;
uint32_t m_MessageID;
bool m_IsSendAccepted;
};
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);
@@ -52,10 +167,31 @@ namespace client
public:
I2CPServer (const std::string& interface, int port);
~I2CPServer ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
void RemoveSession (uint16_t sessionID);
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<I2CPSession::proto::socket> socket);
private:
I2CPMessageHandler m_MessagesHandlers[256];
I2CPMessageHandler m_MessagesHandlers[256];
std::map<uint16_t, std::shared_ptr<I2CPSession> > m_Sessions;
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
I2CPSession::proto::acceptor m_Acceptor;
public:

View File

@@ -11,6 +11,7 @@
#include "Transports.h"
#include "Garlic.h"
#include "I2NPProtocol.h"
#include "version.h"
using namespace i2p::transport;
@@ -101,7 +102,7 @@ namespace i2p
{
RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ());
}
m->len += DELIVERY_STATUS_SIZE;
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
@@ -164,9 +165,10 @@ namespace i2p
buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 5;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
buf ++;
htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 4;
// excluded
htobe16buf (buf, cnt);
@@ -181,7 +183,7 @@ namespace i2p
}
// encryption
memcpy (buf, replyKey, 32);
buf[32] = 1; // 1 tag
buf[32] = uint8_t( 1 ); // 1 tag
memcpy (buf + 33, replyTag, 32);
buf += 65;
@@ -200,7 +202,7 @@ namespace i2p
len += 32;
buf[len] = routers.size ();
len++;
for (auto it: routers)
for (const auto& it: routers)
{
memcpy (buf + len, it, 32);
len += 32;
@@ -249,7 +251,23 @@ namespace i2p
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
size_t size = DATABASE_STORE_HEADER_SIZE;
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen ();
m->len += size;
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
@@ -258,14 +276,13 @@ namespace i2p
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
if (replyToken && replyTunnel)
{
auto leases = leaseSet->GetNonExpiredLeases ();
if (leases.size () > 0)
if (replyTunnel)
{
htobe32buf (payload + size, leases[0]->tunnelID);
htobe32buf (payload + size, replyTunnel->GetNextTunnelID ());
size += 4; // reply tunnelID
memcpy (payload + size, leases[0]->tunnelGateway, 32);
memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32);
size += 32; // reply tunnel gateway
}
else

View File

@@ -90,7 +90,7 @@ namespace i2p
// DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
@@ -224,7 +224,8 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);

View File

@@ -2,8 +2,6 @@
#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>
@@ -16,6 +14,7 @@
#include "Crypto.h"
#include "FS.h"
#include "Log.h"
#include "HTTP.h"
#include "Config.h"
#include "NetDb.h"
#include "RouterContext.h"
@@ -24,6 +23,7 @@
#include "Timestamp.h"
#include "Transports.h"
#include "version.h"
#include "util.h"
#include "I2PControl.h"
namespace i2p
@@ -189,71 +189,64 @@ namespace client
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");
}
}
/* try to parse received data */
std::stringstream json;
std::string response;
bool isHTTP = false;
if (memcmp (buf->data (), "POST", 4) == 0) {
long int remains = 0;
isHTTP = true;
i2p::http::HTTPReq req;
std::size_t len = req.parse(buf->data(), bytes_transferred);
if (len <= 0) {
LogPrint(eLogError, "I2PControl: incomplete/malformed POST request");
return;
}
/* append to json chunk of data from 1st request */
json.write(buf->data() + len, bytes_transferred - len);
remains = req.content_length() - len;
/* if request has Content-Length header, fetch rest of data and store to json buffer */
while (remains > 0) {
len = ((long int) buf->size() < remains) ? buf->size() : remains;
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len));
json.write(buf->data(), bytes_transferred);
remains -= bytes_transferred;
}
} else {
json.write(buf->data(), bytes_transferred);
}
LogPrint(eLogDebug, "I2PControl: json from request: ", json.str());
#if GCC47_BOOST149
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost");
#else
/* now try to parse json itself */
try {
boost::property_tree::ptree pt;
boost::property_tree::read_json (json, pt);
std::string id = pt.get<std::string>("id");
std::string method = pt.get<std::string>("method");
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ()) {
std::ostringstream ss;
ss << "{\"id\":" << id << ",\"result\":{";
(this->*(it->second))(pt.get_child ("params"), ss);
ss << "},\"jsonrpc\":\"2.0\"}";
response = ss.str();
} else {
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
BuildErrorResponse(response, 32601, "Method not found");
}
} catch (std::exception& ex) {
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
BuildErrorResponse(response, 32603, ex.what());
} catch (...) {
LogPrint (eLogError, "I2PControl: handle request unknown exception");
}
#endif
SendResponse (socket, buf, response, isHTTP);
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
@@ -275,27 +268,28 @@ namespace client
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
std::stringstream ss;
ss << "{\"id\":null,\"error\":";
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
ss << "\"jsonrpc\":\"2.0\"}";
content = ss.str();
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
{
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);
if (isHTTP) {
i2p::http::HTTPRes res;
res.code = 200;
res.add_header("Content-Type", "application/json");
res.add_header("Connection", "close");
res.body = content;
std::string tmp = res.to_string();
content = tmp;
}
memcpy (buf->data () + offset, response.str ().c_str (), len);
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
std::copy(content.begin(), content.end(), buf->begin());
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
boost::asio::transfer_all (),
std::bind(&I2PControlService::HandleResponseSent, this,
std::placeholders::_1, std::placeholders::_2, socket, buf));
@@ -322,7 +316,7 @@ namespace client
}
InsertParam (results, "API", api);
results << ",";
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
m_Tokens.insert (token);
InsertParam (results, "Token", token);
}
@@ -364,7 +358,7 @@ namespace client
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
for (auto it = params.begin (); it != params.end (); ++it)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
@@ -440,7 +434,7 @@ namespace client
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
for (auto it = params.begin (); it != params.end (); ++it)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
@@ -489,7 +483,7 @@ namespace client
// network setting
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
for (auto it = params.begin (); it != params.end (); ++it)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);

View File

@@ -45,8 +45,9 @@ namespace client
void ReadRequest (std::shared_ptr<ssl_socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void BuildErrorResponse (std::string & content, int code, const char *message);
void SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
std::shared_ptr<I2PControlBuffer> buf, std::string& 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);

View File

@@ -3,7 +3,6 @@
#include "ClientContext.h"
#include "I2PService.h"
namespace i2p
{
namespace client
@@ -28,12 +27,17 @@ namespace client
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "I2PService: Remote destination ", dest, " not found");
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
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(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)
{
boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
upstream->set_option(option);
downstream->set_option(option);
}
TCPIPPipe::~TCPIPPipe()
{
@@ -71,7 +75,7 @@ namespace client
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: no upstream socket for read");
LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
}
}
@@ -82,14 +86,14 @@ namespace client
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: no downstream socket for read");
LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
}
}
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
{
if (m_up) {
LogPrint(eLogDebug, "TCPIPPipe: write upstream ", (int)len);
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleUpstreamWrite,
@@ -97,14 +101,14 @@ namespace client
std::placeholders::_1)
);
} else {
LogPrint(eLogError, "tcpip pipe upstream socket null");
LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
}
}
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
{
if (m_down) {
LogPrint(eLogDebug, "TCPIPPipe: write downstream ", (int)len);
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleDownstreamWrite,
@@ -112,16 +116,16 @@ namespace client
std::placeholders::_1)
);
} else {
LogPrint(eLogError, "tcpip pipe downstream socket null");
LogPrint(eLogError, "TCPIPPipe: downstream write: no socket");
}
}
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe downstream got ", (int) bytes_transfered);
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Downstream read error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
@@ -135,7 +139,7 @@ namespace client
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Downstream write error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
@@ -143,7 +147,7 @@ namespace client
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Upstream write error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
@@ -151,9 +155,9 @@ namespace client
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe upstream got ", (int) bytes_transfered);
LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Upstream read error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
@@ -206,6 +210,5 @@ namespace client
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
}
}
}
}

View File

@@ -9,6 +9,17 @@ namespace i2p
{
namespace client
{
/** set standard socket options */
static void I2PTunnelSetSocketOptions(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (socket && socket->is_open())
{
boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
socket->set_option(option);
}
}
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port):
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
@@ -34,7 +45,7 @@ namespace client
I2PTunnelConnection::~I2PTunnelConnection ()
{
}
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
{
if (m_Stream)
@@ -50,9 +61,27 @@ namespace client
void I2PTunnelConnection::Connect ()
{
if (m_Socket)
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
I2PTunnelSetSocketOptions(m_Socket);
if (m_Socket) {
#ifdef __linux__
// bind to 127.x.x.x address
// where x.x.x are first three bytes from ident
if (m_RemoteEndpoint.address ().is_v4 () &&
m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127)
{
m_Socket->open (boost::asio::ip::tcp::v4 ());
boost::asio::ip::address_v4::bytes_type bytes;
const uint8_t * ident = m_Stream->GetRemoteIdentity ()->GetIdentHash ();
bytes[0] = 127;
memcpy (bytes.data ()+1, ident, 3);
boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes);
m_Socket->bind (boost::asio::ip::tcp::endpoint (ourIP, 0));
}
#endif
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
shared_from_this (), std::placeholders::_1));
}
}
void I2PTunnelConnection::Terminate ()
@@ -384,7 +413,7 @@ namespace client
{
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip);
}
void I2PServerTunnel::Start ()
{
m_Endpoint.port (m_Port);
@@ -499,5 +528,235 @@ namespace client
conn->Connect ();
}
}
}
void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto session = ObtainUDPSession(from, toPort, fromPort);
session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
session->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession * u) -> bool {
return now - u->LastActivity >= delta;
});
}
UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
{
auto ih = from.GetIdentHash();
for ( UDPSession * s : m_Sessions )
{
if ( s->Identity == ih)
{
/** found existing session */
LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
return s;
}
}
/** create new udp session */
boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0);
m_Sessions.push_back(new UDPSession(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort));
return m_Sessions.back();
}
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to,
uint16_t ourPort, uint16_t theirPort) :
m_Destination(localDestination->GetDatagramDestination()),
m_Service(localDestination->GetService()),
IPSocket(localDestination->GetService(), localEndpoint),
SendEndpoint(endpoint),
LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
LocalPort(ourPort),
RemotePort(theirPort)
{
memcpy(Identity, to->data(), 32);
Receive();
}
void UDPSession::Receive() {
LogPrint(eLogDebug, "UDPSession: Receive");
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
}
void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len)
{
if(!ecode)
{
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
LastActivity = i2p::util::GetMillisecondsSinceEpoch();
m_Destination->SendDatagramTo(m_Buffer, len, Identity, 0, 0);
Receive();
} else {
LogPrint(eLogError, "UDPSession: ", ecode.message());
}
}
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address& localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) :
m_Name(name),
LocalPort(port),
m_LocalAddress(localAddress),
m_RemoteEndpoint(forwardTo)
{
m_LocalDest = localDestination;
m_LocalDest->Start();
auto dgram = m_LocalDest->CreateDatagramDestination();
dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
}
I2PUDPServerTunnel::~I2PUDPServerTunnel()
{
auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) dgram->ResetReceiver();
LogPrint(eLogInfo, "UDPServer: done");
}
void I2PUDPServerTunnel::Start() {
m_LocalDest->Start();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions()
{
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( UDPSession * s : m_Sessions )
{
if (!s->m_Destination) continue;
auto info = s->m_Destination->GetInfoForRemote(s->Identity);
if(!info) continue;
auto sinfo = std::make_shared<DatagramSessionInfo>();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
sinfo->CurrentIBGW = info->IBGW;
sinfo->CurrentOBEP = info->OBEP;
sessions.push_back(sinfo);
}
return sessions;
}
I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort) :
m_Name(name),
m_Session(nullptr),
m_RemoteDest(remoteDest),
m_LocalDest(localDestination),
m_LocalEndpoint(localEndpoint),
m_RemoteIdent(nullptr),
m_ResolveThread(nullptr),
LocalPort(localEndpoint.port()),
RemotePort(remotePort),
m_cancel_resolve(false)
{
auto dgram = m_LocalDest->CreateDatagramDestination();
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5));
}
void I2PUDPClientTunnel::Start() {
m_LocalDest->Start();
if (m_ResolveThread == nullptr)
m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this));
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions()
{
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
if(m_Session && m_LocalDest)
{
auto s = m_Session;
if (s->m_Destination)
{
auto info = m_Session->m_Destination->GetInfoForRemote(s->Identity);
if(info)
{
auto sinfo = std::make_shared<DatagramSessionInfo>();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
sinfo->CurrentIBGW = info->IBGW;
sinfo->CurrentOBEP = info->OBEP;
infos.push_back(sinfo);
}
}
}
return infos;
}
void I2PUDPClientTunnel::TryResolving() {
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
m_RemoteIdent = new i2p::data::IdentHash;
m_RemoteIdent->Fill(0);
while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *m_RemoteIdent) && !m_cancel_resolve)
{
LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if(m_cancel_resolve)
{
LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled");
return;
}
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
// delete existing session
if(m_Session) delete m_Session;
boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 0);
m_Session = new UDPSession(m_LocalEndpoint, m_LocalDest, ep, m_RemoteIdent, LocalPort, RemotePort);
}
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
{
// address match
if(m_Session)
{
// tell session
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32());
m_Session->IPSocket.send_to(boost::asio::buffer(buf, len), m_Session->FromEndpoint);
}
else
LogPrint(eLogWarning, "UDP Client: no session");
}
else
LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32());
}
I2PUDPClientTunnel::~I2PUDPClientTunnel() {
auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) dgram->ResetReceiver();
if (m_Session) delete m_Session;
m_cancel_resolve = true;
if(m_ResolveThread)
{
m_ResolveThread->join();
delete m_ResolveThread;
m_ResolveThread = nullptr;
}
if (m_RemoteIdent) delete m_RemoteIdent;
}
}
}

View File

@@ -9,6 +9,7 @@
#include <boost/asio.hpp>
#include "Identity.h"
#include "Destination.h"
#include "Datagram.h"
#include "Streaming.h"
#include "I2PService.h"
@@ -128,8 +129,121 @@ namespace client
std::string m_Name, m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash;
int m_DestinationPort;
};
};
/** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
/** max size for i2p udp */
const size_t I2P_UDP_MAX_MTU = i2p::datagram::MAX_DATAGRAM_SIZE;
struct UDPSession
{
i2p::datagram::DatagramDestination * m_Destination;
boost::asio::io_service & m_Service;
boost::asio::ip::udp::socket IPSocket;
i2p::data::IdentHash Identity;
boost::asio::ip::udp::endpoint FromEndpoint;
boost::asio::ip::udp::endpoint SendEndpoint;
uint64_t LastActivity;
uint16_t LocalPort;
uint16_t RemotePort;
uint8_t m_Buffer[I2P_UDP_MAX_MTU];
UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident,
uint16_t ourPort, uint16_t theirPort);
void HandleReceived(const boost::system::error_code & ecode, std::size_t len);
void Receive();
};
/** read only info about a datagram session */
struct DatagramSessionInfo
{
/** the name of this forward */
std::string Name;
/** ident hash of local destination */
std::shared_ptr<const i2p::data::IdentHash> LocalIdent;
/** ident hash of remote destination */
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
/** ident hash of OBEP in use for this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentOBEP;
/** i2p router's udp endpoint */
boost::asio::ip::udp::endpoint LocalEndpoint;
/** client's udp endpoint */
boost::asio::ip::udp::endpoint RemoteEndpoint;
/** how long has this converstation been idle in ms */
uint64_t idle;
};
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
class I2PUDPServerTunnel
{
public:
I2PUDPServerTunnel(const std::string & name,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address & localAddress,
boost::asio::ip::udp::endpoint forwardTo, uint16_t port);
~I2PUDPServerTunnel();
/** expire stale udp conversations */
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
void Start();
const char * GetName() const { return m_Name.c_str(); }
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
private:
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
UDPSession * ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
private:
const std::string m_Name;
const uint16_t LocalPort;
boost::asio::ip::address m_LocalAddress;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
std::mutex m_SessionsMutex;
std::vector<UDPSession*> m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
};
class I2PUDPClientTunnel
{
public:
I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort);
~I2PUDPClientTunnel();
void Start();
const char * GetName() const { return m_Name.c_str(); }
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
private:
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void TryResolving();
const std::string m_Name;
UDPSession * m_Session;
const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
i2p::data::IdentHash * m_RemoteIdent;
std::thread * m_ResolveThread;
uint16_t LocalPort;
uint16_t RemotePort;
bool m_cancel_resolve;
};
class I2PServerTunnel: public I2PService
{
public:
@@ -148,8 +262,10 @@ namespace client
const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; }
const char* GetName() { return m_Name.c_str (); }
private:
void SetMaxConnsPerMinute(const uint32_t conns) { m_PortDestination->SetMaxConnsPerMinute(conns); }
private:
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
@@ -165,7 +281,7 @@ namespace client
boost::asio::ip::tcp::endpoint m_Endpoint;
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
std::set<i2p::data::IdentHash> m_AccessList;
bool m_IsAccessList;
bool m_IsAccessList;
};
class I2PServerTunnelHTTP: public I2PServerTunnel

View File

@@ -35,11 +35,12 @@ namespace data
}
IdentityEx::IdentityEx ():
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
}
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type)
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type):
m_IsVerifierCreated (false)
{
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
@@ -135,19 +136,19 @@ namespace data
}
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
FromBuffer (buf, len);
}
IdentityEx::IdentityEx (const IdentityEx& other):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = other;
}
IdentityEx::IdentityEx (const Identity& standard):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = standard;
}
@@ -173,6 +174,7 @@ namespace data
m_ExtendedBuffer = nullptr;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -187,6 +189,7 @@ namespace data
m_ExtendedLen = 0;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -200,7 +203,9 @@ namespace data
}
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
delete[] m_ExtendedBuffer; m_ExtendedBuffer = nullptr;
if(m_ExtendedBuffer) delete[] m_ExtendedBuffer;
m_ExtendedBuffer = nullptr;
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
if (m_ExtendedLen)
{
@@ -304,6 +309,7 @@ namespace data
void IdentityEx::CreateVerifier () const
{
if (m_Verifier) return; // don't create again
auto keyType = GetSigningKeyType ();
switch (keyType)
{
@@ -371,8 +377,17 @@ namespace data
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
{
if (!m_Verifier || !verifier)
m_Verifier.reset (verifier);
if (!m_Verifier)
{
auto created = m_IsVerifierCreated.exchange (true);
if (!created)
m_Verifier.reset (verifier);
else
{
delete verifier;
while (!m_Verifier) ; // spin lock
}
}
else
delete verifier;
}
@@ -380,7 +395,8 @@ namespace data
void IdentityEx::DropVerifier () const
{
// TODO: potential race condition with Verify
m_Verifier = nullptr;
m_IsVerifierCreated = false;
m_Verifier = nullptr;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
@@ -410,6 +426,7 @@ namespace data
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(signingPrivateKeySize + ret > len) return 0; // overflow
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
ret += signingPrivateKeySize;
m_Signer = nullptr;
@@ -422,7 +439,8 @@ namespace data
size_t ret = m_Public->ToBuffer (buf, len);
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(ret + signingPrivateKeySize > len) return 0; // overflow
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize;
return ret;
@@ -452,12 +470,14 @@ namespace data
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
if (m_Signer)
m_Signer->Sign (buf, len, signature);
if (!m_Signer)
CreateSigner();
m_Signer->Sign (buf, len, signature);
}
void PrivateKeys::CreateSigner ()
void PrivateKeys::CreateSigner () const
{
if (m_Signer) return;
switch (m_Public->GetSigningKeyType ())
{
case SIGNING_KEY_TYPE_DSA_SHA1:

View File

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

View File

@@ -4,7 +4,7 @@
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "TunnelPool.h"
#include "Tunnel.h"
#include "LeaseSet.h"
namespace i2p
@@ -21,56 +21,6 @@ namespace data
ReadFromBuffer ();
}
LeaseSet::LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool):
m_IsValid (true), m_StoreLeases (true), m_ExpirationTime (0)
{
if (!pool) return;
// header
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 ();
memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
m_BufferLen += signingKeyLen;
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)
{
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
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 (eLogDebug, "LeaseSet: Local LeaseSet of ", tunnels.size (), " leases created");
ReadFromBuffer ();
}
void LeaseSet::Update (const uint8_t * buf, size_t len)
{
if (len > m_BufferLen)
@@ -114,10 +64,10 @@ namespace data
return;
}
// reset existing leases
// reset existing leases
if (m_StoreLeases)
for (auto it: m_Leases)
it->isUpdated = false;
for (auto& it: m_Leases)
it->isUpdated = false;
else
m_Leases.clear ();
@@ -173,7 +123,7 @@ namespace data
m_Leases.erase (it++);
}
else
it++;
++it;
}
}
@@ -195,7 +145,7 @@ namespace data
if (size > len) return 0;
uint8_t num = buf[size];
size++; // num
if (size + num*44 > len) return 0;
if (size + num*LEASE_SIZE > len) return 0;
uint64_t timestamp= 0 ;
for (int i = 0; i < num; i++)
{
@@ -212,19 +162,32 @@ namespace data
{
return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen);
}
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
bool LeaseSet::ExpiresSoon(const uint64_t dlt, const uint64_t fudge) const
{
auto now = i2p::util::GetMillisecondsSinceEpoch ();
if (fudge) now += rand() % fudge;
if (now >= m_ExpirationTime) return true;
return m_ExpirationTime - now <= dlt;
}
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
{
return GetNonExpiredLeasesExcluding( [] (const Lease & l) -> bool { return false; }, withThreshold);
}
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold) const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<std::shared_ptr<const Lease> > leases;
for (auto it: m_Leases)
for (const auto& it: m_Leases)
{
auto endDate = it->endDate;
if (withThreshold)
endDate += LEASE_ENDDATE_THRESHOLD;
else
endDate -= LEASE_ENDDATE_THRESHOLD;
if (ts < endDate)
if (ts < endDate && !exclude(*it))
leases.push_back (it);
}
return leases;
@@ -233,14 +196,65 @@ namespace data
bool LeaseSet::HasExpiredLeases () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_Leases)
for (const auto& it: m_Leases)
if (ts >= it->endDate) return true;
return false;
}
bool LeaseSet::IsExpired () const
{
if (IsEmpty ()) return true;
if (m_StoreLeases && IsEmpty ()) return true;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
return ts > m_ExpirationTime;
}
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
m_ExpirationTime (0), m_Identity (identity)
{
int num = tunnels.size ();
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
// identity
auto signingKeyLen = m_Identity->GetSigningPublicKeyLen ();
m_BufferLen = m_Identity->GetFullLen () + 256 + signingKeyLen + 1 + num*LEASE_SIZE + m_Identity->GetSignatureLen ();
m_Buffer = new uint8_t[m_BufferLen];
auto offset = m_Identity->ToBuffer (m_Buffer, m_BufferLen);
memcpy (m_Buffer + offset, encryptionPublicKey, 256);
offset += 256;
memset (m_Buffer + offset, 0, signingKeyLen);
offset += signingKeyLen;
// num leases
m_Buffer[offset] = num;
offset++;
// leases
m_Leases = m_Buffer + offset;
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
for (int i = 0; i < num; i++)
{
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
offset += 32; // gateway id
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
offset += 4; // tunnel id
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + offset, ts);
offset += 8; // end date
}
// we don't sign it yet. must be signed later on
}
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
m_ExpirationTime (0), m_Identity (identity)
{
m_BufferLen = len;
m_Buffer = new uint8_t[m_BufferLen];
memcpy (m_Buffer, buf, len);
}
bool LocalLeaseSet::IsExpired () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
return ts > m_ExpirationTime;
}

View File

@@ -4,15 +4,17 @@
#include <inttypes.h>
#include <string.h>
#include <vector>
#include <set>
#include <memory>
#include "Identity.h"
#include "Timestamp.h"
namespace i2p
{
namespace tunnel
{
class TunnelPool;
class InboundTunnel;
}
namespace data
@@ -23,7 +25,13 @@ namespace data
IdentHash tunnelGateway;
uint32_t tunnelID;
uint64_t endDate; // 0 means invalid
bool isUpdated; // trasient
bool isUpdated; // trasient
/* return true if this lease expires within t millisecond + fudge factor */
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const {
auto expire = i2p::util::GetMillisecondsSinceEpoch ();
if(fudge) expire += rand() % fudge;
return endDate - expire >= t;
}
};
struct LeaseCmp
@@ -37,14 +45,16 @@ namespace data
};
};
const int MAX_LS_BUFFER_SIZE = 3072;
typedef std::function<bool(const Lease & l)> LeaseInspectFunc;
const size_t MAX_LS_BUFFER_SIZE = 3072;
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
const uint8_t MAX_NUM_LEASES = 16;
class LeaseSet: public RoutingDestination
{
public:
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;
@@ -55,10 +65,12 @@ namespace data
size_t GetBufferLen () const { return m_BufferLen; };
bool IsValid () const { return m_IsValid; };
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases (bool withThreshold = true) const;
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const;
bool HasExpiredLeases () const;
bool IsExpired () const;
bool IsEmpty () const { return m_Leases.empty (); };
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
bool ExpiresSoon(const uint64_t dlt=1000 * 5, const uint64_t fudge = 0) const ;
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
@@ -82,6 +94,36 @@ namespace data
uint8_t * m_Buffer;
size_t m_BufferLen;
};
class LocalLeaseSet
{
public:
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len);
~LocalLeaseSet () { delete[] m_Buffer; };
const uint8_t * GetBuffer () const { return m_Buffer; };
uint8_t * GetSignature () { return m_Buffer + m_BufferLen - GetSignatureLen (); };
size_t GetBufferLen () const { return m_BufferLen; };
size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); };
uint8_t * GetLeases () { return m_Leases; };
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
bool IsExpired () const;
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.GetBufferLen () && !memcmp (other.GetBuffer (), other.GetBuffer (), m_BufferLen); };
private:
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_Buffer, * m_Leases;
size_t m_BufferLen;
};
}
}

22
Log.cpp
View File

@@ -10,9 +10,9 @@
namespace i2p {
namespace log {
Log logger;
static Log logger;
/**
* @enum Maps our loglevel to their symbolic name
* @brief Maps our loglevel to their symbolic name
*/
static const char * g_LogLevelStr[eNumLogLevels] =
{
@@ -56,7 +56,7 @@ namespace log {
#endif
case eLogFile:
case eLogStream:
m_LogStream->flush();
if (m_LogStream) m_LogStream->flush();
break;
default:
/* do nothing */
@@ -107,10 +107,11 @@ namespace log {
#endif
case eLogFile:
case eLogStream:
*m_LogStream << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << g_LogLevelStr[msg->level]
<< " - " << msg->text << std::endl;
if (m_LogStream)
*m_LogStream << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << g_LogLevelStr[msg->level]
<< " - " << msg->text << std::endl;
break;
case eLogStdout:
default:
@@ -130,10 +131,13 @@ namespace log {
Process();
}
void Log::SendTo (const std::string& path) {
void Log::SendTo (const std::string& path)
{
if (m_LogStream) m_LogStream = nullptr; // close previous
auto flags = std::ofstream::out | std::ofstream::app;
auto os = std::make_shared<std::ofstream> (path, flags);
if (os->is_open ()) {
if (os->is_open ())
{
m_Logfile = path;
m_Destination = eLogFile;
m_LogStream = os;

41
Log.h
View File

@@ -40,8 +40,20 @@ enum LogType {
#endif
};
#ifdef _WIN32
const char LOG_COLOR_ERROR[] = "";
const char LOG_COLOR_WARNING[] = "";
const char LOG_COLOR_RESET[] = "";
#else
const char LOG_COLOR_ERROR[] = "\033[1;31m";
const char LOG_COLOR_WARNING[] = "\033[1;33m";
const char LOG_COLOR_RESET[] = "\033[0m";
#endif
namespace i2p {
namespace log {
struct LogMsg; /* forward declaration */
class Log
@@ -86,7 +98,7 @@ namespace log {
LogLevel GetLogLevel () { return m_MinLevel; };
/**
* @brief Sets minimal alloed level for log messages
* @brief Sets minimal allowed level for log messages
* @param level String with wanted minimal msg level
*/
void SetLogLevel (const std::string& level);
@@ -101,7 +113,7 @@ namespace log {
* @brief Sets log destination to given output stream
* @param os Output stream
*/
void SendTo (std::shared_ptr<std::ostream> s);
void SendTo (std::shared_ptr<std::ostream> os);
#ifndef _WIN32
/**
@@ -129,7 +141,8 @@ namespace log {
};
/**
* @struct Log message container
* @struct LogMsg
* @brief Log message container
*
* We creating it somewhere with LogPrint(),
* then put in MsgQueue for later processing.
@@ -149,17 +162,17 @@ namespace log {
/** internal usage only -- folding args array to single string */
template<typename TValue>
void LogPrint (std::stringstream& s, TValue arg)
void LogPrint (std::stringstream& s, TValue&& arg) noexcept
{
s << arg;
s << std::forward<TValue>(arg);
}
/** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
{
LogPrint (s, arg);
LogPrint (s, args...);
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
}
/**
@@ -168,7 +181,7 @@ void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
* @param args Array of message parts
*/
template<typename... TArgs>
void LogPrint (LogLevel level, TArgs... args)
void LogPrint (LogLevel level, TArgs&&... args) noexcept
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
@@ -176,8 +189,16 @@ void LogPrint (LogLevel level, TArgs... args)
// fold message to single string
std::stringstream ss("");
LogPrint (ss, args ...);
if(level == eLogError) // if log level is ERROR color log message red
ss << LOG_COLOR_ERROR;
else if (level == eLogWarning) // if log level is WARN color log message yellow
ss << LOG_COLOR_WARNING;
LogPrint (ss, std::forward<TArgs>(args)...);
// reset color
ss << LOG_COLOR_RESET;
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
msg->tid = std::this_thread::get_id();
log.Append(msg);

View File

@@ -11,6 +11,8 @@ include filelist.mk
USE_AESNI := yes
USE_STATIC := no
USE_MESHNET := no
USE_UPNP := no
ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp
@@ -30,6 +32,10 @@ else # win32 mingw
include Makefile.mingw
endif
ifeq ($(USE_MESHNET),yes)
NEEDED_CXXFLAGS += -DMESHNET
endif
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
mk_obj_dir:
@@ -76,6 +82,7 @@ $(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
clean:
rm -rf obj
rm -rf docs/generated
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
@@ -86,9 +93,13 @@ dist:
git archive --format=tar.gz -9 --worktree-attributes \
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
doxygen:
doxygen -s docs/Doxyfile
.PHONY: all
.PHONY: clean
.PHONY: deps
.PHONY: doxygen
.PHONY: dist
.PHONY: api
.PHONY: api_client

View File

@@ -9,4 +9,4 @@ CXXFLAGS = -O2
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
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread

View File

@@ -6,9 +6,9 @@ CXX = clang++
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),1)
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif

View File

@@ -1,5 +1,5 @@
# set defaults instead redefine
CXXFLAGS ?= -g -Wall
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic
INCFLAGS ?=
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
@@ -28,24 +28,26 @@ endif
NEEDED_CXXFLAGS += -fPIC
ifeq ($(USE_STATIC),yes)
# NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib
LDLIBS = $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_regex.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt
USE_AESNI := no
else
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
endif
# UPNP Support (miniupnpc 1.5 or 1.6)
ifeq ($(USE_UPNP),1)
LDFLAGS += -ldl
ifeq ($(USE_UPNP),yes)
LDFLAGS += -lminiupnpc
CXXFLAGS += -DUSE_UPNP
endif

View File

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

View File

@@ -3,9 +3,9 @@ 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
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),1)
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif

View File

@@ -1,6 +1,6 @@
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.h"
@@ -19,8 +19,9 @@ namespace i2p
namespace transport
{
NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()),
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
TransportSession (in_RemoteRouter, NTCP_TERMINATION_TIMEOUT),
m_Server (server), m_Socket (m_Server.GetService ()),
m_IsEstablished (false), m_IsTerminated (false),
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
{
m_Establisher = new Establisher;
@@ -77,7 +78,6 @@ namespace transport
m_Server.RemoveNTCPSession (shared_from_this ());
m_SendQueue.clear ();
m_NextMessage = nullptr;
m_TerminationTimer.cancel ();
LogPrint (eLogDebug, "NTCP: session terminated");
}
}
@@ -109,7 +109,6 @@ namespace transport
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
void NTCPSession::ServerLogin ()
@@ -123,7 +122,6 @@ namespace transport
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
}
@@ -430,7 +428,7 @@ namespace transport
}
else
{
LogPrint (eLogInfo, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
LogPrint (eLogDebug, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
m_Server.AddNTCPSession (shared_from_this ());
Connected ();
@@ -557,7 +555,7 @@ namespace transport
m_Handler.Flush ();
}
ScheduleTermination (); // reset termination timer
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
Receive ();
}
}
@@ -665,7 +663,7 @@ namespace transport
{
m_IsSending = true;
std::vector<boost::asio::const_buffer> bufs;
for (auto it: msgs)
for (const auto& it: msgs)
bufs.push_back (CreateMsgBuffer (it));
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
@@ -684,6 +682,7 @@ namespace transport
}
else
{
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumSentBytes += bytes_transferred;
i2p::transport::transports.UpdateSentBytes (bytes_transferred);
if (!m_SendQueue.empty())
@@ -691,8 +690,6 @@ namespace transport
Send (m_SendQueue);
m_SendQueue.clear ();
}
else
ScheduleTermination (); // reset termination timer
}
}
@@ -713,35 +710,25 @@ namespace transport
if (m_IsTerminated) return;
if (m_IsSending)
{
for (auto it: msgs)
m_SendQueue.push_back (it);
if (m_SendQueue.size () < NTCP_MAX_OUTGOING_QUEUE_SIZE)
{
for (const auto& it: msgs)
m_SendQueue.push_back (it);
}
else
{
LogPrint (eLogWarning, "NTCP: outgoing messages queue size exceeds ", NTCP_MAX_OUTGOING_QUEUE_SIZE);
Terminate ();
}
}
else
Send (msgs);
}
void NTCPSession::ScheduleTermination ()
{
m_TerminationTimer.cancel ();
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer,
shared_from_this (), std::placeholders::_1));
}
void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogDebug, "NTCP: No activity for ", NTCP_TERMINATION_TIMEOUT, " seconds");
//Terminate ();
m_Socket.close ();// invoke Terminate () from HandleReceive
}
}
//-----------------------------------------
NTCPServer::NTCPServer ():
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
{
}
@@ -758,33 +745,51 @@ namespace transport
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
// create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (auto address: addresses)
for (const auto& address: addresses)
{
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP && address->host.is_v4 ())
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
if (context.SupportsV6 ())
if (!address) continue;
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
if (address->host.is_v4())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1));
try
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
} catch ( std::exception & ex ) {
/** fail to bind ip4 */
LogPrint(eLogError, "NTCP: Failed to bind to ip4 port ",address->port, ex.what());
continue;
}
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
}
else if (address->host.is_v6() && context.SupportsV6 ())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
try
{
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1));
} catch ( std::exception & ex ) {
LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port);
continue;
}
}
}
}
}
}
ScheduleTermination ();
}
}
@@ -795,11 +800,17 @@ namespace transport
if (m_IsRunning)
{
m_IsRunning = false;
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
m_TerminationTimer.cancel ();
if (m_NTCPAcceptor)
{
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
}
if (m_NTCPV6Acceptor)
{
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
}
m_Service.stop ();
if (m_Thread)
{
@@ -834,6 +845,7 @@ namespace transport
if (it != m_NTCPSessions.end ())
{
LogPrint (eLogWarning, "NTCP: session to ", ident.ToBase64 (), " already exists");
session->Terminate();
return false;
}
m_NTCPSessions.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<NTCPSession> >(ident, session));
@@ -942,7 +954,7 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Connect error: ", ecode.message ());
LogPrint (eLogError, "NTCP: Connect error ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
@@ -962,5 +974,31 @@ namespace transport
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
LogPrint (eLogWarning, "NTCP: ", addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
}
void NTCPServer::ScheduleTermination ()
{
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimer.async_wait (std::bind (&NTCPServer::HandleTerminationTimer,
this, std::placeholders::_1));
}
void NTCPServer::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: m_NTCPSessions)
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
m_Service.post ([session]
{
LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds");
session->Terminate ();
});
}
ScheduleTermination ();
}
}
}
}

View File

@@ -37,9 +37,11 @@ namespace transport
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second
const int NTCP_CLOCK_SKEW = 60; // in seconds
const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up
class NTCPServer;
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
@@ -52,8 +54,8 @@ namespace transport
void Done ();
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
bool IsEstablished () const { return m_IsEstablished; };
bool IsEstablished () const { return m_IsEstablished; };
void ClientLogin ();
void ServerLogin ();
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
@@ -94,16 +96,10 @@ namespace transport
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
NTCPServer& m_Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::deadline_timer m_TerminationTimer;
bool m_IsEstablished, m_IsTerminated;
i2p::crypto::CBCDecryption m_Decryption;
@@ -144,7 +140,10 @@ namespace transport
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
boost::asio::io_service& GetService () { return m_Service; };
void Ban (const boost::asio::ip::address& addr);
@@ -155,6 +154,10 @@ namespace transport
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);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
@@ -162,6 +165,7 @@ namespace transport
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_TerminationTimer;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds

232
NetDb.cpp
View File

@@ -2,7 +2,7 @@
#include <fstream>
#include <vector>
#include <boost/asio.hpp>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.h"
@@ -14,7 +14,6 @@
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
#include "util.h"
using namespace i2p::transport;
@@ -24,7 +23,7 @@ namespace data
{
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat")
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_HiddenMode(false)
{
}
@@ -35,11 +34,11 @@ namespace data
}
void NetDb::Start ()
{
{
m_Storage.SetPlace(i2p::fs::GetDataDir());
m_Storage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
InitProfilesStorage ();
m_Families.LoadCertificates ();
m_Families.LoadCertificates ();
Load ();
if (m_RouterInfos.size () < 25) // reseed if # of router less than 50
Reseed ();
@@ -52,7 +51,7 @@ namespace data
{
if (m_IsRunning)
{
for (auto it: m_RouterInfos)
for (auto& it: m_RouterInfos)
it.second->SaveProfile ();
DeleteObsoleteProfiles ();
m_RouterInfos.clear ();
@@ -67,12 +66,12 @@ namespace data
}
m_LeaseSets.clear();
m_Requests.Stop ();
}
}
}
void NetDb::Run ()
{
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0;
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
while (m_IsRunning)
{
try
@@ -121,8 +120,16 @@ namespace data
ManageLookupResponses ();
}
lastSave = ts;
}
if (ts - lastPublish >= 2400) // publish every 40 minutes
}
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
i2p::context.CleanupDestination ();
lastDestinationCleanup = ts;
}
// if we're in hidden mode don't publish or explore
// if (m_HiddenMode) continue;
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // publish
{
Publish ();
lastPublish = ts;
@@ -161,6 +168,11 @@ namespace data
return false;
}
void NetDb::SetHidden(bool hide) {
// TODO: remove reachable addresses from router info
m_HiddenMode = hide;
}
bool NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len)
{
bool updated = true;
@@ -174,10 +186,8 @@ namespace data
// TODO: check if floodfill has been changed
}
else
{
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
updated = false;
}
}
else
{
@@ -206,6 +216,7 @@ namespace data
bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
bool updated = false;
if (!from) // unsolicited LS must be received directly
{
@@ -217,29 +228,29 @@ namespace data
it->second->Update (buf, len);
if (it->second->IsValid ())
{
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase64());
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32());
updated = true;
}
else
{
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase64());
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase32());
m_LeaseSets.erase (it);
}
}
else
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase64());
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32());
}
else
{
auto leaseSet = std::make_shared<LeaseSet> (buf, len, false); // we don't need leases in netdb
if (leaseSet->IsValid ())
{
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase64());
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
updated = true;
}
else
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase64());
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase32());
}
}
return updated;
@@ -257,6 +268,7 @@ namespace data
std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
auto it = m_LeaseSets.find (destination);
if (it != m_LeaseSets.end ())
return it->second;
@@ -311,6 +323,74 @@ namespace data
return true;
}
void NetDb::VisitLeaseSets(LeaseSetVisitor v)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
for ( auto & entry : m_LeaseSets)
v(entry.first, entry.second);
}
void NetDb::VisitStoredRouterInfos(RouterInfoVisitor v)
{
m_Storage.Iterate([v] (const std::string & filename) {
auto ri = std::make_shared<i2p::data::RouterInfo>(filename);
v(ri);
});
}
void NetDb::VisitRouterInfos(RouterInfoVisitor v)
{
std::unique_lock<std::mutex> lock(m_RouterInfosMutex);
for ( const auto & item : m_RouterInfos )
v(item.second);
}
size_t NetDb::VisitRandomRouterInfos(RouterInfoFilter filter, RouterInfoVisitor v, size_t n)
{
std::vector<std::shared_ptr<const RouterInfo> > found;
const size_t max_iters_per_cyle = 3;
size_t iters = max_iters_per_cyle;
while(n > 0)
{
std::unique_lock<std::mutex> lock(m_RouterInfosMutex);
uint32_t idx = rand () % m_RouterInfos.size ();
uint32_t i = 0;
for (const auto & it : m_RouterInfos) {
if(i >= idx) // are we at the random start point?
{
// yes, check if we want this one
if(filter(it.second))
{
// we have a match
--n;
found.push_back(it.second);
// reset max iterations per cycle
iters = max_iters_per_cyle;
break;
}
}
else // not there yet
++i;
}
// we have enough
if(n == 0) break;
--iters;
// have we tried enough this cycle ?
if(!iters) {
// yes let's try the next cycle
--n;
iters = max_iters_per_cyle;
}
}
// visit the ones we found
size_t visited = 0;
for(const auto & ri : found ) {
v(ri);
++visited;
}
return visited;
}
void NetDb::Load ()
{
// make sure we cleanup netDb from previous attempts
@@ -320,7 +400,7 @@ namespace data
m_LastLoad = i2p::util::GetSecondsSinceEpoch();
std::vector<std::string> files;
m_Storage.Traverse(files);
for (auto path : files)
for (const auto& path : files)
LoadRouterInfo(path);
LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size (), " floodfils)");
@@ -338,7 +418,7 @@ namespace data
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
for (auto it: m_RouterInfos)
for (auto& it: m_RouterInfos)
{
std::string ident = it.second->GetIdentHashBase64();
std::string path = m_Storage.Path(ident);
@@ -386,7 +466,7 @@ namespace data
it = m_RouterInfos.erase (it);
continue;
}
it++;
++it;
}
}
// clean up expired floodfiils
@@ -396,7 +476,7 @@ namespace data
if ((*it)->IsUnreachable ())
it = m_Floodfills.erase (it);
else
it++;
++it;
}
}
}
@@ -450,12 +530,18 @@ namespace data
}
offset += 32;
}
// we must send reply back before this check
if (ident == i2p::context.GetIdentHash ())
{
LogPrint (eLogError, "NetDb: database store with own RouterInfo received, dropped");
return;
}
size_t payloadOffset = offset;
bool updated = false;
if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
{
LogPrint (eLogDebug, "NetDb: store request: LeaseSet");
LogPrint (eLogDebug, "NetDb: store request: LeaseSet for ", ident.ToBase32());
updated = AddLeaseSet (ident, buf + offset, len - offset, m->from);
}
else
@@ -470,8 +556,13 @@ namespace data
}
uint8_t uncompressed[2048];
size_t uncompressedSize = m_Inflator.Inflate (buf + offset, size, uncompressed, 2048);
if (uncompressedSize)
if (uncompressedSize && uncompressedSize < 2048)
updated = AddRouterInfo (ident, uncompressed, uncompressedSize);
else
{
LogPrint (eLogError, "NetDb: decompression failed ", uncompressedSize);
return;
}
}
if (replyToken && context.IsFloodfill () && updated)
@@ -481,25 +572,32 @@ namespace data
uint8_t * payload = floodMsg->GetPayload ();
memcpy (payload, buf, 33); // key + type
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token
auto msgLen = len - payloadOffset;
size_t msgLen = len - payloadOffset;
floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen;
if (floodMsg->len < floodMsg->maxLen)
{
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen);
floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore);
std::set<IdentHash> excluded;
for (int i = 0; i < 3; i++)
excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself
excluded.insert (ident); // don't flood back
for (int i = 0; i < 3; i++)
{
auto floodfill = GetClosestFloodfill (ident, excluded);
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), floodMsg);
{
auto h = floodfill->GetIdentHash();
LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64());
transports.SendMessage (h, CopyI2NPMessage(floodMsg));
excluded.insert (h);
}
else
break;
}
}
else
LogPrint (eLogError, "Database store message is too long ", floodMsg->len);
}
LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len);
}
}
void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
@@ -603,14 +701,18 @@ namespace data
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
IdentHash replyIdent(buf + 32);
uint8_t flag = buf[64];
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " recieved flags=", (int)flag);
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
const uint8_t * excluded = buf + 65;
uint32_t replyTunnelID = 0;
if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel
{
replyTunnelID = bufbe32toh (buf + 64);
replyTunnelID = bufbe32toh (excluded);
excluded += 4;
}
uint16_t numExcluded = bufbe16toh (excluded);
@@ -618,7 +720,7 @@ namespace data
if (numExcluded > 512)
{
LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512");
numExcluded = 0; // TODO:
return;
}
std::shared_ptr<I2NPMessage> replyMsg;
@@ -662,7 +764,12 @@ namespace data
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP))
{
auto leaseSet = FindLeaseSet (ident);
if (leaseSet && !leaseSet->IsExpired ()) // we don't send back our LeaseSets
if (!leaseSet)
{
// no lease set found
LogPrint(eLogDebug, "NetDb: requested LeaseSet not found for ", ident.ToBase32());
}
else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets
{
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
@@ -671,7 +778,7 @@ namespace data
if (!replyMsg)
{
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found. ", numExcluded, " excluded");
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
// find or cleate response
std::vector<IdentHash> closestFloodfills;
bool found = false;
@@ -686,45 +793,49 @@ namespace data
}
if (!found)
{
std::set<IdentHash> excludedRouters;
std::set<IdentHash> excludedRouters;
const uint8_t * exclude_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 32;
excludedRouters.insert (exclude_ident);
exclude_ident += 32;
}
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
if (!numExcluded) // save if no excluded
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
}
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
}
}
}
excluded += numExcluded * 32;
if (replyMsg)
{
if (replyTunnelID)
{
// encryption might be used though tunnel only
if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested
if (flag & DATABASE_LOOKUP_ENCRYPTION_FLAG) // encrypted reply requested
{
const uint8_t * sessionKey = excluded;
uint8_t numTags = sessionKey[32];
if (numTags > 0)
const uint8_t numTags = excluded[32];
if (numTags)
{
const uint8_t * sessionTag = sessionKey + 33; // take first tag
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
}
else
LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided");
}
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
if (outbound)
outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
outbound->SendTunnelDataMsg (replyIdent, replyTunnelID, replyMsg);
else
transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
}
else
transports.SendMessage (buf+32, replyMsg);
transports.SendMessage (replyIdent, replyMsg);
}
}
@@ -849,13 +960,14 @@ namespace data
template<typename Filter>
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const
{
if (!m_RouterInfos.size ()) return 0;
uint32_t ind = rand () % m_RouterInfos.size ();
if (m_RouterInfos.empty())
return 0;
uint32_t ind = rand () % m_RouterInfos.size ();
for (int j = 0; j < 2; j++)
{
uint32_t i = 0;
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
for (auto it: m_RouterInfos)
for (const auto& it: m_RouterInfos)
{
if (i >= ind)
{
@@ -887,7 +999,7 @@ namespace data
else
minMetric.SetMax ();
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
for (const auto& it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
@@ -918,7 +1030,7 @@ namespace data
if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash ();
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
for (const auto& it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
@@ -937,11 +1049,11 @@ namespace data
std::vector<IdentHash> res;
size_t i = 0;
for (auto it: sorted)
for (const auto& it: sorted)
{
if (i < num)
{
auto& ident = it.r->GetIdentHash ();
const auto& ident = it.r->GetIdentHash ();
if (!excluded.count (ident))
{
res.push_back (ident);
@@ -954,6 +1066,14 @@ namespace data
return res;
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily(const std::string & fam) const {
return GetRandomRouter(
[fam](std::shared_ptr<const RouterInfo> router)->bool
{
return router->IsFamily(fam);
});
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
{
@@ -962,7 +1082,7 @@ namespace data
IdentHash destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
// must be called from NetDb thread only
for (auto it: m_RouterInfos)
for (const auto& it: m_RouterInfos)
{
if (!it.second->IsFloodfill ())
{
@@ -988,7 +1108,7 @@ namespace data
it = m_LeaseSets.erase (it);
}
else
it++;
++it;
}
}
@@ -1000,7 +1120,7 @@ namespace data
if (ts > it->second.second + 180) // 3 minutes
it = m_LookupResponses.erase (it);
else
it++;
++it;
}
}
}

41
NetDb.h
View File

@@ -8,7 +8,10 @@
#include <string>
#include <thread>
#include <mutex>
#include <future>
#include "Base.h"
#include "Gzip.h"
#include "FS.h"
#include "Queue.h"
#include "I2NPProtocol.h"
@@ -29,7 +32,17 @@ namespace data
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60;
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours
const int NETDB_PUBLISH_INTERVAL = 60*40;
/** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
/** function for visiting a router info we have locally */
typedef std::function<void(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoVisitor;
/** function for visiting a router info and determining if we want to use it */
typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter;
class NetDb
{
public:
@@ -39,7 +52,7 @@ namespace data
void Start ();
void Stop ();
bool AddRouterInfo (const uint8_t * buf, int len);
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@@ -62,10 +75,14 @@ namespace data
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(const std::string & fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
/** set hidden mode, aka don't publish our RI to netdb and don't explore */
void SetHidden(bool hide);
void Reseed ();
Families& GetFamilies () { return m_Families; };
@@ -73,7 +90,15 @@ namespace data
int GetNumRouters () const { return m_RouterInfos.size (); };
int GetNumFloodfills () const { return m_Floodfills.size (); };
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
/** visit all lease sets we currently store */
void VisitLeaseSets(LeaseSetVisitor v);
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
void VisitStoredRouterInfos(RouterInfoVisitor v);
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
void VisitRouterInfos(RouterInfoVisitor v);
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
private:
void Load ();
@@ -86,11 +111,12 @@ namespace data
void ManageRequests ();
void ManageLookupResponses ();
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
private:
mutable std::mutex m_LeaseSetsMutex;
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex;
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
@@ -111,6 +137,9 @@ namespace data
NetDbRequests m_Requests;
std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp)
/** true if in hidden mode */
bool m_HiddenMode;
};
extern NetDb netdb;

View File

@@ -68,8 +68,7 @@ namespace data
dest->SetRequestComplete (requestComplete);
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
if (!m_RequestedDestinations.insert (std::make_pair (destination,
std::shared_ptr<RequestedDestination> (dest))).second) // not inserted
if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted
return nullptr;
}
return dest;
@@ -77,20 +76,28 @@ namespace data
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
{
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
{
if (r)
it->second->Success (r);
else
it->second->Fail ();
std::shared_ptr<RequestedDestination> request;
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
m_RequestedDestinations.erase (it);
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
{
request = it->second;
m_RequestedDestinations.erase (it);
}
}
if (request)
{
if (r)
request->Success (r);
else
request->Fail ();
}
}
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
return it->second;
@@ -141,7 +148,7 @@ namespace data
if (done)
it = m_RequestedDestinations.erase (it);
else
it++;
++it;
}
}
}

View File

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

View File

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

View File

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

View File

@@ -1,38 +1,63 @@
i2pd
====
Independent C++ implementation of I2P router
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
I2P (Invisible Internet Protocol) is a universal anonymous network layer.
All communications over I2P are anonymous and end-to-end encrypted, participants
don't reveal their real IP addresses.
I2P client is a software used for building and using anonymous I2P
networks. Such networks are commonly used for anonymous peer-to-peer
applications (filesharing, cryptocurrencies) and anonymous client-server
applications (websites, instant messengers, chat-servers).
I2P allows people from all around the world to communicate and share information
without restrictions.
* [Website](http://i2pd.website)
* [Documentation](https://i2pd.readthedocs.io/en/latest/)
* [Wiki](https://github.com/PurpleI2P/i2pd/wiki)
* [Tickets/Issues](https://github.com/PurpleI2P/i2pd/issues)
* [Specifications](https://geti2p.net/spec)
* [Twitter](https://twitter.com/hashtag/i2pd)
Installing
----------
The easiest way to install i2pd is by using
[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest).
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
i2pd from source on your OS.
**Supported systems:**
* 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
* Android
* iOS
Using i2pd
----------
See [documentation](https://i2pd.readthedocs.io/en/latest/usage.html) and
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf).
Donations
---------
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y
License
-------
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.
Donations
---------
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y
Documentation:
--------------
http://i2pd.readthedocs.org
Supported OS
------------
* 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
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,9 @@
#include <string.h>
#include <fstream>
#include <sstream>
#include <boost/regex.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/algorithm/string.hpp>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <zlib.h>
@@ -15,27 +15,15 @@
#include "Log.h"
#include "Identity.h"
#include "NetDb.h"
#include "HTTP.h"
#include "util.h"
#include "Config.h"
namespace i2p
{
namespace data
{
static std::vector<std::string> httpsReseedHostList =
{
"https://reseed.i2p-projekt.de/", // Only HTTPS
"https://i2p.mooo.com/netDb/",
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
"https://us.reseed.i2p2.no:444/",
"https://uk.reseed.i2p2.no:444/",
"https://i2p.manas.ca:8443/",
"https://i2p-0.manas.ca:8443/",
"https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
"https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
"https://download.xxlspeed.com/" // Only HTTPS and SU3 (v3) support
};
Reseeder::Reseeder()
{
}
@@ -46,6 +34,17 @@ namespace data
int Reseeder::ReseedNowSU3 ()
{
std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs);
std::vector<std::string> httpsReseedHostList;
boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on);
std::string filename; i2p::config::GetOption("reseed.file", filename);
if (filename.length() > 0) // reseed file is specified
{
auto num = ProcessSU3File (filename.c_str ());
if (num > 0) return num; // success
LogPrint (eLogWarning, "Can't reseed from ", filename, " . Trying from hosts");
}
auto ind = rand () % httpsReseedHostList.size ();
std::string& reseedHost = httpsReseedHostList[ind];
return ReseedFromSU3 (reseedHost);
@@ -132,52 +131,64 @@ namespace data
s.read (signerID, signerIDLength); // signerID
signerID[signerIDLength] = 0;
//try to verify signature
auto it = m_SigningKeys.find (signerID);
if (it != m_SigningKeys.end ())
{
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
bool verify; i2p::config::GetOption("reseed.verify", verify);
if (verify)
{
//try to verify signature
auto it = m_SigningKeys.find (signerID);
if (it != m_SigningKeys.end ())
{
size_t pos = s.tellg ();
size_t tbsLen = pos + contentLength;
uint8_t * tbs = new uint8_t[tbsLen];
s.seekg (0, std::ios::beg);
s.read ((char *)tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength);
// RSA-raw
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
{
// calculate digest
uint8_t digest[64];
SHA512 (tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
}
size_t pos = s.tellg ();
size_t tbsLen = pos + contentLength;
uint8_t * tbs = new uint8_t[tbsLen];
s.seekg (0, std::ios::beg);
s.read ((char *)tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength);
// RSA-raw
{
// calculate digest
uint8_t digest[64];
SHA512 (tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
else
verify = false; // verified
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
}
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
}
else
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
if (verify) // not verified
{
LogPrint (eLogError, "Reseed: SU3 verification failed");
return 0;
}
// handle content
int numFiles = 0;
size_t contentPos = s.tellg ();
@@ -331,11 +342,21 @@ namespace data
// extract issuer name
char name[100];
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
char * cn = strstr (name, "CN=");
if (cn)
{
cn += 3;
char * terminator = strchr (cn, '/');
if (terminator) terminator[0] = 0;
}
// extract RSA key (we need n only, e = 65537)
RSA * key = X509_get_pubkey (cert)->pkey.rsa;
PublicKey value;
i2p::crypto::bn2buf (key->n, value, 512);
m_SigningKeys[name] = value;
if (cn)
m_SigningKeys[cn] = value;
else
LogPrint (eLogError, "Reseed: Can't find CN field in ", filename);
}
SSL_free (ssl);
}
@@ -368,13 +389,19 @@ namespace data
std::string Reseeder::HttpsRequest (const std::string& address)
{
i2p::util::http::url u(address);
if (u.port_ == 80) u.port_ = 443;
i2p::http::URL url;
if (!url.parse(address)) {
LogPrint(eLogError, "Reseed: failed to parse url: ", address);
return "";
}
url.schema = "https";
if (!url.port)
url.port = 443;
boost::asio::io_service service;
boost::system::error_code ecode;
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode);
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
if (!ecode)
{
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
@@ -386,32 +413,52 @@ namespace data
s.handshake (boost::asio::ssl::stream_base::client, ecode);
if (!ecode)
{
LogPrint (eLogInfo, "Reseed: Connected to ", u.host_, ":", u.port_);
// send request
std::stringstream ss;
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
s.write_some (boost::asio::buffer (ss.str ()));
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
i2p::http::HTTPReq req;
req.uri = url.to_string();
req.add_header("User-Agent", "Wget/1.11.4");
req.add_header("Connection", "close");
s.write_some (boost::asio::buffer (req.to_string()));
// read response
std::stringstream rs;
char response[1024]; size_t l = 0;
do
{
l = s.read_some (boost::asio::buffer (response, 1024), ecode);
if (l) rs.write (response, l);
}
while (!ecode && l);
char recv_buf[1024]; size_t l = 0;
do {
l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode);
if (l) rs.write (recv_buf, l);
} while (!ecode && l);
// process response
return i2p::util::http::GetHttpContent (rs);
std::string data = rs.str();
i2p::http::HTTPRes res;
int len = res.parse(data);
if (len <= 0) {
LogPrint(eLogWarning, "Reseed: incomplete/broken response from ", url.host);
return "";
}
if (res.code != 200) {
LogPrint(eLogError, "Reseed: failed to reseed from ", url.host, ", http code ", res.code);
return "";
}
data.erase(0, len); /* drop http headers from response */
LogPrint(eLogDebug, "Reseed: got ", data.length(), " bytes of data from ", url.host);
if (res.is_chunked()) {
std::stringstream in(data), out;
if (!i2p::http::MergeChunkedResponse(in, out)) {
LogPrint(eLogWarning, "Reseed: failed to merge chunked response from ", url.host);
return "";
}
LogPrint(eLogDebug, "Reseed: got ", data.length(), "(", out.tellg(), ") bytes of data from ", url.host);
data = out.str();
}
return data;
}
else
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to ", u.host_, ": ", ecode.message ());
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't resolve address ", u.host_, ": ", ecode.message ());
LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
return "";
}
}

View File

@@ -18,7 +18,8 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_StartupTime (0), m_Status (eRouterStatusOK )
m_StartupTime (0), m_Status (eRouterStatusOK), m_Error (eRouterErrorNone),
m_NetID (I2PD_NET_ID)
{
}
@@ -49,14 +50,35 @@ namespace i2p
uint16_t port; i2p::config::GetOption("port", port);
if (!port)
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
std::string host; i2p::config::GetOption("host", host);
if (i2p::config::IsDefault("host"))
host = "127.0.0.1"; // replace default address with safe value
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ipv4)
{
std::string host = "127.0.0.1";
if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host);
else if (!nat && !ifname.empty())
/* bind to interface, we have no NAT so set external address too */
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
}
if (ipv6)
{
std::string host = "::";
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
i2p::config::GetOption("host", host);
else if (!ifname.empty())
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
}
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
i2p::config::GetOption("netid", m_NetID);
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
@@ -75,6 +97,7 @@ namespace i2p
if (status != m_Status)
{
m_Status = status;
m_Error = eRouterErrorNone;
switch (m_Status)
{
case eRouterStatusOK:
@@ -92,7 +115,7 @@ namespace i2p
void RouterContext::UpdatePort (int port)
{
bool updated = false;
for (auto address : m_RouterInfo.GetAddresses ())
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->port != port)
{
@@ -107,7 +130,7 @@ namespace i2p
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
{
bool updated = false;
for (auto address : m_RouterInfo.GetAddresses ())
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->host != host && address->IsCompatible (host))
{
@@ -224,16 +247,17 @@ namespace i2p
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
// remove NTCP address
auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++)
for (auto it = addresses.begin (); it != addresses.end (); ++it)
{
if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
(*it)->host.is_v4 ())
{
addresses.erase (addresses.begin () + i);
addresses.erase (it);
break;
}
}
// delete previous introducers
for (auto addr : addresses)
for (auto& addr : addresses)
addr->introducers.clear ();
// update
@@ -253,17 +277,18 @@ namespace i2p
// insert NTCP back
auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++)
for (const auto& addr : addresses)
{
if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU)
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addresses[i]->host.to_string ().c_str (), addresses[i]->port);
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
// delete previous introducers
for (auto addr : addresses)
for (auto& addr : addresses)
addr->introducers.clear ();
// update
@@ -294,7 +319,7 @@ namespace i2p
bool updated = false, found = false;
int port = 0;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto addr: addresses)
for (auto& addr: addresses)
{
if (addr->host.is_v6 () && addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
@@ -417,6 +442,12 @@ namespace i2p
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
void RouterContext::CleanupDestination ()
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::CleanupExpiredTags ();
}
uint32_t RouterContext::GetUptime () const
{

View File

@@ -20,9 +20,16 @@ namespace i2p
{
eRouterStatusOK = 0,
eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2
eRouterStatusFirewalled = 2,
eRouterStatusError = 3
};
enum RouterError
{
eRouterErrorNone = 0,
eRouterErrorClockSkew = 1
};
class RouterContext: public i2p::garlic::GarlicDestination
{
public:
@@ -30,6 +37,7 @@ namespace i2p
RouterContext ();
void Init ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
{
@@ -48,6 +56,10 @@ namespace i2p
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status);
RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; };
void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
@@ -70,16 +82,18 @@ namespace i2p
void SetSupportsV4 (bool supportsV4);
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
void UpdateStats ();
void UpdateStats ();
void CleanupDestination (); // garlic destination
// implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); };
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetStandardIdentity ().publicKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
void SetLeaseSetUpdated () {};
// implements GarlicDestination
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@@ -104,6 +118,8 @@ namespace i2p
uint64_t m_StartupTime; // in seconds since epoch
uint32_t m_BandwidthLimit; // allowed bandwidth
RouterStatus m_Status;
RouterError m_Error;
int m_NetID;
std::mutex m_GarlicMutex;
};

View File

@@ -3,22 +3,33 @@
#include "I2PEndian.h"
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#if (BOOST_VERSION >= 105300)
#include <boost/atomic.hpp>
#endif
#include "version.h"
#include "Crypto.h"
#include "Base.h"
#include "Timestamp.h"
#include "Log.h"
#include "NetDb.h"
#include "RouterContext.h"
#include "RouterInfo.h"
namespace i2p
{
namespace data
{
RouterInfo::RouterInfo (): m_Buffer (nullptr)
{
m_Addresses = boost::make_shared<Addresses>(); // create empty list
}
RouterInfo::RouterInfo (const std::string& fullPath):
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0), m_Caps (0)
{
m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
ReadFromFile ();
}
@@ -26,6 +37,7 @@ namespace data
RouterInfo::RouterInfo (const uint8_t * buf, int len):
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
{
m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
@@ -48,7 +60,7 @@ namespace data
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
// don't clean up m_Addresses, it will be replaced in ReadFromStream
m_Properties.clear ();
// copy buffer
if (!m_Buffer)
@@ -118,14 +130,6 @@ namespace data
m_IsUnreachable = true;
return;
}
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
if (!str)
{
LogPrint (eLogError, "RouterInfo: malformed message");
m_IsUnreachable = true;
return;
}
if (verifySignature)
{
// verify signature
@@ -134,9 +138,20 @@ namespace data
{
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
return;
}
m_RouterIdentity->DropVerifier ();
}
// parse RI
std::stringstream str;
str.write ((const char *)m_Buffer + identityLen, m_BufferLen - identityLen);
ReadFromStream (str);
if (!str)
{
LogPrint (eLogError, "RouterInfo: malformed message");
m_IsUnreachable = true;
}
}
void RouterInfo::ReadFromStream (std::istream& s)
@@ -144,6 +159,7 @@ namespace data
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
auto addresses = boost::make_shared<Addresses>();
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
@@ -155,7 +171,7 @@ namespace data
s.read ((char *)&address.cost, sizeof (address.cost));
s.read ((char *)&address.date, sizeof (address.date));
char transportStyle[5];
ReadString (transportStyle, s);
ReadString (transportStyle, 5, s);
if (!strcmp (transportStyle, "NTCP"))
address.transportStyle = eTransportNTCP;
else if (!strcmp (transportStyle, "SSU"))
@@ -169,11 +185,12 @@ namespace data
size = be16toh (size);
while (r < size)
{
char key[500], value[500];
r += ReadString (key, s);
char key[255], value[255];
r += ReadString (key, 255, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
if (!strcmp (key, "host"))
{
boost::system::error_code ecode;
@@ -215,6 +232,11 @@ namespace data
size_t l = strlen(key);
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
if (index > 9)
{
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
if (s) continue; else return;
}
if (index >= address.introducers.size ())
address.introducers.resize (index + 1);
Introducer& introducer = address.introducers.at (index);
@@ -234,10 +256,15 @@ namespace data
}
if (isValidAddress)
{
m_Addresses.push_back(std::make_shared<Address>(address));
addresses->push_back(std::make_shared<Address>(address));
m_SupportedTransports |= supportedTransports;
}
}
#if (BOOST_VERSION >= 105300)
boost::atomic_store (&m_Addresses, addresses);
#else
m_Addresses = addresses; // race condition
#endif
// read peers
uint8_t numPeers;
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
@@ -248,26 +275,21 @@ namespace data
size = be16toh (size);
while (r < size)
{
#ifdef _WIN32
char key[500], value[500];
// TODO: investigate why properties get read as one long string under Windows
// length should not be more than 44
#else
char key[50], value[50];
#endif
r += ReadString (key, s);
char key[255], value[255];
r += ReadString (key, 255, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
m_Properties[key] = value;
// extract caps
if (!strcmp (key, "caps"))
ExtractCaps (value);
// check netId
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != I2PD_NET_ID)
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != i2p::context.GetNetID ())
{
LogPrint (eLogError, "Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
m_IsUnreachable = true;
}
// family
@@ -288,9 +310,13 @@ namespace data
if (!s) return;
}
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers))
SetUnreachable (true);
}
}
bool RouterInfo::IsFamily(const std::string & fam) const {
return m_Family == fam;
}
void RouterInfo::ExtractCaps (const char * value)
{
@@ -335,40 +361,37 @@ namespace data
void RouterInfo::UpdateCapsProperty ()
{
std::string caps;
if (m_Caps & eFloodfill) {
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
caps += (m_Caps & eExtraBandwidth)
? CAPS_FLAG_EXTRA_BANDWIDTH1 // 'P'
: CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
} else {
if (m_Caps & eExtraBandwidth) {
caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
} else if (m_Caps & eHighBandwidth) {
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
} else {
caps += CAPS_FLAG_LOW_BANDWIDTH2; // 'L'
}
}
else
{
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
}
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
SetProperty ("caps", caps.c_str ());
SetProperty ("caps", caps);
}
void RouterInfo::WriteToStream (std::ostream& s)
void RouterInfo::WriteToStream (std::ostream& s) const
{
uint64_t ts = htobe64 (m_Timestamp);
s.write ((char *)&ts, sizeof (ts));
s.write ((const char *)&ts, sizeof (ts));
// addresses
uint8_t numAddresses = m_Addresses.size ();
uint8_t numAddresses = m_Addresses->size ();
s.write ((char *)&numAddresses, sizeof (numAddresses));
for (auto addr : m_Addresses)
for (const auto& addr_ptr : *m_Addresses)
{
Address& address = *addr;
s.write ((char *)&address.cost, sizeof (address.cost));
s.write ((char *)&address.date, sizeof (address.date));
const Address& address = *addr_ptr;
s.write ((const char *)&address.cost, sizeof (address.cost));
s.write ((const char *)&address.date, sizeof (address.date));
std::stringstream properties;
if (address.transportStyle == eTransportNTCP)
WriteString ("NTCP", s);
@@ -397,7 +420,7 @@ namespace data
if (address.introducers.size () > 0)
{
int i = 0;
for (auto introducer: address.introducers)
for (const auto& introducer: address.introducers)
{
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -406,7 +429,7 @@ namespace data
i++;
}
i = 0;
for (auto introducer: address.introducers)
for (const auto& introducer: address.introducers)
{
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -418,7 +441,7 @@ namespace data
i++;
}
i = 0;
for (auto introducer: address.introducers)
for (const auto& introducer: address.introducers)
{
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -427,7 +450,7 @@ namespace data
i++;
}
i = 0;
for (auto introducer: address.introducers)
for (const auto& introducer: address.introducers)
{
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -469,7 +492,7 @@ namespace data
// properties
std::stringstream properties;
for (auto& p : m_Properties)
for (const auto& p : m_Properties)
{
WriteString (p.first, properties);
properties << '=';
@@ -532,16 +555,26 @@ namespace data
return true;
}
size_t RouterInfo::ReadString (char * str, std::istream& s)
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
{
uint8_t len;
s.read ((char *)&len, 1);
s.read (str, len);
str[len] = 0;
return len+1;
uint8_t l;
s.read ((char *)&l, 1);
if (l < len)
{
s.read (str, l);
if (!s) l = 0; // failed, return empty string
str[l] = 0;
}
else
{
LogPrint (eLogWarning, "RouterInfo: string length ", (int)l, " exceeds buffer size ", len);
s.seekg (l, std::ios::cur); // skip
str[0] = 0;
}
return l+1;
}
void RouterInfo::WriteString (const std::string& str, std::ostream& s)
void RouterInfo::WriteString (const std::string& str, std::ostream& s) const
{
uint8_t len = str.size ();
s.write ((char *)&len, 1);
@@ -557,10 +590,10 @@ namespace data
addr->cost = 2;
addr->date = 0;
addr->mtu = 0;
for (auto it: m_Addresses) // don't insert same address twice
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_Addresses.push_back(addr);
m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4;
m_Addresses->push_back(std::move(addr));
}
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
@@ -573,21 +606,22 @@ namespace data
addr->date = 0;
addr->mtu = mtu;
memcpy (addr->key, key, 32);
for (auto it: m_Addresses) // don't insert same address twice
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_Addresses.push_back(addr);
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
m_Addresses->push_back(std::move(addr));
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
}
bool RouterInfo::AddIntroducer (const Introducer& introducer)
{
for (auto addr : m_Addresses)
for (auto& addr : *m_Addresses)
{
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{
for (auto intro: addr->introducers)
for (auto& intro: addr->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
addr->introducers.push_back (introducer);
return true;
@@ -598,16 +632,16 @@ namespace data
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
{
for (auto addr: m_Addresses)
for (auto& addr: *m_Addresses)
{
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{
for (std::vector<Introducer>::iterator it = addr->introducers.begin (); it != addr->introducers.end (); it++)
for (auto it = addr->introducers.begin (); it != addr->introducers.end (); ++it)
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
{
addr->introducers.erase (it);
return true;
}
}
}
}
return false;
@@ -687,28 +721,14 @@ namespace data
{
if (IsV6 ())
{
// NTCP
m_SupportedTransports &= ~eNTCPV6;
for (size_t i = 0; i < m_Addresses.size (); i++)
m_SupportedTransports &= ~(eNTCPV6 | eSSUV6);
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
m_Addresses[i]->host.is_v6 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
}
// SSU
m_SupportedTransports &= ~eSSUV6;
for (size_t i = 0; i < m_Addresses.size (); i++)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
m_Addresses[i]->host.is_v6 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
auto addr = *it;
if (addr->host.is_v6 ())
it = m_Addresses->erase (it);
else
++it;
}
}
}
@@ -717,28 +737,14 @@ namespace data
{
if (IsV4 ())
{
// NTCP
m_SupportedTransports &= ~eNTCPV4;
for (size_t i = 0; i < m_Addresses.size (); i++)
m_SupportedTransports &= ~(eNTCPV4 | eSSUV4);
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
m_Addresses[i]->host.is_v4 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
}
// SSU
m_SupportedTransports &= ~eSSUV4;
for (size_t i = 0; i < m_Addresses.size (); i++)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
m_Addresses[i]->host.is_v4 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
auto addr = *it;
if (addr->host.is_v4 ())
it = m_Addresses->erase (it);
else
++it;
}
}
}
@@ -766,7 +772,12 @@ namespace data
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
{
for (auto address : m_Addresses)
#if (BOOST_VERSION >= 105300)
auto addresses = boost::atomic_load (&m_Addresses);
#else
auto addresses = m_Addresses;
#endif
for (const auto& address : *addresses)
{
if (address->transportStyle == s)
{

View File

@@ -5,8 +5,10 @@
#include <string>
#include <map>
#include <vector>
#include <list>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include "Identity.h"
#include "Profiling.h"
@@ -105,9 +107,10 @@ namespace data
return !(*this == other);
}
};
typedef std::list<std::shared_ptr<Address> > Addresses;
RouterInfo ();
RouterInfo (const std::string& fullPath);
RouterInfo (): m_Buffer (nullptr) { };
RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (const uint8_t * buf, int len);
@@ -117,7 +120,7 @@ namespace data
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<std::shared_ptr<Address> >& GetAddresses () { return m_Addresses; };
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCPAddress (bool v4only = true) const;
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
std::shared_ptr<const Address> GetSSUV6Address () const;
@@ -171,6 +174,9 @@ namespace data
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
bool IsNewer (const uint8_t * buf, size_t len) const;
/** return true if we are in a router family and the signature is valid */
bool IsFamily(const std::string & fam) const;
// implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_RouterIdentity->GetIdentHash (); };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity->GetStandardIdentity ().publicKey; };
@@ -182,9 +188,9 @@ namespace data
void ReadFromFile ();
void ReadFromStream (std::istream& s);
void ReadFromBuffer (bool verifySignature);
void WriteToStream (std::ostream& s);
size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s);
void WriteToStream (std::ostream& s) const;
size_t ReadString (char* str, size_t len, std::istream& s) const;
void WriteString (const std::string& str, std::ostream& s) const;
void ExtractCaps (const char * value);
std::shared_ptr<const Address> GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
void UpdateCapsProperty ();
@@ -196,7 +202,7 @@ namespace data
uint8_t * m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp;
std::vector<std::shared_ptr<Address> > m_Addresses;
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_Caps;

View File

@@ -56,7 +56,8 @@ namespace client
if (m_Session)
{
m_Session->DelSocket (shared_from_this ());
m_Session->localDestination->StopAcceptingStreams ();
if (m_Session->localDestination)
m_Session->localDestination->StopAcceptingStreams ();
}
break;
}
@@ -685,7 +686,7 @@ namespace client
{
{
std::lock_guard<std::mutex> lock(m_SocketsMutex);
for (auto sock : m_Sockets) {
for (auto& sock : m_Sockets) {
sock->CloseStream();
}
}
@@ -718,7 +719,7 @@ namespace client
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto it: m_Sessions)
for (auto& it: m_Sessions)
it.second->CloseStreams ();
m_Sessions.clear ();
m_Service.stop ();

2
SAM.h
View File

@@ -154,7 +154,7 @@ namespace client
std::list<std::shared_ptr<SAMSocket> > l;
{
std::lock_guard<std::mutex> lock(m_SocketsMutex);
for( auto & sock : m_Sockets ) l.push_back(sock);
for(const auto& sock : m_Sockets ) l.push_back(sock);
}
return l;
}

View File

@@ -26,7 +26,7 @@ namespace proxy
{
uint8_t size;
char value[max_socks_hostname_size];
void FromString (std::string str)
void FromString (const std::string& str)
{
size = str.length();
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;

167
SSU.cpp
View File

@@ -10,12 +10,33 @@ namespace i2p
{
namespace transport
{
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
SSUServer::SSUServer (const boost::asio::ip::address & addr, int port):
m_OnlyV6(true), m_IsRunning(false),
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
m_EndpointV6 (addr, port),
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
{
m_SocketV6.open (boost::asio::ip::udp::v6());
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
m_SocketV6.bind (m_EndpointV6);
}
SSUServer::SSUServer (int port):
m_OnlyV6(false), m_IsRunning(false),
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service)
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
{
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
if (context.SupportsV6 ())
@@ -35,13 +56,18 @@ namespace transport
void SSUServer::Start ()
{
m_IsRunning = true;
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
if (!m_OnlyV6)
{
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
ScheduleTermination ();
}
if (context.SupportsV6 ())
{
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
ScheduleTerminationV6 ();
}
SchedulePeerTestsCleanupTimer ();
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
@@ -51,6 +77,8 @@ namespace transport
{
DeleteAllSessions ();
m_IsRunning = false;
m_TerminationTimer.cancel ();
m_TerminationTimerV6.cancel ();
m_Service.stop ();
m_Socket.close ();
m_ServiceV6.stop ();
@@ -215,9 +243,8 @@ namespace transport
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
{
std::shared_ptr<SSUSession> session;
for (auto it1: packets)
for (auto& packet: packets)
{
auto packet = it1;
try
{
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
@@ -231,7 +258,7 @@ namespace transport
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
(*sessions)[packet->from] = session;
LogPrint (eLogInfo, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
}
}
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
@@ -312,7 +339,7 @@ namespace transport
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
sessions[remoteEndpoint] = session;
// connect
LogPrint (eLogInfo, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect ();
}
@@ -364,10 +391,10 @@ namespace transport
}
if (introducerSession) // session found
LogPrint (eLogInfo, "SSU: Session to introducer already exists");
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
else // create new
{
LogPrint (eLogInfo, "SSU: Creating new session to introducer");
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession;
@@ -409,11 +436,11 @@ namespace transport
void SSUServer::DeleteAllSessions ()
{
for (auto it: m_Sessions)
for (auto& it: m_Sessions)
it.second->Close ();
m_Sessions.clear ();
for (auto it: m_SessionsV6)
for (auto& it: m_SessionsV6)
it.second->Close ();
m_SessionsV6.clear ();
}
@@ -422,7 +449,7 @@ namespace transport
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
{
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
for (auto s :m_Sessions)
for (const auto& s :m_Sessions)
if (filter (s.second)) filteredSessions.push_back (s.second);
if (filteredSessions.size () > 0)
{
@@ -437,8 +464,31 @@ namespace transport
return GetRandomV4Session (
[excluded](std::shared_ptr<SSUSession> session)->bool
{
return session->GetState () == eSessionStateEstablished && !session->IsV6 () &&
session != excluded;
return session->GetState () == eSessionStateEstablished && session != excluded;
}
);
}
template<typename Filter>
std::shared_ptr<SSUSession> SSUServer::GetRandomV6Session (Filter filter) // v6 only
{
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
for (const auto& s :m_SessionsV6)
if (filter (s.second)) filteredSessions.push_back (s.second);
if (filteredSessions.size () > 0)
{
auto ind = rand () % filteredSessions.size ();
return filteredSessions[ind];
}
return nullptr;
}
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded) // v6 only
{
return GetRandomV6Session (
[excluded](std::shared_ptr<SSUSession> session)->bool
{
return session->GetState () == eSessionStateEstablished && session != excluded;
}
);
}
@@ -490,7 +540,7 @@ namespace transport
std::list<boost::asio::ip::udp::endpoint> newList;
size_t numIntroducers = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it :m_Introducers)
for (const auto& it : m_Introducers)
{
auto session = FindSession (it);
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
@@ -507,23 +557,20 @@ namespace transport
{
// create new
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
if (introducers.size () > 0)
for (const auto& it1: introducers)
{
for (auto it1: introducers)
const auto& ep = it1->GetRemoteEndpoint ();
i2p::data::RouterInfo::Introducer introducer;
introducer.iHost = ep.address ();
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
if (i2p::context.AddIntroducer (introducer))
{
auto& ep = it1->GetRemoteEndpoint ();
i2p::data::RouterInfo::Introducer introducer;
introducer.iHost = ep.address ();
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
if (i2p::context.AddIntroducer (introducer))
{
newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
}
}
}
newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
}
}
}
m_Introducers = newList;
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
@@ -592,13 +639,65 @@ namespace transport
it = m_PeerTests.erase (it);
}
else
it++;
++it;
}
if (numDeleted > 0)
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
SchedulePeerTestsCleanupTimer ();
}
}
void SSUServer::ScheduleTermination ()
{
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer,
this, std::placeholders::_1));
}
void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: m_Sessions)
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
m_Service.post ([session]
{
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
session->Failed ();
});
}
ScheduleTermination ();
}
}
void SSUServer::ScheduleTerminationV6 ()
{
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6,
this, std::placeholders::_1));
}
void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: m_SessionsV6)
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
m_ServiceV6.post ([session]
{
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
session->Failed ();
});
}
ScheduleTerminationV6 ();
}
}
}
}

21
SSU.h
View File

@@ -23,6 +23,7 @@ namespace transport
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
struct SSUPacket
@@ -37,6 +38,7 @@ namespace transport
public:
SSUServer (int port);
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
~SSUServer ();
void Start ();
void Stop ();
@@ -47,6 +49,7 @@ namespace transport
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
void DeleteSession (std::shared_ptr<SSUSession> session);
void DeleteAllSessions ();
@@ -62,7 +65,7 @@ namespace transport
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest (uint32_t nonce);
private:
void Run ();
@@ -78,7 +81,9 @@ namespace transport
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
@@ -86,6 +91,12 @@ namespace transport
void SchedulePeerTestsCleanupTimer ();
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleTerminationV6 ();
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
private:
struct PeerTest
@@ -93,15 +104,17 @@ namespace transport
uint64_t creationTime;
PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice
};
};
bool m_OnlyV6;
bool m_IsRunning;
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread;
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService;
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer,
m_TerminationTimer, m_TerminationTimerV6;
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer

View File

@@ -25,10 +25,10 @@ namespace transport
}
SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
m_Session (session), m_ResendTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ()),
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize)
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
{
}
@@ -44,7 +44,6 @@ namespace transport
void SSUData::Stop ()
{
m_ResendTimer.cancel ();
m_DecayTimer.cancel ();
m_IncompleteMessagesCleanupTimer.cancel ();
}
@@ -233,15 +232,12 @@ namespace transport
{
if (!m_ReceivedMessages.count (msgID))
{
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
m_ReceivedMessages.clear ();
else
ScheduleDecay ();
m_ReceivedMessages.insert (msgID);
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
if (!msg->IsExpired ())
m_Handler.PutNextMessage (msg);
else
LogPrint (eLogInfo, "SSU: message expired");
LogPrint (eLogDebug, "SSU: message expired");
}
else
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
@@ -425,28 +421,30 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numResent = 0;
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
{
if (ts >= it->second->nextResendTime)
{
if (it->second->numResends < MAX_NUM_RESENDS)
{
{
for (auto& f: it->second->fragments)
if (f)
if (f)
{
try
{
{
m_Session.Send (f->buf, f->len); // resend
numResent++;
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogWarning, "SSU: Can't resend data fragment ", ec.what ());
}
}
}
it->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
it++;
++it;
}
else
{
@@ -455,27 +453,18 @@ namespace transport
}
}
else
it++;
++it;
}
ScheduleResend ();
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
ScheduleResend ();
else
{
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
m_Session.Close ();
}
}
}
void SSUData::ScheduleDecay ()
{
m_DecayTimer.cancel ();
m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleDecayTimer (ecode); });
}
void SSUData::HandleDecayTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
m_ReceivedMessages.clear ();
}
void SSUData::ScheduleIncompleteMessagesCleanup ()
{
m_IncompleteMessagesCleanupTimer.cancel ();
@@ -498,8 +487,13 @@ namespace transport
it = m_IncompleteMessages.erase (it);
}
else
it++;
++it;
}
// decay
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES ||
i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL)
m_ReceivedMessages.clear ();
ScheduleIncompleteMessagesCleanup ();
}
}

View File

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

View File

@@ -12,22 +12,23 @@ namespace i2p
namespace transport
{
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ): TransportSession (router),
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ):
TransportSession (router, SSU_TERMINATION_TIMEOUT),
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_ConnectTimer (GetService ()),
m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false),
m_RelayTag (0),m_Data (*this), m_IsDataReceived (false)
{
if (router)
{
// we are client
auto address = router->GetSSUAddress ();
auto address = router->GetSSUAddress (false);
if (address) m_IntroKey = address->key;
m_Data.AdjustPacketSize (router); // mtu
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (address) m_IntroKey = address->key;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
@@ -96,7 +97,7 @@ namespace transport
{
if (!len) return; // ignore zero-length packets
if (m_State == eSessionStateEstablished)
ScheduleTermination ();
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first
DecryptSessionKey (buf, len);
@@ -108,7 +109,7 @@ namespace transport
else
{
// try own intro key
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported");
@@ -228,7 +229,7 @@ namespace transport
}
LogPrint (eLogDebug, "SSU message: session created");
m_Timer.cancel (); // connect timer
m_ConnectTimer.cancel (); // connect timer
SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
auto headerSize = GetSSUHeaderSize (buf);
if (headerSize >= len)
@@ -271,6 +272,16 @@ namespace transport
s.Insert (payload, 8); // relayTag and signed on time
m_RelayTag = bufbe32toh (payload);
payload += 4; // relayTag
if (i2p::context.GetStatus () == eRouterStatusTesting)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t signedOnTime = bufbe32toh(payload);
if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW)
{
LogPrint (eLogError, "SSU: clock skew detected ", (int)ts - signedOnTime, ". Check your clock");
i2p::context.SetError (eRouterErrorClockSkew);
}
}
payload += 4; // signed on time
// decrypt signature
size_t signatureLen = m_RemoteIdentity->GetSignatureLen ();
@@ -309,6 +320,14 @@ namespace transport
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (payload, identitySize));
m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ());
payload += identitySize; // identity
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t signedOnTime = bufbe32toh(payload);
if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW)
{
LogPrint (eLogError, "SSU message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew");
Failed ();
return;
}
if (m_SignedData)
m_SignedData->Insert (payload, 4); // insert Alice's signed on time
payload += 4; // signed-on time
@@ -366,7 +385,7 @@ namespace transport
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce)
{
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported");
@@ -803,9 +822,9 @@ namespace transport
void SSUSession::ScheduleConnectTimer ()
{
m_Timer.cancel ();
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
m_ConnectTimer.cancel ();
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
shared_from_this (), std::placeholders::_1));
}
@@ -814,7 +833,7 @@ namespace transport
if (!ecode)
{
// timeout expired
LogPrint (eLogWarning, "SSU: session was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
LogPrint (eLogWarning, "SSU: session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
Failed ();
}
}
@@ -825,8 +844,8 @@ namespace transport
if (m_State == eSessionStateUnknown)
{
// set connect timer
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
shared_from_this (), std::placeholders::_1));
}
uint32_t nonce;
@@ -839,8 +858,8 @@ namespace transport
{
m_State = eSessionStateIntroduced;
// set connect timer
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
shared_from_this (), std::placeholders::_1));
}
@@ -850,7 +869,7 @@ namespace transport
SendSesionDestroyed ();
transports.PeerDisconnected (shared_from_this ());
m_Data.Stop ();
m_Timer.cancel ();
m_ConnectTimer.cancel ();
}
void SSUSession::Done ()
@@ -867,7 +886,7 @@ namespace transport
transports.PeerConnected (shared_from_this ());
if (m_IsPeerTest)
SendPeerTest ();
ScheduleTermination ();
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
}
void SSUSession::Failed ()
@@ -879,24 +898,6 @@ namespace transport
}
}
void SSUSession::ScheduleTermination ()
{
m_Timer.cancel ();
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT));
m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer,
shared_from_this (), std::placeholders::_1));
}
void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "SSU: no activity for ", SSU_TERMINATION_TIMEOUT, " seconds");
Failed ();
}
}
void SSUSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs));
@@ -906,7 +907,7 @@ namespace transport
{
if (m_State == eSessionStateEstablished)
{
for (auto it: msgs)
for (const auto& it: msgs)
if (it) m_Data.Send (it);
}
}
@@ -930,10 +931,10 @@ namespace transport
{
uint32_t nonce = bufbe32toh (buf); // 4 bytes
uint8_t size = buf[4]; // 1 byte
uint32_t address = (size == 4) ? buf32toh(buf + 5) : 0; // big endian, size bytes
const uint8_t * address = buf + 5; // big endian, size bytes
uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes
const uint8_t * introKey = buf + size + 7;
if (port && !address)
if (port && (size != 4) && (size != 16))
{
LogPrint (eLogWarning, "SSU: Address of ", size, " bytes not supported");
return;
@@ -954,8 +955,7 @@ namespace transport
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
i2p::context.SetStatus (eRouterStatusOK);
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey, true, false); // to Charlie
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
}
break;
}
@@ -984,8 +984,7 @@ namespace transport
case ePeerTestParticipantCharlie:
{
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Charlie");
SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey); // to Alice with her actual address
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address
m_Server.RemovePeerTest (nonce); // nonce has been used
break;
}
@@ -1000,17 +999,29 @@ namespace transport
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice with her address received from Bob
boost::asio::ip::address addr; // Alice's address
if (size == 4) // v4
{
boost::asio::ip::address_v4::bytes_type bytes;
memcpy (bytes.data (), address, 4);
addr = boost::asio::ip::address_v4 (bytes);
}
else // v6
{
boost::asio::ip::address_v6::bytes_type bytes;
memcpy (bytes.data (), address, 16);
addr = boost::asio::ip::address_v6 (bytes);
}
SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob
}
else
{
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Bob");
auto session = m_Server.GetRandomEstablishedV4Session (shared_from_this ()); // Charlie, TODO: implement v6 support
auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie
if (session)
{
m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ());
session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
session->SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
}
}
}
@@ -1020,7 +1031,7 @@ namespace transport
}
}
void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port,
void SSUSession::SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port,
const uint8_t * introKey, bool toAddress, bool sendAddress)
// toAddress is true for Alice<->Chalie communications only
// sendAddress is false if message comes from Alice
@@ -1031,12 +1042,21 @@ namespace transport
htobe32buf (payload, nonce);
payload += 4; // nonce
// address and port
if (sendAddress && address)
{
*payload = 4;
payload++; // size
htobe32buf (payload, address);
payload += 4; // address
if (sendAddress)
{
if (address.is_v4 ())
{
*payload = 4;
memcpy (payload + 1, address.to_v4 ().to_bytes ().data (), 4); // our IP V4
}
else if (address.is_v6 ())
{
*payload = 16;
memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6
}
else
*payload = 0;
payload += (payload[0] + 1);
}
else
{
@@ -1064,7 +1084,7 @@ namespace transport
{
// encrypt message with specified intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey);
boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port);
boost::asio::ip::udp::endpoint e (address, port);
m_Server.Send (buf, 80, e);
}
else
@@ -1079,7 +1099,7 @@ namespace transport
{
// we are Alice
LogPrint (eLogDebug, "SSU: sending peer test");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
@@ -1090,7 +1110,7 @@ namespace transport
if (!nonce) nonce = 1;
m_IsPeerTest = false;
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1);
SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice
}
void SSUSession::SendKeepAlive ()
@@ -1106,7 +1126,7 @@ namespace transport
FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
Send (buf, 48);
LogPrint (eLogDebug, "SSU: keep-alive sent");
ScheduleTermination ();
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
}
}

View File

@@ -27,6 +27,7 @@ namespace transport
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU_CLOCK_SKEW = 60; // in seconds
// payload types (4 bits)
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
@@ -77,6 +78,7 @@ namespace transport
void WaitForIntroduction ();
void Close ();
void Done ();
void Failed ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
@@ -114,11 +116,10 @@ namespace transport
void ProcessRelayResponse (const uint8_t * buf, size_t len);
void ProcessRelayIntro (const uint8_t * buf, size_t len);
void Established ();
void Failed ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
void ProcessData (uint8_t * buf, size_t len);
void SendSesionDestroyed ();
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
@@ -131,15 +132,12 @@ namespace transport
void DecryptSessionKey (uint8_t * buf, size_t len);
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
friend class SSUData; // TODO: change in later
SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_Timer;
boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest;
SessionState m_State;
bool m_IsSessionKey;

View File

@@ -36,7 +36,6 @@ namespace stream
Stream::~Stream ()
{
Terminate ();
while (!m_ReceiveQueue.empty ())
{
auto packet = m_ReceiveQueue.front ();
@@ -66,6 +65,7 @@ namespace stream
m_SendHandler = nullptr;
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted));
}
m_LocalDestination.DeleteStream (shared_from_this ());
}
void Stream::HandleNextPacket (Packet * packet)
@@ -87,7 +87,7 @@ namespace stream
return;
}
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn);
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID);
if (receivedSeqn == m_LastReceivedSequenceNumber + 1)
{
// we have received next in sequence message
@@ -129,13 +129,13 @@ namespace stream
if (receivedSeqn <= m_LastReceivedSequenceNumber)
{
// we have received duplicate
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " received");
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID);
SendQuickAck (); // resend ack for previous message again
delete packet; // packet dropped
}
else
{
LogPrint (eLogWarning, "Streaming: Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
// save message and wait for missing message again
SavePacket (packet);
if (m_LastReceivedSequenceNumber >= 0)
@@ -183,7 +183,7 @@ namespace stream
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, packet->GetOptionSize ());
optionData += m_RemoteIdentity->GetFullLen ();
if (!m_RemoteLeaseSet)
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 ());
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
}
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
@@ -201,7 +201,7 @@ namespace stream
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
{
LogPrint (eLogError, "Streaming: Signature verification failed");
LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
Close ();
flags |= PACKET_FLAG_CLOSE;
}
@@ -220,11 +220,19 @@ namespace stream
m_LastReceivedSequenceNumber = receivedSeqn;
if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET))
if (flags & PACKET_FLAG_RESET)
{
LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn);
m_Status = eStreamStatusReset;
Close ();
}
else if (flags & PACKET_FLAG_CLOSE)
{
if (m_Status != eStreamStatusClosed)
SendClose ();
m_Status = eStreamStatusClosed;
Terminate ();
}
}
void Stream::ProcessAck (Packet * packet)
@@ -232,9 +240,14 @@ namespace stream
bool acknowledged = false;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t ackThrough = packet->GetAckThrough ();
if (ackThrough > m_SequenceNumber)
{
LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber);
return;
}
int nackCount = packet->GetNACKCount ();
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
{
{
auto seqn = (*it)->GetSeqn ();
if (seqn <= ackThrough)
{
@@ -250,15 +263,20 @@ namespace stream
if (nacked)
{
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK");
it++;
++it;
continue;
}
}
auto sentPacket = *it;
uint64_t rtt = ts - sentPacket->sendTime;
if(ts < sentPacket->sendTime)
{
LogPrint(eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime);
rtt = 1;
}
m_RTT = (m_RTT*seqn + rtt)/(seqn + 1);
m_RTO = m_RTT*1.5; // TODO: implement it better
LogPrint (eLogDebug, "Packet ", seqn, " acknowledged rtt=", rtt);
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime);
m_SentPackets.erase (it++);
delete sentPacket;
acknowledged = true;
@@ -289,8 +307,10 @@ namespace stream
m_NumResendAttempts = 0;
SendBuffer ();
}
if (m_Status == eStreamStatusClosing)
Close (); // all outgoing messages have been sent
if (m_Status == eStreamStatusClosed)
Terminate ();
else if (m_Status == eStreamStatusClosing)
Close (); // check is all outgoing messages have been sent and we can send close
}
size_t Stream::Send (const uint8_t * buf, size_t len)
@@ -336,9 +356,9 @@ namespace stream
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
if (isNoAck)
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
else
htobuf32 (packet + size, 0);
else
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
@@ -390,11 +410,14 @@ namespace stream
}
if (packets.size () > 0)
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
if (m_SavedPackets.empty ()) // no NACKS
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
}
bool isEmpty = m_SentPackets.empty ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: packets)
for (auto& it: packets)
{
it->sendTime = ts;
m_SentPackets.insert (it);
@@ -443,7 +466,7 @@ namespace stream
auto seqn = it->GetSeqn ();
if (numNacks + (seqn - nextSeqn) >= 256)
{
LogPrint (eLogError, "Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
htobe32buf (packet + 12, nextSeqn); // change ack Through
break;
}
@@ -478,35 +501,32 @@ namespace stream
void Stream::Close ()
{
LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status);
switch (m_Status)
{
case eStreamStatusOpen:
m_Status = eStreamStatusClosing;
Close (); // recursion
if (m_Status == eStreamStatusClosing) //still closing
LogPrint (eLogInfo, "Streaming: Trying to send stream data before closing");
LogPrint (eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", m_SendStreamID);
break;
case eStreamStatusReset:
SendClose ();
Terminate ();
m_LocalDestination.DeleteStream (shared_from_this ());
// TODO: send reset
Terminate ();
break;
case eStreamStatusClosing:
if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send
{
m_Status = eStreamStatusClosed;
SendClose ();
Terminate ();
m_LocalDestination.DeleteStream (shared_from_this ());
}
break;
case eStreamStatusClosed:
// already closed
Terminate ();
m_LocalDestination.DeleteStream (shared_from_this ());
break;
default:
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status);
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID);
};
}
@@ -521,7 +541,7 @@ namespace stream
size += 4; // receiveStreamID
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
@@ -538,7 +558,7 @@ namespace stream
p->len = size;
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
LogPrint (eLogDebug, "Streaming: FIN sent");
LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID);
}
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
@@ -570,15 +590,10 @@ namespace stream
m_AckSendTimer.cancel ();
}
SendPackets (std::vector<Packet *> { packet });
if (m_Status == eStreamStatusOpen)
{
bool isEmpty = m_SentPackets.empty ();
m_SentPackets.insert (packet);
if (isEmpty)
ScheduleResend ();
}
else
delete packet;
bool isEmpty = m_SentPackets.empty ();
m_SentPackets.insert (packet);
if (isEmpty)
ScheduleResend ();
return true;
}
else
@@ -592,7 +607,7 @@ namespace stream
UpdateCurrentRemoteLease ();
if (!m_RemoteLeaseSet)
{
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet");
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID);
return;
}
}
@@ -617,7 +632,7 @@ namespace stream
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel)
{
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool");
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID);
return;
}
@@ -641,13 +656,16 @@ namespace stream
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
}
else
LogPrint (eLogWarning, "Streaming: All leases are expired");
LogPrint (eLogWarning, "Streaming: All leases are expired, sSID=", m_SendStreamID);
}
void Stream::ScheduleResend ()
{
m_ResendTimer.cancel ();
// check for invalid value
if (m_RTO <= 0)
m_RTO = 1;
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
shared_from_this (), std::placeholders::_1));
@@ -660,7 +678,7 @@ namespace stream
// check for resend attempts
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
{
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate");
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
m_Status = eStreamStatusReset;
Close ();
return;
@@ -695,13 +713,13 @@ namespace stream
case 4:
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
UpdateCurrentRemoteLease (); // pick another lease
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream");
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
break;
case 3:
// pick another outbound tunnel
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream");
LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID);
break;
default: ;
}
@@ -717,13 +735,21 @@ namespace stream
{
if (m_LastReceivedSequenceNumber < 0)
{
LogPrint (eLogWarning, "Streaming: SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate");
LogPrint (eLogWarning, "Streaming: SYN has not been received after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
m_Status = eStreamStatusReset;
Close ();
return;
}
if (m_Status == eStreamStatusOpen)
{
if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ())
{
// seems something went wrong and we should re-select tunnels
m_CurrentOutboundTunnel = nullptr;
m_CurrentRemoteLease = nullptr;
}
SendQuickAck ();
}
m_IsAckSendScheduled = false;
}
}
@@ -752,7 +778,7 @@ namespace stream
bool updated = false;
if (expired && m_CurrentRemoteLease)
{
for (auto it: leases)
for (const auto& it: leases)
if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID))
{
m_CurrentRemoteLease = it;
@@ -782,13 +808,16 @@ namespace stream
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_PendingIncomingTimer (m_Owner->GetService ())
m_PendingIncomingTimer (m_Owner->GetService ()),
m_ConnTrackTimer(m_Owner->GetService()),
m_ConnsPerMinute(DEFAULT_MAX_CONNS_PER_MIN),
m_LastBanClear(i2p::util::GetMillisecondsSinceEpoch())
{
}
StreamingDestination::~StreamingDestination ()
{
for (auto it: m_SavedPackets)
for (auto& it: m_SavedPackets)
{
for (auto it1: it.second) delete it1;
it.second.clear ();
@@ -797,17 +826,23 @@ namespace stream
}
void StreamingDestination::Start ()
{
{
ScheduleConnTrack();
}
void StreamingDestination::Stop ()
{
ResetAcceptor ();
m_PendingIncomingTimer.cancel ();
m_ConnTrackTimer.cancel();
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams.clear ();
}
}
{
std::unique_lock<std::mutex> l(m_ConnsMutex);
m_Conns.clear ();
}
}
void StreamingDestination::HandleNextPacket (Packet * packet)
@@ -820,7 +855,7 @@ namespace stream
it->second->HandleNextPacket (packet);
else
{
LogPrint (eLogError, "Streaming: Unknown stream sendStreamID=", sendStreamID);
LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID);
delete packet;
}
}
@@ -831,17 +866,29 @@ namespace stream
auto incomingStream = CreateNewIncomingStream ();
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
incomingStream->HandleNextPacket (packet); // SYN
auto ident = incomingStream->GetRemoteIdentity();
if(ident)
{
auto ih = ident->GetIdentHash();
if(DropNewStream(ih))
{
// drop
LogPrint(eLogWarning, "Streaming: Dropping connection, too many inbound streams from ", ih.ToBase32());
incomingStream->Terminate();
return;
}
}
// handle saved packets if any
{
auto it = m_SavedPackets.find (receiveStreamID);
if (it != m_SavedPackets.end ())
{
LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for receiveStreamID=", receiveStreamID);
LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for rSID=", receiveStreamID);
for (auto it1: it->second)
incomingStream->HandleNextPacket (it1);
m_SavedPackets.erase (it);
}
}
}
// accept
if (m_Acceptor != nullptr)
m_Acceptor (incomingStream);
@@ -855,7 +902,7 @@ namespace stream
m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT));
m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer,
shared_from_this (), std::placeholders::_1));
LogPrint (eLogDebug, "Streaming: Pending incoming stream added");
LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID);
}
else
{
@@ -867,7 +914,7 @@ namespace stream
else // follow on packet without SYN
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
for (auto it: m_Streams)
for (auto& it: m_Streams)
if (it.second->GetSendStreamID () == receiveStreamID)
{
// found
@@ -934,7 +981,7 @@ namespace stream
m_Owner->GetService ().post([acceptor, this](void)
{
m_Acceptor = acceptor;
for (auto it: m_PendingIncomingStreams)
for (auto& it: m_PendingIncomingStreams)
if (it->GetStatus () == eStreamStatusOpen) // still open?
m_Acceptor (it);
m_PendingIncomingStreams.clear ();
@@ -953,7 +1000,7 @@ namespace stream
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired");
for (auto it: m_PendingIncomingStreams)
for (auto& it: m_PendingIncomingStreams)
it->Close ();
m_PendingIncomingStreams.clear ();
}
@@ -994,6 +1041,64 @@ namespace stream
else
msg = nullptr;
return msg;
}
}
void StreamingDestination::SetMaxConnsPerMinute(const uint32_t conns)
{
m_ConnsPerMinute = conns;
LogPrint(eLogDebug, "Streaming: Set max conns per minute per destination to ", conns);
}
bool StreamingDestination::DropNewStream(const i2p::data::IdentHash & ih)
{
std::lock_guard<std::mutex> lock(m_ConnsMutex);
if (m_Banned.size() > MAX_BANNED_CONNS) return true; // overload
auto end = std::end(m_Banned);
if ( std::find(std::begin(m_Banned), end, ih) != end) return true; // already banned
auto itr = m_Conns.find(ih);
if (itr == m_Conns.end())
m_Conns[ih] = 0;
m_Conns[ih] += 1;
bool ban = m_Conns[ih] >= m_ConnsPerMinute;
if (ban)
{
m_Banned.push_back(ih);
m_Conns.erase(ih);
LogPrint(eLogWarning, "Streaming: ban ", ih.ToBase32());
}
return ban;
}
void StreamingDestination::HandleConnTrack(const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
{ // acquire lock
std::lock_guard<std::mutex> lock(m_ConnsMutex);
// clear conn tracking
m_Conns.clear();
// check for ban clear
auto ts = i2p::util::GetMillisecondsSinceEpoch();
if (ts - m_LastBanClear >= DEFAULT_BAN_INTERVAL)
{
// clear bans
m_Banned.clear();
m_LastBanClear = ts;
}
}
// reschedule timer
ScheduleConnTrack();
}
}
void StreamingDestination::ScheduleConnTrack()
{
m_ConnTrackTimer.expires_from_now (boost::posix_time::seconds(60));
m_ConnTrackTimer.async_wait (
std::bind (&StreamingDestination::HandleConnTrack,
shared_from_this (), std::placeholders::_1));
}
}
}

View File

@@ -51,6 +51,22 @@ namespace stream
const int INITIAL_RTO = 9000; // in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
/** i2cp option for limiting inbound stremaing connections */
const char I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN[] = "maxconns";
/** default maximum connections attempts per minute per destination */
const uint32_t DEFAULT_MAX_CONNS_PER_MIN = 600;
/**
* max banned destinations per local destination
* TODO: make configurable
*/
const uint16_t MAX_BANNED_CONNS = 9999;
/**
* length of a ban in ms
* TODO: make configurable
*/
const uint64_t DEFAULT_BAN_INTERVAL = 60 * 60 * 1000;
struct Packet
{
@@ -134,10 +150,11 @@ namespace stream
size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); };
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
private:
/** don't call me */
void Terminate ();
private:
void SendBuffer ();
void SendQuickAck ();
@@ -210,12 +227,22 @@ namespace stream
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort);
/** set max connections per minute per destination */
void SetMaxConnsPerMinute(const uint32_t conns);
private:
void HandleNextPacket (Packet * packet);
std::shared_ptr<Stream> CreateNewIncomingStream ();
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
/** handle cleaning up connection tracking for ratelimits */
void HandleConnTrack(const boost::system::error_code& ecode);
bool DropNewStream(const i2p::data::IdentHash & ident);
void ScheduleConnTrack();
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
@@ -227,7 +254,16 @@ namespace stream
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
std::mutex m_ConnsMutex;
/** how many connections per minute did each identity have */
std::map<i2p::data::IdentHash, uint32_t> m_Conns;
boost::asio::deadline_timer m_ConnTrackTimer;
uint32_t m_ConnsPerMinute;
/** banned identities */
std::vector<i2p::data::IdentHash> m_Banned;
uint64_t m_LastBanClear;
public:
i2p::data::GzipInflator m_Inflator;

89
Tag.h Normal file
View File

@@ -0,0 +1,89 @@
#ifndef TAG_H__
#define TAG_H__
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <boost/static_assert.hpp>
#include <string.h>
#include "Base.h"
namespace i2p {
namespace data {
template<size_t sz>
class Tag
{
BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes");
public:
Tag () = default;
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }
bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }
bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }
uint8_t * operator()() { return m_Buf; }
const uint8_t * operator()() const { return m_Buf; }
operator uint8_t * () { return m_Buf; }
operator const uint8_t * () const { return m_Buf; }
const uint8_t * data() const { return m_Buf; }
const uint64_t * GetLL () const { return ll; }
bool IsZero () const
{
for (size_t i = 0; i < sz/8; ++i)
if (ll[i]) return false;
return true;
}
void Fill(uint8_t c)
{
memset(m_Buf, c, sz);
}
std::string ToBase64 () const
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
return std::string (str, str + l);
}
std::string ToBase32 () const
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
return std::string (str, str + l);
}
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
union // 8 bytes aligned
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
} // data
} // i2p
#endif /* TAG_H__ */

50
Timestamp.cpp Normal file
View File

@@ -0,0 +1,50 @@
#include <inttypes.h>
#include <string.h>
#include <boost/asio.hpp>
#include "I2PEndian.h"
#include "Timestamp.h"
namespace i2p
{
namespace util
{
std::chrono::system_clock::duration g_TimeOffset = std::chrono::system_clock::duration::zero ();
void SyncTimeWithNTP (const std::string& address)
{
boost::asio::io_service service;
boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp");
boost::system::error_code ec;
auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec);
if (!ec && it != boost::asio::ip::udp::resolver::iterator())
{
auto ep = (*it).endpoint (); // take first one
boost::asio::ip::udp::socket socket (service);
socket.open (boost::asio::ip::udp::v4 (), ec);
if (!ec)
{
uint8_t request[48];// 48 bytes NTP request
memset (request, 0, 48);
request[0] = 0x80; // client mode, version 0
uint8_t * response = new uint8_t[1500]; // MTU
size_t len = 0;
try
{
socket.send_to (boost::asio::buffer (request, 48), ep);
len = socket.receive_from (boost::asio::buffer (response, 1500), ep);
}
catch (std::exception& e)
{
}
if (len >= 8)
{
uint32_t ts = bufbe32toh (response + 4);
if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900
}
delete[] response;
}
}
}
}
}

View File

@@ -8,6 +8,8 @@ namespace i2p
{
namespace util
{
extern std::chrono::system_clock::duration g_TimeOffset;
inline uint64_t GetMillisecondsSinceEpoch ()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(

View File

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

View File

@@ -9,6 +9,7 @@
#include "Crypto.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "Timestamp.h"
namespace i2p
{
@@ -53,8 +54,9 @@ namespace transport
{
public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router)
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ())
{
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
@@ -70,6 +72,11 @@ namespace transport
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
int GetTerminationTimeout () const { return m_TerminationTimeout; };
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
protected:
@@ -78,6 +85,8 @@ namespace transport
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp;
};
}
}

View File

@@ -46,7 +46,7 @@ namespace transport
int num;
while ((num = m_QueueSize - m_Queue.size ()) > 0)
CreateDHKeysPairs (num);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Acquired.wait (l); // wait for element gets aquired
}
}
@@ -60,7 +60,7 @@ namespace transport
{
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
}
@@ -69,7 +69,7 @@ namespace transport
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
{
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
if (!m_Queue.empty ())
{
auto pair = m_Queue.front ();
@@ -86,14 +86,14 @@ namespace transport
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
Transports transports;
Transports::Transports ():
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_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)
@@ -105,28 +105,46 @@ namespace transport
Stop ();
}
void Transports::Start ()
void Transports::Start (bool enableNTCP, bool enableSSU)
{
m_DHKeysPairSupplier.Start ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this));
// create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (auto address : addresses)
for (const auto& address : addresses)
{
if (!m_NTCPServer)
{
if (!address) continue;
if (m_NTCPServer == nullptr && enableNTCP)
{
m_NTCPServer = new NTCPServer ();
m_NTCPServer->Start ();
if (!(m_NTCPServer->IsBoundV6() || m_NTCPServer->IsBoundV4())) {
/** failed to bind to NTCP */
LogPrint(eLogError, "Transports: failed to bind to TCP");
m_NTCPServer->Stop();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
}
if (address->transportStyle == RouterInfo::eTransportSSU && address->host.is_v4 ())
if (address->transportStyle == RouterInfo::eTransportSSU)
{
if (!m_SSUServer)
{
m_SSUServer = new SSUServer (address->port);
if (m_SSUServer == nullptr && enableSSU)
{
if (address->host.is_v4())
m_SSUServer = new SSUServer (address->port);
else
m_SSUServer = new SSUServer (address->host, address->port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
m_SSUServer->Start ();
try {
m_SSUServer->Start ();
} catch ( std::exception & ex ) {
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
delete m_SSUServer;
m_SSUServer = nullptr;
continue;
}
DetectExternalIP ();
}
else
@@ -206,7 +224,7 @@ namespace transport
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
{
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
}
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
@@ -219,7 +237,7 @@ namespace transport
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
{
// we send it to ourself
for (auto it: msgs)
for (auto& it: msgs)
i2p::HandleI2NPMessage (it);
return;
}
@@ -231,7 +249,7 @@ namespace transport
{
auto r = netdb.FindRouter (ident);
{
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
i2p::util::GetSecondsSinceEpoch (), {} })).first;
}
@@ -247,8 +265,17 @@ namespace transport
it->second.sessions.front ()->SendI2NPMessages (msgs);
else
{
for (auto it1: msgs)
it->second.delayedMessages.push_back (it1);
if (it->second.delayedMessages.size () < MAX_NUM_DELAYED_MESSAGES)
{
for (auto& it1: msgs)
it->second.delayedMessages.push_back (it1);
}
else
{
LogPrint (eLogWarning, "Transports: delayed messages queue size exceeds ", MAX_NUM_DELAYED_MESSAGES);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
}
@@ -288,7 +315,7 @@ namespace transport
}
}
else
LogPrint (eLogWarning, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
}
if (peer.numAttempts == 1)// SSU
{
@@ -320,7 +347,7 @@ namespace transport
}
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
return false;
}
@@ -352,7 +379,7 @@ namespace transport
else
{
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
@@ -396,7 +423,7 @@ namespace transport
}
}
LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it1);
}
}
@@ -438,7 +465,7 @@ namespace transport
}
}
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it1);
}
}
@@ -446,7 +473,7 @@ namespace transport
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
@@ -456,26 +483,34 @@ namespace transport
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint (eLogDebug, "Transports: SSU session closed");
}
// TODO: delete NTCP
}
auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr;
if (ntcpSession) // try deleting ntcp session too
{
ntcpSession->Terminate ();
LogPrint(eLogDebug, "Transports: NTCP session closed");
}
}
void Transports::DetectExternalIP ()
{
if (m_SSUServer)
{
#ifndef MESHNET
i2p::context.SetStatus (eRouterStatusTesting);
#endif
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
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
m_SSUServer->CreateSession (router); // no peer test
}
}
}
@@ -498,7 +533,7 @@ namespace transport
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, true); // peer test
m_SSUServer->CreateSession (router, true); // peer test
}
}
}
@@ -517,7 +552,7 @@ namespace transport
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
{
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash ();
@@ -530,11 +565,13 @@ namespace transport
// check if first message is our DatabaseStore (publishing)
auto firstMsg = it->second.delayedMessages[0];
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
sendDatabaseStore = false; // we have it in the list already
}
if (sendDatabaseStore)
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () });
else
session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds
it->second.sessions.push_back (session);
session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear ();
@@ -542,7 +579,7 @@ namespace transport
else // incoming connection
{
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
}
});
@@ -551,7 +588,7 @@ namespace transport
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
{
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash ();
@@ -565,7 +602,7 @@ namespace transport
ConnectToPeer (ident, it->second);
else
{
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
@@ -590,14 +627,20 @@ namespace transport
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
{
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
std::unique_lock<std::mutex> l(m_PeersMutex);
auto profile = i2p::data::GetRouterProfile(it->first);
if (profile)
{
profile->TunnelNonReplied();
profile->Save();
}
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.erase (it);
}
else
it++;
++it;
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
DetectExternalIP ();
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
@@ -606,12 +649,36 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{
if (!m_Peers.size ()) return nullptr;
if (m_Peers.empty ()) 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;
}
void Transports::RestrictRoutes(std::vector<std::string> families)
{
std::lock_guard<std::mutex> lock(m_FamilyMutex);
m_TrustedFamilies.clear();
for ( const auto& fam : families )
m_TrustedFamilies.push_back(fam);
}
bool Transports::RoutesRestricted() const {
std::lock_guard<std::mutex> lock(m_FamilyMutex);
return m_TrustedFamilies.size() > 0;
}
/** XXX: if routes are not restricted this dies */
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const {
std::string fam;
{
std::lock_guard<std::mutex> lock(m_FamilyMutex);
// TODO: random family (?)
fam = m_TrustedFamilies[0];
}
boost::to_lower(fam);
return i2p::data::netdb.GetRandomRouterInFamily(fam);
}
}
}

View File

@@ -60,12 +60,13 @@ namespace transport
void Done ()
{
for (auto it: sessions)
for (auto& it: sessions)
it->Done ();
}
};
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
const int MAX_NUM_DELAYED_MESSAGES = 50;
class Transports
{
public:
@@ -73,9 +74,15 @@ namespace transport
Transports ();
~Transports ();
void Start ();
void Start (bool enableNTCP=true, bool enableSSU=true);
void Stop ();
bool IsBoundNTCP() const { return m_NTCPServer != nullptr; }
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
bool IsOnline() const { return m_IsOnline; };
void SetOnline (bool online) { m_IsOnline = online; };
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
@@ -98,6 +105,13 @@ namespace transport
size_t GetNumPeers () const { return m_Peers.size (); };
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
/** get a trusted first hop for restricted routes */
std::shared_ptr<const i2p::data::RouterInfo> GetRestrictedPeer() const;
/** do we want to use restricted routes? */
bool RoutesRestricted() const;
/** restrict routes to use only these router families for first hops */
void RestrictRoutes(std::vector<std::string> families);
void PeerTest ();
private:
@@ -122,7 +136,7 @@ namespace transport
private:
bool m_IsRunning;
bool m_IsOnline, m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
@@ -140,6 +154,10 @@ namespace transport
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
uint64_t m_LastBandwidthUpdateTime;
/** which router families to trust for first hops */
std::vector<std::string> m_TrustedFamilies;
mutable std::mutex m_FamilyMutex;
public:
// for HTTP only

View File

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

View File

@@ -73,7 +73,7 @@ namespace tunnel
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
virtual void Print (std::stringstream& s) const {};
virtual void Print (std::stringstream&) const {};
// implements TunnelBase
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
@@ -176,10 +176,10 @@ namespace tunnel
void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel);
std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config);
void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
template<class TTunnel>
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,
@@ -189,6 +189,9 @@ namespace tunnel
private:
template<class TTunnel>
std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
template<class TTunnel>
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
@@ -204,8 +207,8 @@ namespace tunnel
void ManagePendingTunnels (PendingTunnels& pendingTunnels);
void ManageTunnelPools ();
void CreateZeroHopsInboundTunnel ();
void CreateZeroHopsOutboundTunnel ();
std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel ();
std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel ();
private:

View File

@@ -159,6 +159,11 @@ namespace tunnel
return num;
}
bool IsEmpty () const
{
return !m_FirstHop;
}
virtual bool IsInbound () const { return m_FirstHop->isGateway; }
virtual uint32_t GetTunnelID () const
@@ -208,7 +213,7 @@ namespace tunnel
void CreatePeers (const Peers& peers)
{
TunnelHopConfig * prev = nullptr;
for (auto it: peers)
for (const auto& it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)

View File

@@ -205,7 +205,7 @@ namespace tunnel
if (it->second.fragmentNum == msg.nextFragmentNum)
{
LogPrint (eLogWarning, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
auto size = it->second.data->GetLength ();
size_t size = it->second.data->GetLength ();
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
@@ -235,7 +235,7 @@ namespace tunnel
LogPrint (eLogInfo, "TunnelMessage: message expired");
return;
}
auto typeID = msg.data->GetTypeID ();
uint8_t 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) &&

View File

@@ -49,7 +49,7 @@ namespace tunnel
// create fragments
std::shared_ptr<I2NPMessage> msg = block.data;
auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
if (fullMsgLen <= m_RemainingSize)
{
// message fits. First and last fragment
@@ -66,10 +66,10 @@ namespace tunnel
{
if (!messageCreated) // check if we should complete previous message
{
auto numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE;
size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE;
// length of bytes don't fit full tunnel message
// every follow-on fragment adds 7 bytes
auto nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE;
size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE;
if (!nonFit || nonFit > m_RemainingSize)
{
CompleteCurrentTunnelDataMessage ();
@@ -197,7 +197,7 @@ namespace tunnel
{
m_Buffer.CompleteCurrentTunnelDataMessage ();
auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto tunnelMsg : tunnelMsgs)
for (auto& tunnelMsg : tunnelMsgs)
{
m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg);
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData);

View File

@@ -15,7 +15,8 @@ namespace tunnel
{
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true)
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
m_CustomPeerSelector(nullptr)
{
}
@@ -49,13 +50,13 @@ namespace tunnel
{
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it: m_InboundTunnels)
for (auto& it: m_InboundTunnels)
it->SetTunnelPool (nullptr);
m_InboundTunnels.clear ();
}
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels)
for (auto& it: m_OutboundTunnels)
it->SetTunnelPool (nullptr);
m_OutboundTunnels.clear ();
}
@@ -78,7 +79,7 @@ namespace tunnel
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
for (auto& it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
@@ -101,7 +102,7 @@ namespace tunnel
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
for (auto& it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
@@ -114,7 +115,7 @@ namespace tunnel
std::vector<std::shared_ptr<InboundTunnel> > v;
int i = 0;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
for (const auto& it : m_InboundTunnels)
{
if (i >= num) break;
if (it->IsEstablished ())
@@ -144,7 +145,7 @@ namespace tunnel
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)
for (const auto& it: tunnels)
{
if (it->IsEstablished () && it != excluded)
{
@@ -164,7 +165,7 @@ namespace tunnel
if (old)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels)
for (const auto& it: m_OutboundTunnels)
if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
{
tunnel = it;
@@ -182,7 +183,7 @@ namespace tunnel
int num = 0;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumInboundTunnels; i++)
@@ -191,7 +192,7 @@ namespace tunnel
num = 0;
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it : m_OutboundTunnels)
for (const auto& it : m_OutboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumOutboundTunnels; i++)
@@ -203,11 +204,10 @@ namespace tunnel
decltype(m_Tests) tests;
{
std::unique_lock<std::mutex> l(m_TestsMutex);
tests = m_Tests;
m_Tests.clear ();
tests.swap(m_Tests);
}
for (auto it: tests)
for (auto& it: tests)
{
LogPrint (eLogWarning, "Tunnels: test of tunnel ", it.first, " failed");
// if test failed again with another tunnel we consider it failed
@@ -248,12 +248,12 @@ namespace tunnel
if ((*it1)->IsFailed ())
{
failed = true;
it1++;
++it1;
}
if ((*it2)->IsFailed ())
{
failed = true;
it2++;
++it2;
}
if (!failed)
{
@@ -265,7 +265,7 @@ namespace tunnel
}
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
CreateDeliveryStatusMsg (msgID));
it1++; it2++;
++it1; ++it2;
}
}
}
@@ -322,16 +322,34 @@ namespace tunnel
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
if (!hop || hop->GetProfile ()->IsBad ())
hop = i2p::data::netdb.GetRandomRouter ();
hop = i2p::data::netdb.GetRandomRouter (prevHop);
return hop;
}
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{
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)
// peers is empty
if (numHops <= 0) return true;
// custom peer selector in use ?
{
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
if (m_CustomPeerSelector)
return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound);
}
// explicit peers in use
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
auto prevHop = i2p::context.GetSharedRouterInfo ();
if(i2p::transport::transports.RoutesRestricted())
{
/** if routes are restricted prepend trusted first hop */
auto hop = i2p::transport::transports.GetRestrictedPeer();
if(!hop) return false;
peers.push_back(hop->GetRouterIdentity());
prevHop = hop;
}
else if (i2p::transport::transports.GetNumPeers () > 25)
{
auto r = i2p::transport::transports.GetRandomPeer ();
if (r && !r->GetProfile ()->IsBad ())
@@ -342,7 +360,7 @@ namespace tunnel
}
}
for (int i = 0; i < numHops; i++)
for(int i = 0; i < numHops; i++ )
{
auto hop = SelectNextHop (prevHop);
if (!hop)
@@ -352,7 +370,7 @@ namespace tunnel
}
prevHop = hop;
peers.push_back (hop->GetRouterIdentity ());
}
}
return true;
}
@@ -389,9 +407,16 @@ namespace 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);
std::shared_ptr<TunnelConfig> config;
if (m_NumInboundHops > 0)
{
std::reverse (peers.begin (), peers.end ());
config = std::make_shared<TunnelConfig> (peers);
}
auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
}
else
LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available");
@@ -403,8 +428,12 @@ namespace tunnel
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);
std::shared_ptr<TunnelConfig> config;
if (m_NumInboundHops > 0) config = std::make_shared<TunnelConfig>(tunnel->GetPeers ());
auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
newTunnel->SetTunnelPool (shared_from_this());
if (newTunnel->IsEstablished ()) // zero hops
TunnelCreated (newTunnel);
}
void TunnelPool::CreateOutboundTunnel ()
@@ -418,9 +447,13 @@ namespace 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 ()));
std::shared_ptr<TunnelConfig> config;
if (m_NumOutboundHops > 0)
config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
auto tunnel = tunnels.CreateOutboundTunnel (config);
tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
}
else
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
@@ -437,10 +470,13 @@ namespace tunnel
if (inboundTunnel)
{
LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (tunnel->GetPeers (),
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
std::shared_ptr<TunnelConfig> config;
if (m_NumOutboundHops > 0)
config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
auto newTunnel = tunnels.CreateOutboundTunnel (config);
newTunnel->SetTunnelPool (shared_from_this ());
if (newTunnel->IsEstablished ()) // zero hops
TunnelCreated (newTunnel);
}
else
LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found");
@@ -449,8 +485,25 @@ namespace tunnel
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);
auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
}
void TunnelPool::SetCustomPeerSelector(TunnelPeerSelector selector)
{
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
m_CustomPeerSelector = selector;
}
void TunnelPool::UnsetCustomPeerSelector()
{
SetCustomPeerSelector(nullptr);
}
bool TunnelPool::HasCustomPeerSelector()
{
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
return m_CustomPeerSelector != nullptr;
}
}
}

View File

@@ -23,6 +23,16 @@ namespace tunnel
class InboundTunnel;
class OutboundTunnel;
/** interface for custom tunnel peer selection algorithm */
struct ITunnelPeerSelector
{
typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
typedef std::vector<Peer> TunnelPath;
virtual bool SelectPeers(TunnelPath & peers, int hops, bool isInbound) = 0;
};
typedef std::shared_ptr<ITunnelPeerSelector> TunnelPeerSelector;
class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination
{
public:
@@ -45,7 +55,6 @@ namespace tunnel
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr) const;
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const;
std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
void TestTunnels ();
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
@@ -56,9 +65,12 @@ namespace tunnel
int GetNumInboundTunnels () const { return m_NumInboundTunnels; };
int GetNumOutboundTunnels () const { return m_NumOutboundTunnels; };
private:
void SetCustomPeerSelector(TunnelPeerSelector selector);
void UnsetCustomPeerSelector();
bool HasCustomPeerSelector();
private:
void CreateInboundTunnel ();
void CreateOutboundTunnel ();
void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
@@ -80,7 +92,8 @@ namespace tunnel
mutable std::mutex m_TestsMutex;
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
bool m_IsActive;
std::mutex m_CustomPeerSelectorMutex;
TunnelPeerSelector m_CustomPeerSelector;
public:
// for HTTP only

270
UPnP.cpp
View File

@@ -6,13 +6,6 @@
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#ifdef _WIN32
#include <windows.h>
#define dlsym GetProcAddress
#else
#include <dlfcn.h>
#endif
#include "Log.h"
#include "RouterContext.h"
@@ -20,213 +13,190 @@
#include "NetDb.h"
#include "util.h"
#include "RouterInfo.h"
#include "Config.h"
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
// These are per-process and are safe to reuse for all threads
decltype(upnpDiscover) *upnpDiscoverFunc;
decltype(UPNP_AddPortMapping) *UPNP_AddPortMappingFunc;
decltype(UPNP_GetValidIGD) *UPNP_GetValidIGDFunc;
decltype(UPNP_GetExternalIPAddress) *UPNP_GetExternalIPAddressFunc;
decltype(UPNP_DeletePortMapping) *UPNP_DeletePortMappingFunc;
decltype(freeUPNPDevlist) *freeUPNPDevlistFunc;
decltype(FreeUPNPUrls) *FreeUPNPUrlsFunc;
// Nice approach http://stackoverflow.com/a/21517513/673826
template<class M, typename F>
F GetKnownProcAddressImpl(M hmod, const char *name, F) {
auto proc = reinterpret_cast<F>(dlsym(hmod, name));
if (!proc) {
LogPrint(eLogError, "UPnP: Error resolving ", name, " from library, version mismatch?");
}
return proc;
}
#define GetKnownProcAddress(hmod, func) GetKnownProcAddressImpl(hmod, #func, func##Func);
namespace i2p
{
namespace transport
{
UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false)
UPnP::UPnP () : m_IsRunning(false), m_Thread (nullptr), m_Timer (m_Service)
{
}
void UPnP::Stop ()
{
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
if (m_IsRunning)
{
LogPrint(eLogInfo, "UPnP: stopping");
m_IsRunning = false;
m_Timer.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
m_Thread.reset (nullptr);
}
CloseMapping ();
Close ();
}
}
void UPnP::Start()
{
if (!m_IsModuleLoaded) {
#ifdef MAC_OSX
m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY);
#elif _WIN32
m_Module = LoadLibrary ("miniupnpc.dll"); // official prebuilt binary, e.g., in upnpc-exe-win32-20140422.zip
#else
m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY);
#endif
if (m_Module == NULL)
{
LogPrint (eLogError, "UPnP: Error loading UPNP library, version mismatch?");
return;
}
else
{
upnpDiscoverFunc = GetKnownProcAddress (m_Module, upnpDiscover);
UPNP_GetValidIGDFunc = GetKnownProcAddress (m_Module, UPNP_GetValidIGD);
UPNP_GetExternalIPAddressFunc = GetKnownProcAddress (m_Module, UPNP_GetExternalIPAddress);
UPNP_AddPortMappingFunc = GetKnownProcAddress (m_Module, UPNP_AddPortMapping);
UPNP_DeletePortMappingFunc = GetKnownProcAddress (m_Module, UPNP_DeletePortMapping);
freeUPNPDevlistFunc = GetKnownProcAddress (m_Module, freeUPNPDevlist);
FreeUPNPUrlsFunc = GetKnownProcAddress (m_Module, FreeUPNPUrls);
if (upnpDiscoverFunc && UPNP_GetValidIGDFunc && UPNP_GetExternalIPAddressFunc && UPNP_AddPortMappingFunc &&
UPNP_DeletePortMappingFunc && freeUPNPDevlistFunc && FreeUPNPUrlsFunc)
m_IsModuleLoaded = true;
}
}
m_Thread = new std::thread (std::bind (&UPnP::Run, this));
m_IsRunning = true;
LogPrint(eLogInfo, "UPnP: starting");
m_Service.post (std::bind (&UPnP::Discover, this));
std::unique_lock<std::mutex> l(m_StartedMutex);
m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this)));
m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum
}
UPnP::~UPnP ()
{
Stop ();
}
void UPnP::Run ()
{
const std::vector<std::shared_ptr<i2p::data::RouterInfo::Address> > a = context.GetRouterInfo().GetAddresses();
for (auto address : a)
{
if (!address->host.is_v6 ())
{
Discover ();
if (address->transportStyle == data::RouterInfo::eTransportSSU )
{
TryPortMapping (I2P_UPNP_UDP, address->port);
}
else if (address->transportStyle == data::RouterInfo::eTransportNTCP )
{
TryPortMapping (I2P_UPNP_TCP, address->port);
}
}
}
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
}
}
}
void UPnP::Discover ()
{
int nerror = 0;
#if MINIUPNPC_API_VERSION >= 14
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
#else
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
#endif
{
// notify satrting thread
std::unique_lock<std::mutex> l(m_StartedMutex);
m_Started.notify_all ();
}
int r;
r = UPNP_GetValidIGDFunc (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
r = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
if (r == 1)
{
r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(r != UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress () returned ", r);
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress() returned ", r);
return;
}
else
{
if (m_externalIPAddress[0])
if (!m_externalIPAddress[0])
{
LogPrint (eLogInfo, "UPnP: ExternalIPAddress = ", m_externalIPAddress);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
return;
}
else
{
LogPrint (eLogError, "UPnP: GetExternalIPAddress failed.");
LogPrint (eLogError, "UPnP: GetExternalIPAddress() failed.");
return;
}
}
}
else
{
LogPrint (eLogError, "UPnP: GetValidIGD() failed.");
return;
}
// UPnP discovered
LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
// port mapping
PortMapping ();
}
void UPnP::TryPortMapping (int type, int port)
{
std::string strType, strPort (std::to_string (port));
switch (type)
void UPnP::PortMapping ()
{
const auto& a = context.GetRouterInfo().GetAddresses();
for (const auto& address : a)
{
case I2P_UPNP_TCP:
strType = "TCP";
break;
case I2P_UPNP_UDP:
default:
strType = "UDP";
if (!address->host.is_v6 ())
TryPortMapping (address);
}
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
PortMapping ();
});
}
void UPnP::CloseMapping ()
{
const auto& a = context.GetRouterInfo().GetAddresses();
for (const auto& address : a)
{
if (!address->host.is_v6 ())
CloseMapping (address);
}
}
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int r;
std::string strDesc = "I2Pd";
try {
for (;;) {
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
if (r!=UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: AddPortMapping (", strPort.c_str () ,", ", strPort.c_str () ,", ", m_NetworkAddr, ") failed with code ", r);
return;
}
else
{
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", strPort.c_str() ,")");
return;
}
std::this_thread::sleep_for(std::chrono::minutes(20)); // c++11
//boost::this_thread::sleep_for(); // pre c++11
//sleep(20*60); // non-portable
}
}
catch (boost::thread_interrupted)
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
if (r!=UPNPCOMMAND_SUCCESS)
{
CloseMapping(type, port);
Close();
throw;
LogPrint (eLogError, "UPnP: AddPortMapping (", m_NetworkAddr, ":", strPort, ") failed with code ", r);
return;
}
else
{
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort, " type ", strType, " -> ", m_externalIPAddress ,":", strPort ,")");
return;
}
}
void UPnP::CloseMapping (int type, int port)
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
std::string strType, strPort (std::to_string (port));
switch (type)
{
case I2P_UPNP_TCP:
strType = "TCP";
break;
case I2P_UPNP_UDP:
default:
strType = "UDP";
}
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int r = 0;
r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r, "\n");
r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r);
}
void UPnP::Close ()
{
freeUPNPDevlistFunc (m_Devlist);
freeUPNPDevlist (m_Devlist);
m_Devlist = 0;
FreeUPNPUrlsFunc (&m_upnpUrls);
#ifndef _WIN32
dlclose (m_Module);
#else
FreeLibrary (m_Module);
#endif
FreeUPNPUrls (&m_upnpUrls);
}
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
switch (address->transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
return "TCP";
break;
case i2p::data::RouterInfo::eTransportSSU:
default:
return "UDP";
}
}
}
}
#endif
#else /* USE_UPNP */
namespace i2p {
namespace transport {
}
}
#endif /* USE_UPNP */

49
UPnP.h
View File

@@ -4,6 +4,9 @@
#ifdef USE_UPNP
#include <string>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <memory>
#include <miniupnpc/miniwget.h>
#include <miniupnpc/miniupnpc.h>
@@ -12,11 +15,6 @@
#include <boost/asio.hpp>
#include "util.h"
#define I2P_UPNP_TCP 1
#define I2P_UPNP_UDP 2
namespace i2p
{
namespace transport
@@ -32,13 +30,25 @@ namespace transport
void Start ();
void Stop ();
void Discover ();
void TryPortMapping (int type, int port);
void CloseMapping (int type, int port);
private:
void Run ();
std::thread * m_Thread;
void Discover ();
void PortMapping ();
void TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
void CloseMapping ();
void CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
void Run ();
std::string GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address);
private:
bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
std::condition_variable m_Started;
std::mutex m_StartedMutex;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
struct UPNPUrls m_upnpUrls;
struct IGDdatas m_upnpData;
@@ -48,15 +58,22 @@ namespace transport
struct UPNPDev * m_Devlist = 0;
char m_NetworkAddr[64];
char m_externalIPAddress[40];
bool m_IsModuleLoaded;
#ifndef _WIN32
void *m_Module;
#else
HINSTANCE m_Module;
#endif
};
}
}
#else // USE_UPNP
namespace i2p {
namespace transport {
/* class stub */
class UPnP {
public:
UPnP () {};
~UPnP () {};
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
void Stop () {};
};
}
}
#endif // USE_UPNP
#endif // __UPNP_H__

View File

@@ -2,6 +2,7 @@
#include <windows.h>
#include <shellapi.h>
#include "../Config.h"
#include "../RouterContext.h"
#include "../version.h"
#include "resource.h"
#include "Win32App.h"
@@ -15,10 +16,13 @@
#define ID_EXIT 2001
#define ID_CONSOLE 2002
#define ID_APP 2003
#define ID_GRACEFUL_SHUTDOWN 2004
#define ID_TRAY_ICON 2050
#define WM_TRAYICON (WM_USER + 1)
#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
namespace i2p
{
namespace win32
@@ -30,6 +34,7 @@ namespace win32
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL);
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);
@@ -82,6 +87,7 @@ namespace win32
case WM_CLOSE:
{
RemoveTrayIcon (hWnd);
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
PostQuitMessage (0);
break;
}
@@ -101,6 +107,12 @@ namespace win32
PostMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
case ID_GRACEFUL_SHUTDOWN:
{
i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
return 0;
}
case ID_CONSOLE:
{
char buf[30];
@@ -167,6 +179,15 @@ namespace win32
}
break;
}
case WM_TIMER:
{
if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
{
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
return 0;
}
break;
}
}
return DefWindowProc( hWnd, uMsg, wParam, lParam);
}
@@ -218,5 +239,13 @@ namespace win32
{
UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
}
bool GracefulShutdown ()
{
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
if (hWnd)
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
return hWnd;
}
}
}

View File

@@ -10,6 +10,7 @@ namespace win32
bool StartWin32App ();
void StopWin32App ();
int RunWin32App ();
bool GracefulShutdown ();
}
}
#endif // WIN32APP_H__

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