Compare commits

..

1963 Commits

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

Daemon.cpp:
* wait for 1 second before checking if transports are bound to wait for transports to bind
2016-06-30 13:25:20 -04:00
orignal
eab08ea78c don't accept our own RouterInfo 2016-06-30 13:15:36 -04:00
Jeff Becker
d7653769b4 more logging 2016-06-30 12:57:20 -04:00
Jeff Becker
c72d9695da testnet changes 2016-06-30 12:27:23 -04:00
orignal
0493f00a7a pass null tunnel config for zero hops tunnel 2016-06-30 12:24:26 -04:00
Jeff Becker
ff757ddc88 don't check for exact LS 2016-06-30 11:27:40 -04:00
Jeff Becker
c4c495948a don't flood if older 2016-06-30 11:10:01 -04:00
Jeff Becker
986ee6bac3 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 11:03:15 -04:00
orignal
6ab7e79987 send own copy of a message during flood 2016-06-30 11:01:21 -04:00
Jeff Becker
c6e35876fa if LS is older, assume updated so we reply 2016-06-30 10:51:39 -04:00
Jeff Becker
eb31accf20 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 10:25:44 -04:00
orignal
ea1ba0f09b don't flood to itself 2016-06-30 10:21:53 -04:00
Jeff Becker
10911f5b64 make it compile 2016-06-30 09:52:37 -04:00
Jeff Becker
0eab8e9322 more pedantic logging changes 2016-06-30 09:49:28 -04:00
Jeff Becker
fb2602716e add floodfill to exclude 2016-06-30 09:48:08 -04:00
Jeff Becker
be68906ae9 logging tweaks 2016-06-30 09:45:06 -04:00
Jeff Becker
c53d6d80ba Merge branch 'master' into meshnet 2016-06-30 09:15:30 -04:00
Jeff Becker
1362177352 Merge remote-tracking branch 'purple/openssl' 2016-06-30 08:57:59 -04:00
xcps
28ab1230e2 '@' can exist in url path 2016-06-30 08:00:18 -04:00
orignal
0784934520 Merge pull request #551 from majestrate/http-router-ident
add RouterInfo hash to web ui
2016-06-30 07:46:28 -04:00
Jeff Becker
79fbf9d47f add RouterInfo hash to web ui 2016-06-30 07:41:40 -04:00
Jeff Becker
dabdc8e4c5 Merge branch 'upstream-openssl' 2016-06-30 07:40:15 -04:00
Jeff Becker
466ad192b0 add RouterInfo hash to web ui 2016-06-30 07:36:35 -04:00
Jeff Becker
ac59d9e1b6 Merge branch 'upstream-openssl' 2016-06-30 07:36:14 -04:00
Jeff Becker
d44245e1e9 add RouterInfo hash to web ui 2016-06-30 07:35:30 -04:00
orignal
c50105493a fixed zero-hops tunnels 2016-06-29 21:37:17 -04:00
Jeff Becker
2768a62f92 more logging 2016-06-29 18:05:08 -04:00
Jeff Becker
5841d0d87d add more specific logging 2016-06-29 17:59:56 -04:00
Jeff Becker
2a796051bf try fixing LS 2016-06-29 17:42:26 -04:00
Jeff Becker
50286fd173 use inet_ntop properly 2016-06-29 16:10:43 -04:00
Jeff Becker
44f0bad2a6 fug 2016-06-29 15:52:32 -04:00
Jeff Becker
611b9c4fd1 ugh 2016-06-29 15:50:39 -04:00
Jeff Becker
3f50776062 ugh 2016-06-29 15:48:02 -04:00
Jeff Becker
c70d2ad6fa try fixing 2016-06-29 15:42:03 -04:00
Jeff Becker
9dd0bd604c fix 2016-06-29 15:37:37 -04:00
Jeff Becker
0565519509 don't print out raw ident 2016-06-29 15:15:33 -04:00
Jeff Becker
c282d95be1 undo change 2016-06-29 15:10:33 -04:00
Jeff Becker
766286b8bc undo pedantic change 2016-06-29 14:57:42 -04:00
Jeff Becker
f405c62f1e pedantic style and logging changes 2016-06-29 14:56:00 -04:00
Jeff Becker
93fbd7b3ba Merge branch 'upstream-openssl' into meshnet 2016-06-29 13:32:39 -04:00
Jeff Becker
ae5cea7f36 change order of initialization 2016-06-29 11:59:43 -04:00
Jeff Becker
8b7b6cfbc5 try fixing segfault 2016-06-29 11:57:44 -04:00
orignal
f6e988d6fd support zero-hops tunnels for destinations 2016-06-29 11:26:46 -04:00
Jeff Becker
f88f68f248 Add bind to network interface option 2016-06-29 11:06:51 -04:00
Jeff Becker
14f2b24b16 update logging 2016-06-29 10:11:14 -04:00
Jeff Becker
f2dde98e2f add meshnet option to cmake build files 2016-06-29 09:37:38 -04:00
Jeff Becker
90d8ec0e81 add more logging 2016-06-29 09:37:21 -04:00
hagen
14cdb531c8 * Streaming.cpp : tune logs 2016-06-29 02:14:39 +00:00
Jeff Becker
3a50320f79 add more logging 2016-06-28 15:35:58 -04:00
Jeff Becker
597b5e6cfb use different constants for now in meshnet mode 2016-06-28 14:50:25 -04:00
Jeff Becker
2cd056cfb3 try banning non responsive routers 2016-06-28 14:43:55 -04:00
Jeff Becker
be6aab4c40 revert 2016-06-28 14:24:18 -04:00
Jeff Becker
d030df925f Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-28 14:21:09 -04:00
Jeff Becker
50756eb94a Merge branch 'restricted_routes' into meshnet 2016-06-28 13:28:57 -04:00
Jeff Becker
21b3576b66 stop using auto 2016-06-28 12:20:18 -04:00
hagen
2e5226356b * Tag.h : add (c) header 2016-06-28 14:31:40 +00:00
hagen
21b76d3d2b * Config.cpp : drop compat parser 2016-06-28 14:31:40 +00:00
hagen
2d252e6459 * HTTP.cpp : rename method 2016-06-28 14:31:40 +00:00
Jeff Becker
43be363542 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-28 09:34:38 -04:00
orignal
8cb3e3418a send garlic cloves directly if garlic was received derectly 2016-06-28 09:31:41 -04:00
Jeff Becker
cd47ddd539 default to USE_MESHNET=yes 2016-06-28 07:57:48 -04:00
orignal
2be1c10522 Tag.h and Gzip.h/.cpp added 2016-06-28 07:11:55 -04:00
hagen
7f26670173 + tests/test-base-64.cpp 2016-06-28 01:45:14 +00:00
hagen
d838ce85c3 * Base.h : extract Tag template class to separate header 2016-06-28 01:45:11 +00:00
hagen
6350f5e6e8 * Base.cpp : extract gzip classes to separate file 2016-06-28 01:45:07 +00:00
orignal
10f3690ede Merge pull request #547 from atnaguzin/openssl
fixed #546
2016-06-27 20:30:31 -04:00
Jeff Becker
06daa8bb0e try shooting in the dark for workarround 2016-06-27 17:39:13 -04:00
Jeff Becker
34a90f442e try shooting in the dark for workarround 2016-06-27 17:37:31 -04:00
Jeff Becker
afe81dcdbe add logging 2016-06-27 17:25:29 -04:00
Jeff Becker
24d616672b revert daemon.cpp change 2016-06-27 17:11:03 -04:00
MXPLRS|Kirill
9cfc61cd45 fixed #546 2016-06-28 00:00:54 +03:00
MXPLRS | Kirill
487df84b90 Merge pull request #3 from PurpleI2P/openssl
Upstream pull
2016-06-27 23:54:53 +03:00
Jeff Becker
614c1306f6 use correct netid when using separate test network 2016-06-27 16:40:46 -04:00
Jeff Becker
07dca9bd16 tweak ssu mtu again for meshnet 2016-06-27 15:28:26 -04:00
Jeff Becker
1ebcbd5b0e use smaller mtu for meshnet mode 2016-06-27 15:06:15 -04:00
Jeff Becker
32644ddada try fixing duplicate Routers In tunnel path 2016-06-27 14:16:29 -04:00
Jeff Becker
cf3bab996e when routers < 5 and in meshnet mode do not select random peers 2016-06-27 14:00:04 -04:00
Jeff Becker
926ffe2581 change default addressbook for meshnet mode 2016-06-27 13:46:14 -04:00
Jeff Becker
fff3587d99 only set as testing when not in meshnet mode 2016-06-27 13:20:21 -04:00
Jeff Becker
866cf940da make always reachable when in meshnet mode 2016-06-27 13:15:05 -04:00
Jeff Becker
7868e1527e try fixing ipv6 ssu 2016-06-27 11:08:23 -04:00
Jeff Becker
556bfb752a disable meshnet by default, use make USE_MESHNET=yes to build for cjdns 2016-06-27 10:33:14 -04:00
Jeff Becker
a3b08654b4 try adding ipv6 only mode for ssu 2016-06-27 10:24:37 -04:00
Jeff Becker
4cf4436169 initial meshnet mode 2016-06-27 09:47:53 -04:00
hagen
e2acc55819 * HTTPProxy.cpp : unwrap HandleStreamRequestComplete() 2016-06-27 13:14:42 +00:00
hagen
6b29d6b8dc * HTTPProxy.cpp : unwrap AsyncSockRead() 2016-06-27 13:14:39 +00:00
hagen
b668c4c302 * add global switch USE_UPNP to makefile 2016-06-27 13:12:01 +00:00
hagen
646778227a * tune log messages 2016-06-27 13:11:10 +00:00
hagen
a973630cb4 * fix tests 2016-06-27 13:11:10 +00:00
hagen
4e7375c09c * Addressbook.cpp : move storage creation to Start() 2016-06-27 13:11:10 +00:00
hagen
881d0652e7 * update debian package defaults 2016-06-27 13:11:10 +00:00
orignal
0c46993baa Merge pull request #544 from atnaguzin/openssl
Updating i2pd_qt.pro
2016-06-27 06:54:49 -04:00
MXPLRS|Kirill
09b15f4940 edited i2pd_qt.pro 2016-06-27 05:55:07 +03:00
MXPLRS | Kirill
ebf7be56bb Merge pull request #2 from PurpleI2P/openssl
upstream pull
2016-06-27 05:52:56 +03:00
hagen
727068cc4b * HTTPProxy.cpp : migrate HTTPRequestFailed() to new http classes 2016-06-27 02:37:33 +00:00
hagen
6f77c6f3f4 * HTTPProxy.cpp : don't reuse part of httppserver, addresshelpers handling will be moved to proxy in future 2016-06-27 02:37:32 +00:00
hagen
c5e3e17eae * HTTPProxy.cpp : extract IsI2PAddress() from class 2016-06-27 02:37:30 +00:00
Jeff Becker
effdb70417 Merge branch 'upstream-openssl' into restricted_routes 2016-06-26 21:53:13 -04:00
hagen
e28f910c88 * enable travis for UPNP=ON back 2016-06-27 01:30:02 +00:00
hagen
c84468dbed * fix cmake build with upnp=on 2016-06-27 01:12:20 +00:00
orignal
4b9e39ac64 limit SSU outgoing windows 2016-06-26 17:03:04 -04:00
orignal
6b0c05ee7c Merge pull request #542 from majestrate/fix-rpi-static
fix static build for rpi linux
2016-06-26 13:31:14 -04:00
Jeff Becker
5c6ec70126 fix static build for rpi linux 2016-06-26 11:17:05 -04:00
orignal
2757ef94c9 don't include UPNP twice for android 2016-06-26 08:37:40 -04:00
orignal
9ba7120011 fixed build error 2016-06-26 08:32:36 -04:00
MXPLRS | Kirill
124f9ec44e Merge pull request #1 from PurpleI2P/openssl
upstream pull
2016-06-26 15:29:43 +03:00
orignal
0d229175cd Merge pull request #539 from hypnosis-i2p/openssl
added tray icon to linux and windows versions
2016-06-25 21:01:54 -04:00
hypnosis-i2p
10638b6e40 fixed unnecessary resources setting 2016-06-26 02:48:13 +08:00
hypnosis-i2p
134baad56d added tray icon to linux and windows versions 2016-06-26 02:32:54 +08:00
Jeff Becker
9a5984d750 Merge branch 'upstream-openssl' into restricted_routes 2016-06-25 10:55:22 -04:00
Jeff Becker
073f42e64a Merge branch 'upstream-openssl' into restricted_routes 2016-06-25 10:54:41 -04:00
orignal
096927beed don't sedn explicit Ack if no NACKs only 2016-06-24 21:54:58 -04:00
xcps
4bc76995d1 docs: default httpproxy.port changed to actual 4444 2016-06-24 19:29:59 -04:00
xcps
9f41151156 HTTP proxy redirects to 0.0.0.0:7070/?page=jumpservices 2016-06-24 19:25:48 -04:00
xcps
6b3bd755b0 fixtypo 2016-06-24 19:07:47 -04:00
xcps
7e580e6a0b Update HTTPServer.cpp 2016-06-24 17:58:46 -04:00
orignal
047c8eda22 stop accepting tunnels by graceful shutdown 2016-06-24 16:26:13 -04:00
orignal
f22e5c209c fixed QT linux build 2016-06-24 16:05:03 -04:00
orignal
8cc3a08871 Merge pull request #536 from hypnosis-i2p/openssl
Added graceful quit button; the code for stopping tunnels pending
2016-06-24 15:43:12 -04:00
hypnosis-i2p
35f6c6cb98 graceful quit button added 2016-06-25 03:38:50 +08:00
hypnosis-i2p
814b174f25 android version code bump 2016-06-25 03:38:50 +08:00
orignal
5fbaf0bc7d disabled UPNP=ON 2016-06-24 15:29:36 -04:00
orignal
ba772ab481 static miniupnpc 2016-06-24 14:20:35 -04:00
orignal
0a4888a18f link with miniupnp 2016-06-24 14:18:50 -04:00
orignal
fedbf2cc44 link UPnP with app if USE_UPNP is set 2016-06-24 13:15:51 -04:00
orignal
0f68bbac8e single #ifdef for protocol type 2016-06-23 14:01:41 -04:00
orignal
13e965096b UPnP for android 2016-06-23 12:57:36 -04:00
orignal
92961bb7bf i2cp for android 2016-06-23 11:23:06 -04:00
hagen
340686ba06 * HTTPProxy.{cpp,h} : rename classes, drop typedef 2016-06-23 13:24:44 +00:00
hagen
d8906f508c * HTTPProxy.cpp : HTTP error message cleanup 2016-06-23 13:24:44 +00:00
hagen
dde53ea4ba * HTTPProxy.cpp : HTTPRequestFailed() now responds with error message 2016-06-23 13:24:44 +00:00
hagen
225ed5b662 * HTTPProxy.{cpp,h} : move & sort headers 2016-06-23 13:24:44 +00:00
hagen
02857cf2b5 * Base.cpp : drop logger dependency 2016-06-23 13:24:44 +00:00
hagen
118a771980 * update changelog 2016-06-23 13:24:37 +00:00
orignal
359123564f Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-06-22 15:49:02 -04:00
orignal
49d2878938 use local sockets for android 2016-06-22 15:48:36 -04:00
orignal
f08ea4a9a3 Merge pull request #535 from PurpleI2P/openssl
2.8.0
2016-06-22 15:47:22 -04:00
Jeff Becker
f2bc71bc2a Merge branch 'upstream-openssl' 2016-06-22 13:19:00 -04:00
orignal
2b1e40c6c6 Update build_notes_windows.md 2016-06-22 11:15:40 -04:00
orignal
105c2deeb3 Merge pull request #534 from atnaguzin/patch-2
markup update
2016-06-22 11:06:44 -04:00
MXPLRS | Kirill
c3dbbc9144 Update build_notes_windows.md 2016-06-22 18:04:11 +03:00
orignal
ca55a9a8a6 build with UPnP 2016-06-22 10:53:29 -04:00
orignal
7ea5af448e UPNP support from windows 2016-06-22 09:41:01 -04:00
orignal
2f6898142e Merge pull request #533 from atnaguzin/openssl
updated config files
2016-06-22 07:13:23 -04:00
MXPLRS | Kirill
02e0b8cc32 Update i2pd.conf 2016-06-22 11:49:22 +03:00
MXPLRS|Kirill
b962a4c69b updated config files 2016-06-22 11:46:37 +03:00
orignal
ed3e83df67 android is supported now 2016-06-21 12:34:20 -04:00
orignal
b07bff61f0 set target SDK to 23 2016-06-21 09:09:31 -04:00
Jeff Becker
a7c955055c Merge branch 'upstream-openssl' into restricted_routes 2016-06-21 08:15:26 -04:00
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
90ea714e48 version 2.7.0 2016-05-18 09:22:48 -04:00
orignal
f9e4182624 temporary disable 'reload config' item 2016-05-18 09:22:11 -04:00
orignal
0291cc2ef4 Merge pull request #493 from PurpleI2P/openssl
recent changes
2016-05-17 15:06:39 -04:00
orignal
caf2e469a6 remove mascot 2016-05-17 12:35:08 -04:00
Jeff
45da2843ee Merge pull request #492 from weekendi2p/openssl
fix jumpservices uri
2016-05-16 20:10:51 -04:00
weekendi2p
8353f928a1 fix jumpservices 2016-05-17 01:42:58 +02:00
Jeff Becker
9c1a6d042e Merge remote-tracking branch 'purple/openssl' 2016-05-16 16:04:48 -04:00
orignal
448b25a8b2 receive I2CP messages 2016-05-13 15:13:36 -04:00
orignal
4c2d4009da handle protocol byte 2016-05-12 16:17:10 -04:00
orignal
67f1e07508 I2CP added 2016-05-12 15:37:46 -04:00
orignal
c49fdf1233 initial commit for reload config command 2016-05-12 11:38:18 -04:00
orignal
7c835bae20 changed back to <openssl/ 2016-05-11 16:02:26 -04:00
orignal
ae81cc2644 windows doesn't support graceful shutdown yet 2016-05-11 15:33:53 -04:00
orignal
3907b4101a include openssl through OPENSSL macro 2016-05-11 15:12:38 -04:00
orignal
aa5ea0e3a1 support gcc 6 2016-05-11 11:57:02 -04:00
Jeff
d21043802e Merge branch 'upstream' 2016-05-11 10:02:49 -04:00
orignal
995bdb3f9e Merge pull request #490 from majestrate/pr-fix-http-unit-tests-osx
add missing header to unbreak build
2016-05-11 09:36:27 -04:00
Jeff
8363b4fda7 add missing header 2016-05-11 09:33:25 -04:00
orignal
23979f4ce6 Merge pull request #489 from majestrate/pr-fix-http-unit-tests-osx
fix http unit test SIGBUS in os x
2016-05-11 09:06:29 -04:00
Jeff
28b5f39b84 fix http unit test SIGBUS in os x 2016-05-11 08:42:50 -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
orignal
aa215f2a5a regular/homebrew build selection for Mac OS X 2016-05-11 07:08:02 -04:00
hagen
b03a6a5327 Merge branch 'new-webconsole' into openssl
+ new http/url classes
* extract most page/cmd handlers from HTTPConnection class
* general cleanup of HTTPServer.{cpp,h}
+ http basic auth for webconsole
+ gracefull/quick shutdown commands
- drop direct access for b32 addresses from webconsole: use proxy instead
2016-05-11 01:29:47 +00:00
orignal
ca36a6fe41 update our IP after signture verification 2016-05-10 15:55:48 -04:00
orignal
bd6285c8b1 Merge pull request #487 from majestrate/fix-mac-build
fix mac build
2016-05-09 20:28:25 -04:00
Jeff
00cfdc7d92 fix mac brew, use libressl and homebrew 2016-05-04 12:12:24 -04:00
orignal
5e2dc14dd5 get family string from local RouterInfo 2016-04-28 18:16:11 -04:00
orignal
c5f2890cbe Merge pull request #484 from weekendi2p/openssl
Show family name in web interface
2016-04-28 17:30:56 -04:00
weekendi2p
36aaca997a Merge pull request #1 from PurpleI2P/openssl
sync
2016-04-28 20:21:27 +00:00
orignal
e9f7c61113 Merge pull request #483 from majestrate/fix-issue-482
try fixing issue #482
2016-04-27 12:23:15 -04:00
Jeff Becker
2373b94d3e try fixing issue #482 2016-04-27 12:08:08 -04:00
hagen
f131e31949 * HTTPServer.cpp: add request logging 2016-04-27 00:39:34 +00:00
hagen
8fd55a210a * HTTPServer.cpp: add 'Shutdown' commands 2016-04-27 00:39:34 +00:00
hagen
678650beaf * HTTPServer.{cpp,h}: basic auth 2016-04-27 00:39:34 +00:00
hagen
e09386be44 * add http.auth, http.user & http.pass options 2016-04-27 00:39:34 +00:00
hagen
75db2867dc * HTTPServer.cpp: protect SAM pages if disabled 2016-04-27 00:39:34 +00:00
hagen
80e37df012 * HTTPServer.{cpp,h}: change page/cmd processing flow 2016-04-27 00:39:33 +00:00
hagen
1f404bb622 * HTTPServer.cpp: move html parts outside HTTPConnection class 2016-04-27 00:39:33 +00:00
hagen
54078087e5 * HTTPServer.cpp: move common code to function 2016-04-27 00:39:33 +00:00
hagen
23b8df1c36 * HTTPServer.cpp: move commands to separate page 2016-04-27 00:39:33 +00:00
hagen
65395516b0 * HTTPServer.cpp: drop separate function handlers for commands 2016-04-27 00:39:33 +00:00
hagen
849308e28d * HTTPServer.cpp: drop boost::date_time dep 2016-04-27 00:39:24 +00:00
hagen
4d98a64000 * HTTPServer.{cpp,h}: extract html-rendering methods from class 2016-04-27 00:39:24 +00:00
hagen
0c8fdfca7d * HTTPServer.{cpp,h}: merge HandleWriteReply & Terminate : the same purpose 2016-04-27 00:39:24 +00:00
hagen
fd928e8d12 * HTTPServer.h: not virtual: not inherited anywhere 2016-04-27 00:39:24 +00:00
hagen
2a1fe99a29 * HTTPServer.{cpp,h}: drop rest of streaming support 2016-04-27 00:39:24 +00:00
hagen
4fa4ba6301 * HTTPServer.cpp: move known jump services to std::map 2016-04-27 00:39:24 +00:00
hagen
48b3959cfb * HTTPServer.{cpp,h}: cleanup 2016-04-27 00:39:24 +00:00
hagen
9bbff744e9 * HTTPServer.{cpp,h}: chg HandleRequest() signature 2016-04-27 00:39:24 +00:00
hagen
19b0c266f9 * HTTPServer.{cpp,h}: * extract css-styles to separate block * split /?page= from /?cmd= requests * cleaner html-template 2016-04-27 00:39:24 +00:00
hagen
fead940d10 * HTTPServer.{cpp,h}: * move query parsing code to one place * use /?cmd=X instead /?X * unified handler signatures 2016-04-27 00:39:24 +00:00
hagen
687e17ac52 * HTTPServer.{cpp,h}: throw away direct quering b32 addresses: use proxy 2016-04-27 00:39:12 +00:00
hagen
b1c85dcb74 * HTTPServer.{cpp,h}: throw away request/reply/url, use new impl 2016-04-27 00:39:12 +00:00
hagen
a15aad9f9c * unit-tests 2016-04-27 00:39:12 +00:00
hagen
06a1a8690d * add HTTP.{cpp,h} to build 2016-04-27 00:39:12 +00:00
hagen
42b9b6426a + new http req/res/url structs 2016-04-27 00:39:12 +00:00
hagen
332f0118a2 * rename namespace for http 2016-04-27 00:39:12 +00:00
hagen
6ed709d6e6 * HTTPServer.{cpp,h}: extract itoopie{Image,Favicon} from HTTPConnection (!) class 2016-04-27 00:39:12 +00:00
hagen
7a461c1684 * HTTPServer.{cpp,h}: move #include to one place 2016-04-27 00:39:12 +00:00
hagen
7cf171671d * HTTPConnection::reply : to_buffers() -> to_string() 2016-04-27 00:39:12 +00:00
weekendi2p
ebee94fb11 removed 1 blank line.. 2016-04-27 01:19:27 +02:00
weekendi2p
61e8becd38 wrong file version 2016-04-27 00:48:23 +02:00
weekendi2p
a78caa2976 added SetFamilyString(); GetFamilyString() and shows family in webiface 2016-04-27 00:31:33 +02:00
orignal
c54f7c81c4 Merge pull request #480 from weekendi2p/openssl
new family: volatile
2016-04-26 13:06:58 -04:00
weekendi2p
85840872ab family: volatile.crt 2016-04-26 19:39:10 +02:00
orignal
d582c30f6e allow same port at different interfaces 2016-04-24 17:32:24 -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
4431d50635 limits.transittunnels 2016-04-20 15:02:11 -04:00
orignal
e120e9a78e configurable transit tunnels limit 2016-04-20 14:53:50 -04:00
orignal
b6e379d14e Merge pull request #475 from weekendi2p/openssl
added limits.transittunnels
2016-04-20 13:32:17 -04:00
weekendi2p
9a86034162 limits options 2016-04-20 19:24:50 +02:00
weekendi2p
8456c8b47b limits options 2016-04-20 19:22:04 +02:00
weekendi2p
bb656ce44b added some limits options 2016-04-20 19:12:14 +02:00
orignal
3c2a3898e1 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-04-18 21:08:26 -04:00
orignal
c265bd6c4d delete pre-calculated tablle upon termination 2016-04-18 21:07:45 -04:00
orignal
aff65083cc precomputation.elgamal 2016-04-17 17:03:56 -04:00
orignal
aff8cd478c optional elgamal precomputation for x64 2016-04-17 16:57:58 -04:00
orignal
bce2a63772 rollback some changes 2016-04-14 14:05:25 -04:00
hagen
3f9d2601b4 + HTTPConnection::SendError() 2016-04-14 00:34:15 +00:00
hagen
04bfd52fba * HTTPConnection::SendReply() : cleaner code 2016-04-14 00:34:14 +00:00
hagen
87dd890eb0 * HTTPConnection::reply : to_buffers() -> to_string() 2016-04-14 00:34:13 +00:00
hagen
a5c0b48b57 * HandleDestinationRequestTimeout() : readable code 2016-04-14 00:34:11 +00:00
hagen
5d38693b4d * HTTPServer : fold namespace to two constants 2016-04-14 00:34:10 +00:00
hagen
a4773d259d * use std::to_string() instead boost's function 2016-04-14 00:34:08 +00:00
orignal
f9b6b1bf76 Merge pull request #473 from PurpleI2P/openssl
precalculate elgamal
2016-04-13 15:10:55 -04:00
orignal
ef106f3232 fixed typo 2016-04-13 11:22:08 -04:00
orignal
c0b0df34d2 clean montgomery context 2016-04-12 19:07:11 -04:00
orignal
d15cc7cc47 changed tray icon back to ictoopie 2016-04-11 12:39:32 -04:00
orignal
6336d38a3e Removed downloads. Added Docimentation 2016-04-11 12:04:15 -04:00
orignal
6a9d2ba653 use precalculated table for DH 2016-04-10 21:16:18 -04:00
orignal
34a8d4a57d use precalculated table for ElGamal encryption 2016-04-10 17:06:02 -04:00
orignal
ffc666eaaa g^x mod p using precalculated table 2016-04-09 22:44:13 -04:00
orignal
c45aab7cef precalculate g^x mod p table 2016-04-08 15:45:23 -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
2ebb2d8f0e fixed race condition 2016-04-06 21:02:58 -04:00
xcps
afe2935c9d webconsole update 2016-04-06 16:33:23 -04:00
orignal
380c7b7720 use 226 bits private keys for non-x64 2016-04-06 16:11:18 -04:00
orignal
8657226594 use 226 bits private keys for non-x64 2016-04-06 15:49:46 -04:00
orignal
41da48f5ff Merge pull request #468 from PurpleI2P/openssl
recent changes
2016-04-06 12:27:15 -04:00
orignal
405aa906c5 short exponent for non-x64 2016-04-05 13:18:25 -04:00
orignal
1c507a47d2 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-04-05 10:22:35 -04:00
orignal
f48a7df80f recreate router.info if missing or malformed 2016-04-05 10:22:32 -04:00
hagen
5f73f09836 * RouterInfo::SaveToFile() now returns bool 2016-04-05 11:37:39 +00:00
hagen
f63dd75f08 * set bw limits thru i2pcontrol (#461) (experimental) 2016-04-05 11:37:25 +00:00
hagen
cc55335a8d * docs/configuration.md 2016-04-05 11:08:07 +00:00
hagen
b5875f3a0a * update year in copyrights 2016-04-05 11:08:05 +00:00
hagen
cb8333a48f * update manpage 2016-04-05 11:08:02 +00:00
hagen
f412f4ca88 * use commented i2pd.conf as default 2016-04-05 11:08:00 +00:00
orignal
941f30d1ea show streams from all streaming destinations 2016-04-04 22:17:04 -04:00
orignal
97afa502c5 shard_ptr for SAMSession 2016-04-02 22:16:49 -04:00
Jeff Becker
9ae9ea18e1 Merge remote-tracking branch 'purple/openssl' 2016-04-02 10:13:53 -04:00
orignal
0bf2abaa4c fixed race condition at startup 2016-04-02 08:57:35 -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
1fc5dacd87 Merge pull request #464 from majestrate/master
Add locking to SAM
2016-04-01 12:56:30 -04:00
orignal
5c877de2c2 check if hosts is incomplete 2016-04-01 12:51:34 -04:00
Jeff Becker
751b95d4af add locking to SAM when adding/removing sockets 2016-04-01 11:36:56 -04:00
Jeff Becker
e0d5ba9915 Merge tag 'tags/2.6.0' 2016-04-01 10:57:22 -04:00
orignal
b8a6946661 Merge pull request #462 from PurpleI2P/openssl
2.6.0
2016-03-31 10:34:08 -04:00
orignal
e5fac08d1d release 2.6.0 2016-03-31 09:12:21 -04:00
orignal
df5b7c7d0d specify bandwidth for floodfill 2016-03-30 21:31:17 -04:00
hagen
27649f7d4c * update docs 2016-03-31 00:18:54 +00:00
hagen
350dea6228 * update --bandwidth option handling 2016-03-31 00:18:52 +00:00
hagen
aef6b7712c * Transports: update IsBandwidthExceeded() and comments in header 2016-03-31 00:18:49 +00:00
hagen
642bcfcdea * RouterContext : replace Set(Low|High|Extra)Bandwidth with SetBandwidth() 2016-03-31 00:18:46 +00:00
hagen
e625d8aabc * RouterInfo.cpp : remove .c_str() 2016-03-31 00:18:44 +00:00
hagen
5888ecbdcd * RouterInfo::UpdateCapsProperty() : add only one bw letter 2016-03-31 00:18:42 +00:00
hagen
e2a76056b8 * RouterInfo.h : add comments with bandwidth letter speed in KBps 2016-03-31 00:18:40 +00:00
Jeff Becker
a98498eb06 Merge remote-tracking branch 'purple/openssl' 2016-03-30 07:17:03 -04:00
orignal
8366c8d2a7 don't initiate graceful shutdown twice 2016-03-29 21:37:30 -04:00
Mikal
ed8d441a02 Merge pull request #459 from PurpleI2P/add_exceptions
Adding exceptions on clients
2016-03-30 00:08:52 +02:00
Mikal Villa
f1fb265119 Adding exceptions for SOCKS, SAM and BOB proxy/briges 2016-03-30 00:03:15 +02:00
Mikal Villa
6c628094ce Adding exception handler on HTTP Proxy 2016-03-29 23:55:29 +02:00
Mikal
a60c52e2f0 Merge pull request #454 from PurpleI2P/Travis_fixes
Adding working Travis build config for Linux Clang + OS X Clang / GCC
2016-03-29 19:25:23 +02:00
orignal
ac2e1709f8 graceful shutdown by SIGINT 2016-03-29 12:50:34 -04:00
orignal
db88183a23 graceful shutdown by SIGINT 2016-03-29 12:34:53 -04:00
Mikal Villa
c7d55ad858 Trying to fix broken builds on 10.7. Works fine on local 10.11 2016-03-29 16:01:07 +02:00
Mikal Villa
06a4e6c323 OSX worker already got boost and cmake installed 2016-03-29 15:30:06 +02:00
orignal
d1de89f387 Merge pull request #455 from PurpleI2P/openssl
recent changes
2016-03-29 09:20:54 -04:00
Mikal Villa
bbba01da92 OSX travis update (Test) 2016-03-29 15:09:40 +02:00
orignal
25dbf62274 Merge pull request #453 from manasb/openssl
update mca2-i2p.crt for correct CN
2016-03-28 18:17:02 -04:00
Manas Bhatnagar
ed6851863b update mca2-i2p.crt 2016-03-28 18:08:11 -04:00
orignal
ba924e295e Merge pull request #452 from manasb/openssl
Rename i2p.crt to mca2-i2p.crt
2016-03-28 17:56:39 -04:00
Manas Bhatnagar
0828065a62 Rename i2p.crt to mca2-i2p.crt 2016-03-28 17:51:24 -04:00
orignal
68c789dceb Merge pull request #451 from majestrate/master
fix failed build on freebsd 10.1
2016-03-28 17:36:25 -04:00
orignal
6424084502 Merge pull request #450 from manasb/openssl
i2p family cert file
2016-03-28 17:35:44 -04:00
Jeff Becker
4abea18afe Merge remote-tracking branch 'purple/openssl' 2016-03-28 17:16:05 -04:00
Jeff Becker
0a3c4f131e fix issue #449 failed build of freebsd 10.1 2016-03-28 17:15:27 -04:00
Manas Bhatnagar
f5e1077e20 i2p family cert file 2016-03-28 17:05:39 -04:00
xcps
44d1c3fd2f Merge pull request #448 from xcps/openssl
default subscription address to inr, jump services b32
2016-03-28 14:42:21 -04:00
xcps
e345161763 default subscription address to inr, jump services b32 2016-03-28 14:33:55 -04:00
Jeff Becker
64d7c87591 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd 2016-03-28 12:26:09 -04:00
hagen
1fae3baaa3 * logger: print also thread id 2016-03-28 14:44:56 +00:00
hagen
38103aaac5 * logger: explicit allow log output 2016-03-28 14:44:54 +00:00
hagen
cc25b22f11 * update docs 2016-03-28 11:36:07 +00:00
hagen
e6dbeda18e * rename tunnels.conf -> tunnels.conf, add detection of old config 2016-03-28 11:36:05 +00:00
hagen
8437d45866 * rename i2p.conf -> i2pd.conf, add detection of old config 2016-03-28 11:35:49 +00:00
hagen
0bb89de821 Merge branch 'logs-refactoring' into openssl 2016-03-28 11:33:58 +00:00
hagen
905cad56d8 * add note 2016-03-28 06:02:56 +00:00
orignal
65eeb70eb3 Merge pull request #444 from i2phttp/openssl
Renamed i2pd.conf->i2p.conf with some fixes
2016-03-27 22:54:23 -04:00
orignal
266744f640 fixe memory leak 2016-03-27 12:06:00 -04:00
i2phttp
23d6739580 Renamed i2pd.conf->i2p.conf with some fixes 2016-03-27 16:27:36 +03:00
Jeff Becker
5c9970c786 delete packet if not saved 2016-03-27 09:16:30 -04:00
hagen
3eae716a2d * drop MsgQueue wrapper : not used anymore 2016-03-27 00:17:34 +00:00
hagen
c57b13d922 * migration 2016-03-27 00:17:29 +00:00
hagen
17fb419fb1 * new logs: code 2016-03-27 00:15:50 +00:00
hagen
598d0e216a * fix build requrements 2016-03-27 00:05:47 +00:00
hagen
7bbe926232 * use freopen() instead close()/open() : avoid potential fd leak 2016-03-27 00:05:47 +00:00
hagen
2e848a7c9a * chg default branch for 'dist' target 2016-03-27 00:05:47 +00:00
hagen
437225b43e * convert makefiles back to unix linefeeds 2016-03-27 00:05:47 +00:00
orignal
d39229713f lookup address upon request 2016-03-26 15:02:27 -04:00
xcps
93911be1b9 Merge pull request #441 from xcps/openssl
Sent/received traffic amount humanize
2016-03-26 10:34:53 -04:00
orignal
b74055478c Merge pull request #440 from majestrate/master
add syslog option for logging
2016-03-26 10:34:26 -04:00
xcps
8614c4db73 Sent/received traffic amount humanize 2016-03-26 10:32:19 -04:00
orignal
215d39fc54 address lookup 2016-03-26 10:31:47 -04:00
Jeff Becker
c4e5a130ee don't break win32 2016-03-26 09:49:45 -04:00
Jeff Becker
630072b574 Merge remote-tracking branch 'purple/openssl' 2016-03-26 09:41:31 -04:00
Jeff Becker
5261a3e845 add syslog logging option 2016-03-26 09:40:19 -04:00
xcps
0096a91a57 Merge pull request #439 from xcps/check_if_i2p_address
check if i2p address before call jump service
2016-03-26 02:48:17 -04:00
xcps
56699a9f89 check if i2p address to call jump service 2016-03-26 02:45:37 -04:00
orignal
31ff1372ae Merge pull request #438 from PurpleI2P/openssl
transort resolvers
2016-03-25 21:47:24 -04:00
orignal
3afb1922bb Update family.md 2016-03-25 16:04:44 -04:00
orignal
83c0a8b047 Merge pull request #437 from 0niichan/patch-8
Fixed b64 textarea in the webconsole
2016-03-25 11:53:22 -04:00
0niichan
6699bd47b5 Fixed b64 textarea in the webconsole 2016-03-25 22:48:58 +07:00
orignal
34223b8d4f select appropritae address 2016-03-24 20:14:58 -04:00
orignal
5befe1f019 select appropritae address 2016-03-24 20:04:45 -04:00
orignal
87f86e72f4 Merge pull request #436 from majestrate/master
add option for toggling ipv4
2016-03-24 19:56:29 -04:00
Jeff Becker
53b7eba31a Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2016-03-24 18:46:09 -04:00
Jeff Becker
12c12a8ad1 add no ipv4 option in config 2016-03-24 18:44:41 -04:00
Jeff Becker
897cc7d355 Merge remote-tracking branch 'purple/openssl' 2016-03-24 18:40:15 -04:00
orignal
2e5c56205c address resolver 2016-03-24 14:48:07 -04:00
orignal
bc5ff37e37 check for chunk size 2016-03-24 11:18:11 -04:00
orignal
20341a381f show version in the 'About' window 2016-03-24 11:05:47 -04:00
hagen
926b945846 * UPnP.h : comments 2016-03-24 10:32:20 +00:00
hagen
aa877a73ba * fix mistype 2016-03-24 10:32:18 +00:00
orignal
b28208d1bf 0.9.25 2016-03-23 19:03:17 -04:00
orignal
9bd97383bd don't connect to ipv6 address if not supported 2016-03-23 16:04:42 -04:00
orignal
522c7b2f9d Merge pull request #433 from 0niichan/patch-7
Fix height and width of the main window
2016-03-22 15:43:53 -04:00
0niichan
1833c0acbc Fix height and width of the main window 2016-03-23 02:37:22 +07:00
orignal
c5644ee3f9 hold previous lookup response 2016-03-22 13:10:02 -04:00
orignal
447566fe14 gcc 4.8 2016-03-22 09:50:24 -04:00
orignal
9692c34f6c don't insert same address twice 2016-03-22 07:30:16 -04:00
orignal
37c450f1e1 fixed race condition 2016-03-21 15:13:07 -04:00
orignal
a003e396c5 fixed UPnP build 2016-03-21 13:45:35 -04:00
orignal
996f61efe1 use shared_ptr for Address 2016-03-21 13:02:51 -04:00
orignal
40cdcf8b06 Merge pull request #431 from PurpleI2P/openssl
recent changes
2016-03-21 10:54:02 -04:00
orignal
5947364846 updated reseeds list 2016-03-21 09:02:44 -04:00
orignal
9470107bba show mascot image 2016-03-20 18:34:29 -04:00
orignal
54b945511b Merge pull request #428 from 0niichan/0niichan-Fixed-Anke.ico
Fixed anke.ico
2016-03-20 17:02:52 -04:00
orignal
acfaa0041e fixed anke.ico 2016-03-20 17:01:20 -04:00
0niichan
aeed2dbc3e Fixed anke.ico 2016-03-21 03:36:17 +07:00
orignal
0c6befe8a5 fixed build 2016-03-20 16:00:29 -04:00
orignal
bdcb26edae mascot bitmap added to resources 2016-03-20 14:58:35 -04:00
orignal
e091667b42 Merge pull request #426 from 0niichan/"Anke"-by-MilkHater,-the-I2Pd-mascot-1
"Anke" by MilkHater, the I2Pd mascot
2016-03-20 14:42:45 -04:00
0niichan
d35b14f4cc "Anke" by MilkHater, the I2Pd mascot
700px .bmp and 2200px .jpg
2016-03-20 23:00:20 +07:00
orignal
87996c6811 Merge pull request #425 from 0niichan/"Anke"-by-MilkHater,-the-I2Pd-mascot
"Anke" by MilkHater, the I2Pd mascot
2016-03-20 09:52:27 -04:00
0niichan
a880c733c8 "Anke" by MilkHater, the I2Pd mascot 2016-03-20 20:47:09 +07:00
orignal
ca10dfeb5f Merge pull request #424 from 0niichan/New-.ico-by-MilkHater
New .ico by MilkHater
2016-03-20 09:10:40 -04:00
0niichan
91f55a637b New .ico by MilkHater 2016-03-20 20:02:04 +07:00
orignal
8ae43cfd14 Merge pull request #421 from l-n-s/openssl
Added example configuration file
2016-03-19 19:18:45 -04:00
libre-net-society
83d9513c4a Added example configuration file 2016-03-20 01:39:35 +03:00
orignal
1c76d43e44 mention true/false values for bool params 2016-03-19 08:17:30 -04:00
orignal
1036ce0fa5 create addressbook before etags 2016-03-19 08:07:09 -04:00
orignal
3dbab68f17 don't send own RouterInfo twice 2016-03-18 22:53:03 -04:00
orignal
5896cebeaa list 'enabled' options 2016-03-18 13:35:33 -04:00
orignal
fbe629154d Merge pull request #418 from xcps/connection_strip
strip connection http header
2016-03-18 11:09:15 -04:00
xcps
364136213b extra space 2016-03-18 10:06:53 -04:00
xcps
136b663cef strip connection http header 2016-03-18 10:00:10 -04:00
orignal
803f11bebb local addresses 2016-03-16 15:40:29 -04:00
Mikhail Titov
7c8036807a Cross compiling notes for Win32 target 2016-03-15 19:04:57 -05:00
orignal
84ccca0e98 read persistent ETags 2016-03-15 14:37:07 -04:00
orignal
74efdb95e8 persist etag 2016-03-14 22:00:05 -04:00
orignal
10e45ac493 Merge pull request #414 from mlt/fix413
Fix VS2013 build and close #413
2016-03-14 18:19:37 -04:00
Mikhail Titov
60befdb36e VS2013 snprintf compatibility 2016-03-14 15:18:51 -05:00
Mikhail Titov
59f99ea9bb Ask to minimize on Win32app close
This closes #413
2016-03-14 15:15:13 -05:00
orignal
1a894abcff persist etag for addressbook subscription 2016-03-14 16:05:57 -04:00
orignal
4934fc8809 fixed typo 2016-03-14 13:33:51 -04:00
orignal
18cc6a184f Merge pull request #412 from PurpleI2P/openssl
recent changes
2016-03-14 11:52:43 -04:00
Mikhail Titov
0a08765d73 Win32: hide to tray, webconsole menu item
Standard icon works for me on Windows 8
2016-03-14 02:35:15 -05:00
orignal
355c7437ed supoort win32 console application 2016-03-11 22:24:23 -05:00
orignal
3c55c2d777 fixed race condition at startup 2016-03-11 19:27:43 -05:00
orignal
94806ad0b3 try subscriptions right after initial download 2016-03-11 16:29:49 -05:00
orignal
6840259734 Merge pull request #410 from xcps/jumpservice
jump services
2016-03-11 07:05:25 -05:00
xcps
a1fc48f2a6 Update HTTPServer.cpp 2016-03-11 16:16:11 +05:00
xcps
400e3d21f9 jump services 2016-03-11 15:30:50 +05:00
Mikhail Titov
8f3daad502 Sane TTL for UPnP API>=14 and remove old miniupnpc support 2016-03-11 02:37:04 -06:00
hagen
b0395933de * Addressbook: fix module name 2016-03-11 00:46:58 +00:00
orignal
f8f2ab9cba fixed windows build 2016-03-10 19:34:32 -05:00
orignal
ae5f5375da Merge pull request #406 from mlt/msvc
Compatibility fixes for 64 bit MSVC build
2016-03-10 18:07:54 -05:00
Mikhail Titov
ab5f1e712b AppVeyor msys fix attempt 2016-03-10 14:40:35 -06:00
Mikhail Titov
4532ca97fa caffeine insomnia for win32 2016-03-10 14:20:46 -06:00
Mikhail Titov
5a9ef57f78 Make mingw via cmake happy with _WIN32 in FS.CPP 2016-03-10 14:20:45 -06:00
Mikhail Titov
8791f382b3 Make a deep copy of our addresses for UPnP
Somehow "Expression: vector iterators incompatible" gets thrown especially on fresh start
TODO: figure out details
2016-03-10 14:20:45 -06:00
Mikhail Titov
abdef67ccc _WIN32_WINNT drove nuts 64 bit MSVC builds
TODO: figure out why
2016-03-10 14:20:44 -06:00
Mikhail Titov
33494c4f4b Catch up for miniupnpc API 15 2016-03-10 14:20:43 -06:00
Mikhail Titov
daad975f5d fixup! invoke win32app functions from main 2016-03-10 14:20:43 -06:00
Mikhail Titov
18c00f0a4b Avoid debug symbol files (PDB) collision with MSVC 2016-03-10 14:20:41 -06:00
Mikhail Titov
e7f46b4fbe Create missing directories on the way 2016-03-10 14:20:40 -06:00
Mikhail Titov
74827cd8cf Workaround c++11 dynamic array for MSVC 2016-03-10 14:20:40 -06:00
orignal
5ffe1893cd reduce windows binary size 2016-03-10 14:46:45 -05:00
orignal
f24618e8df Merge pull request #409 from PurpleI2P/openssl
recent changes
2016-03-10 13:37:59 -05:00
orignal
0e5b32ef13 2.5.1 2016-03-10 13:34:16 -05:00
orignal
0493a321d2 oveeride --log for windows 2016-03-10 12:23:17 -05:00
orignal
38b6c12153 fixed bug with missed data directory 2016-03-10 12:05:28 -05:00
orignal
74d4b8e0b9 invoke win32app functions from main 2016-03-09 14:41:14 -05:00
orignal
f843d34234 Merge pull request #407 from PurpleI2P/openssl
recent changes
2016-03-09 13:41:55 -05:00
orignal
95b2bf3645 fixed windows build 2016-03-09 09:38:19 -05:00
hagen
121ac4f1de * move mingw-specific rules to Makefile.mingw 2016-03-09 13:36:39 +00:00
orignal
ec8550d587 use ictoopie_16 in tray 2016-03-08 21:18:48 -05:00
orignal
e403c419e5 16x16 icon added 2016-03-08 17:40:43 -05:00
orignal
4b0d587fe1 Daemon::run 2016-03-08 15:02:32 -05:00
orignal
ebd356c7bd set correct icons 2016-03-08 11:24:29 -05:00
orignal
507093dbad compile with resources 2016-03-07 21:36:11 -05:00
orignal
4cfdc77015 invoke daemon 2016-03-07 16:17:06 -05:00
orignal
9096cacba8 tray icon added 2016-03-07 16:06:34 -05:00
orignal
607336d3ce tray icon added 2016-03-07 15:57:32 -05:00
orignal
6383fc3575 initial commit of Win32App 2016-03-07 14:54:57 -05:00
orignal
a5576ddbf3 don't acquire DH keys pair until connection is established 2016-03-06 09:57:38 -05:00
orignal
e2a70873b8 fixed garbage in console for windows 2016-03-05 21:46:01 -05:00
orignal
23c7340afe Merge pull request #404 from PurpleI2P/openssl
2.5.0
2016-03-04 21:35:41 -05:00
orignal
380b56a89d 2.5.0 2016-03-04 21:34:23 -05:00
orignal
8e09f3478f fixed warnings 2016-03-04 20:35:53 -05:00
orignal
c1ce51eb12 Merge pull request #403 from xcps/webirc2
variable name
2016-03-04 09:53:52 -05:00
xcps
9aeb773169 variable name 2016-03-04 19:26:28 +05:00
orignal
091c13ff41 Merge pull request #402 from xcps/webirc2
WebIRC support
2016-03-04 07:05:37 -05:00
xcps
ef0bab0c6e webirc support 2016-03-04 11:37:38 +05:00
orignal
70bd16adf6 set established state for zero-hops tunnles 2016-03-03 17:57:15 -05:00
orignal
96a713afeb zero-hops outbound tunnels 2016-03-03 16:24:13 -05:00
orignal
bf3615fb32 Merge pull request #401 from PurpleI2P/openssl
recent changes
2016-03-03 10:31:04 -05:00
orignal
0f56b1c943 show number of received bytes for zero-hops inbound tunnel 2016-03-03 07:30:38 -05:00
orignal
d541572882 enable zero-hops inbound tunnel 2016-03-02 22:41:53 -05:00
orignal
ecfdc377ec send close floodfills only in DatabaseSearchReply 2016-03-02 19:46:32 -05:00
orignal
fa67e90767 inbound zero-hops tunnel 2016-03-02 16:12:02 -05:00
orignal
81b72d5481 fixed crash on termination if proxies were excluded 2016-03-02 12:04:02 -05:00
orignal
ef6028e933 replace std::map to std::list for inbound tunnels 2016-03-02 11:58:52 -05:00
orignal
5d41fe4a35 Merge pull request #400 from majestrate/webui-add-tunnel-count
Add Transit/Client Tunnel Count to web ui
2016-03-02 10:13:51 -05:00
Jeff Becker
1dc6cec1aa add client/transit tunnel count in webui 2016-03-02 10:05:26 -05:00
Jeff Becker
9378668e52 add colin 2016-03-02 10:03:50 -05:00
Jeff Becker
eb96ead80e add tunnel counts to front page of web ui 2016-03-02 10:03:36 -05:00
orignal
9403fbaf81 common tunnels' hash table 2016-03-01 20:48:56 -05:00
orignal
79190f313d use shared_ptr for transit tunnels 2016-03-01 15:22:36 -05:00
orignal
4c124284b6 Merge pull request #399 from PurpleI2P/openssl
irc tunnel and gzip
2016-03-01 13:39:26 -05:00
orignal
6d892179c8 added gzip parameter for server tunnels 2016-02-29 14:44:15 -05:00
orignal
61675c20d8 don't delete log file upon HUP 2016-02-29 11:02:55 -05:00
orignal
4aae878db8 increase LeaseSet expiration threshold 2016-02-28 21:43:18 -05:00
orignal
918884bd11 Merge pull request #398 from xcps/irc_ip2b32
irc tunnel
2016-02-28 16:19:19 -05:00
xcps
8799f9079b change part for replace 2016-02-29 02:15:29 +05:00
orignal
0b471cfd06 Merge pull request #397 from xcps/irc_ip2b32
Irc ip to b32
2016-02-28 14:14:11 -05:00
xcps
7b39a12396 ready 2016-02-28 22:32:34 +05:00
xcps
57a53b4b6c fixed I2PServerTunnelHTTP call 2016-02-28 21:13:01 +05:00
orignal
f6d0b3368f znx cert added 2016-02-28 09:56:17 -05:00
xcps
0fe7bdf849 init 2016-02-28 18:17:36 +05:00
xcps
a26dc39a6d ident fix 2016-02-28 18:17:35 +05:00
xcps
e45cfe7d0c init 2016-02-28 18:17:35 +05:00
orignal
efefa8caf5 Merge pull request #395 from PurpleI2P/openssl
socks outproxy
2016-02-27 16:16:15 -05:00
orignal
cc13db9b1f updated FreeBSD instructions 2016-02-27 15:44:36 -05:00
orignal
f339544256 Merge pull request #394 from majestrate/outproxy-socks
support for outproxy via local upstream socks proxy
2016-02-26 21:42:04 -05:00
Jeff Becker
1a05bcb295 initial support for out proxy via local upstream socks proxy 2016-02-26 17:06:11 -05:00
orignal
190e26276a reuse tunnel pair for LS request 2016-02-26 16:17:29 -05:00
orignal
bb33760e87 don't re-request twice 2016-02-26 16:16:59 -05:00
orignal
9e105b4983 Merge pull request #393 from PurpleI2P/openssl
recent changes
2016-02-26 15:13:31 -05:00
orignal
8dcf70408d hostoverride added 2016-02-25 20:32:05 -05:00
orignal
9d6d1825c7 pass flag to SSU header 2016-02-25 18:40:40 -05:00
orignal
1a4923cdce don't request relayTag if we are reachable 2016-02-25 15:57:58 -05:00
orignal
316e440390 Merge pull request #392 from PurpleI2P/openssl
recent changes
2016-02-25 13:51:03 -05:00
orignal
7d66019220 start checking for expiration after 10 minutes 2016-02-24 11:50:56 -05:00
orignal
f98a6fb665 tighten RouterInfo expiration 2016-02-24 11:31:14 -05:00
orignal
dbdc7279c4 Merge pull request #391 from PurpleI2P/openssl
new fs
2016-02-24 11:21:36 -05:00
orignal
7726705b5c process request relay tag extended SSU option 2016-02-23 12:16:53 -05:00
orignal
34b7e8815a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-23 11:17:14 -05:00
orignal
8ac2b58a44 Merge branch 'master' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-23 11:17:01 -05:00
orignal
fe97f0929b delete expired floodfills thorugh a separate loop 2016-02-22 20:51:32 -05:00
orignal
6eec353c2b moved tunnel config file inialization to ClientContext 2016-02-22 15:27:40 -05:00
orignal
2b4c3b8d1f start up if i2p.conf is not presented 2016-02-22 15:17:58 -05:00
orignal
df99b37c4d Merge pull request #388 from l-n-s/openssl
added family documentation
2016-02-22 13:45:29 -05:00
libre-net-society
ab6f3fcf8e added family documentation 2016-02-22 20:49:17 +03:00
orignal
ca6f656e1b ignore non-reachable floodfills 2016-02-22 10:27:43 -05:00
orignal
88798b1a9e fixed windows build 2016-02-22 09:53:26 -05:00
hagen
c197270125 Merge branch 'new-fs' into openssl
Conflicts:
	Family.cpp
2016-02-22 13:04:53 +00:00
hagen
dc344d4658 * add comment 2016-02-22 12:57:25 +00:00
orignal
b4864831e0 Merge pull request #387 from PurpleI2P/openssl
family
2016-02-21 20:29:49 -05:00
orignal
476dffff13 Create family.md 2016-02-21 15:26:14 -05:00
orignal
389ee974f3 family 2016-02-20 21:20:21 -05:00
hagen
0d15eceacb * Profiling : move storage from FS.cpp to Profiling.cpp 2016-02-21 01:49:35 +00:00
hagen
b69fbdda9a * NetDb : move storage from FS.cpp to NetDb.cpp 2016-02-21 01:49:32 +00:00
hagen
d3746e0119 * FS.h : add include guards 2016-02-21 01:49:29 +00:00
orignal
230af9cafa set router's family 2016-02-20 20:20:19 -05:00
orignal
4db63d113c i2pd-dev certificate updated 2016-02-20 09:22:09 -05:00
orignal
008583396d extract CN 2016-02-20 08:33:13 -05:00
hagen
33a33e3c71 * i2p::util::http::GetHttpContent() : use std::transform instead boost 2016-02-20 01:47:34 +00:00
hagen
d312d753e9 * Destination.cpp : fix lambda with 4.7 2016-02-20 01:47:32 +00:00
hagen
02310d4af6 * Family : use i2p::fs::ReadDir instead direct boost::filesystem call 2016-02-20 01:47:29 +00:00
orignal
0e6d8c4e25 i2pd-dev family certificate added 2016-02-19 20:09:48 -05:00
hagen
55315fca80 Merge branch 'openssl' into new-fs
Conflicts:
	AddressBook.cpp
	NetDb.cpp
	filelist.mk
2016-02-20 00:59:48 +00:00
orignal
4eef9e780f extract and verify family from RouterInfo 2016-02-19 16:37:41 -05:00
orignal
7bfc3562af extract EcDSA key from family certificate 2016-02-19 16:13:46 -05:00
orignal
5b0b0d6d36 Merge pull request #386 from PurpleI2P/openssl
recent changes
2016-02-19 13:18:36 -05:00
orignal
cb64072f7b fixed windows build 2016-02-19 11:18:01 -05:00
orignal
c5b6da7201 case-insensitive http responses 2016-02-19 10:04:52 -05:00
orignal
f1d4818045 Family.cpp added 2016-02-18 22:39:09 -05:00
orignal
76b49f6985 uncompress stream by chunks 2016-02-18 22:34:55 -05:00
orignal
094d9193b9 start addressbook first 2016-02-18 22:34:14 -05:00
orignal
3053a9b6a0 enable i2p gzip compression 2016-02-18 20:35:14 -05:00
orignal
47bf0ef591 free pkey after usage 2016-02-18 16:28:43 -05:00
orignal
e2aa2709ac family added 2016-02-18 15:57:43 -05:00
orignal
9a6d478eb1 handle compressed addressbook 2016-02-18 13:19:31 -05:00
orignal
4f37e7dc3c Merge pull request #383 from PurpleI2P/openssl
recent changes
2016-02-18 09:06:48 -05:00
hagen
2a4ba8d349 * Addressbook : move storage init code from constructor to Init() : was too early 2016-02-18 10:42:50 +00:00
hagen
85bd7a63c6 * AddressBook : embed HashedStorage instance into AddressBookFilesystemStorage class 2016-02-18 10:42:50 +00:00
hagen
138d57143a * FS.cpp : add const to accessors 2016-02-18 10:42:50 +00:00
hagen
464a228106 * FS.cpp : rename method 2016-02-18 10:42:50 +00:00
hagen
2b92a039bb * FS.h : more comments 2016-02-18 10:42:50 +00:00
hagen
f190ee951c * use characters sets from Base.cpp - remove ABook class 2016-02-18 10:42:50 +00:00
hagen
68cc75cada * Base.cpp : add T32 character set + accessor 2016-02-18 10:42:41 +00:00
orignal
b4e324ec0e flood to 3 closest floodfills 2016-02-17 21:24:21 -05:00
orignal
32fe2e7974 correct monotonic expiration time calculation 2016-02-17 19:36:07 -05:00
orignal
713513aacc flood newer RI/LS only 2016-02-17 15:36:55 -05:00
orignal
b4ffca56a3 update lease's expiration time continiously 2016-02-17 13:10:29 -05:00
orignal
f2168774a5 check leaseset timestamp 2016-02-16 22:57:38 -05:00
orignal
febc00d357 fixed race condition of DeliveryStatus message 2016-02-16 16:10:22 -05:00
orignal
01a8c507e5 Merge pull request #381 from PurpleI2P/openssl
recent changes
2016-02-16 16:01:12 -05:00
orignal
bf7982cc2e build with make added 2016-02-16 15:08:35 -05:00
orignal
2e9689886b build with make added 2016-02-16 15:07:56 -05:00
orignal
2003b34036 12 hours expiration if more than 2500 routers 2016-02-15 21:40:49 -05:00
orignal
e1995b5c70 try to download default hosts.txt until success 2016-02-15 18:20:01 -05:00
orignal
3890acabc4 Merge pull request #380 from PurpleI2P/openssl
fixed http issues
2016-02-15 16:22:15 -05:00
orignal
ba6c0d0423 fixed messy http pages 2016-02-15 15:16:53 -05:00
orignal
882e7a845e process remaining data from stream 2016-02-14 22:10:56 -05:00
orignal
ca56d3fc23 handle LeaseSet expiration correctly 2016-02-14 18:30:07 -05:00
orignal
49b1e76585 use rtt for ack timeout 2016-02-13 23:10:51 -05:00
orignal
80f81685d1 use rtt for ack timeout 2016-02-13 23:02:58 -05:00
orignal
21dead3125 increase lease expiration threshold 2016-02-13 17:56:42 -05:00
orignal
1521d08285 family cetificates added 2016-02-13 17:13:07 -05:00
orignal
59b2e31add ssl certificates updated 2016-02-13 17:10:54 -05:00
orignal
b5feb3fd66 update reseeds list 2016-02-13 17:03:25 -05:00
orignal
7785e6ebd2 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-12 20:56:46 -05:00
orignal
c561d71dc0 count lease expiration threshold 2016-02-12 20:56:29 -05:00
orignal
2cfb697867 strip our Referer and replace User-Agent 2016-02-12 15:42:13 -05:00
orignal
c680ff006e Merge pull request #378 from PurpleI2P/openssl
recent changes
2016-02-12 11:23:29 -05:00
orignal
333103f50e shared RTT 2016-02-11 22:18:44 -05:00
orignal
517385fb63 Lease enddate threshold 2016-02-11 22:18:24 -05:00
orignal
ee8ab58d64 don't reply to lookup with expired LeaseSet 2016-02-11 22:17:33 -05:00
orignal
b967acda58 flood to floodfills that are close than us only 2016-02-11 15:05:46 -05:00
orignal
d81ca5f919 local destination leaseset storage verification 2016-02-11 14:45:33 -05:00
orignal
07adf64aec Merge pull request #376 from PurpleI2P/openssl
recent changes
2016-02-11 14:37:11 -05:00
orignal
fbb98e1aec show actual name of an invalid parameter 2016-02-11 11:18:15 -05:00
orignal
2fdf927704 show actual name of an invalid parameter 2016-02-11 10:54:36 -05:00
hagen
4b84656133 * i2p::fs migration: drop unused code from util.* (#314) 2016-02-11 13:05:00 +00:00
hagen
97c136d043 * i2p::fs migration: Daemon, DaemonLinux, api (#290) 2016-02-11 13:05:00 +00:00
hagen
79bf44b3f5 * i2p::fs migration: ClientContext, Destination, HTTPServer, Reseed, RouterContext 2016-02-11 13:05:00 +00:00
hagen
ddd8d4aeb2 * i2p::fs migration: AddressBook.* 2016-02-11 13:05:00 +00:00
hagen
bfcb6f577f * i2p::fs migration: Profiling.* 2016-02-11 13:05:00 +00:00
hagen
2b137b43e6 * i2p::fs migration: I2PControl.* 2016-02-11 13:05:00 +00:00
hagen
6d74493491 * i2p::fs migration: NetDb.* 2016-02-11 13:05:00 +00:00
hagen
6f4271c054 * update buildsystems 2016-02-11 13:05:00 +00:00
hagen
f24054100e * new i2p::fs implementation 2016-02-11 13:05:00 +00:00
hagen
6e98649607 * I2PControl: send valid error response, instead closing connection 2016-02-11 13:00:56 +00:00
hagen
b2108ff2d0 * fix flags on std::ifstream 2016-02-11 13:00:56 +00:00
hagen
8949ebf041 * tune logging 2016-02-11 13:00:56 +00:00
hagen
576801cd32 * Addressbook: load addresses at start, not on first request 2016-02-11 13:00:56 +00:00
hagen
2f2b12811f * Addressbook: don't save to disk if address map is empty 2016-02-11 13:00:56 +00:00
hagen
d8ea3a9035 * make target 'strip' 2016-02-11 13:00:56 +00:00
orignal
45c3b3987b reset floodfill 2016-02-11 07:50:29 -05:00
orignal
93720fffd4 shared path between streams 2016-02-10 22:51:08 -05:00
orignal
61ad6a2b88 set supported transports flag after actual address insertion 2016-02-10 16:09:34 -05:00
orignal
c9d5b3c0ff Merge pull request #373 from PurpleI2P/openssl
recent changes
2016-02-10 10:51:21 -05:00
hagen
d51bf735c4 * fix mistype 2016-02-10 10:37:30 +00:00
hagen
22c388ab18 * fix compilation with gcc 4.7/4.8 2016-02-10 10:37:30 +00:00
hagen
d5f831301f * explicit log message when bandwidth set to 'low' 2016-02-10 10:37:30 +00:00
hagen
dcab37a148 * update debian/i2pd.{init,upstart} : logging options 2016-02-10 10:37:30 +00:00
hagen
60b2da3671 * add --datadir option (not actually works yet) (#290) 2016-02-10 10:37:30 +00:00
hagen
5c1b5816d4 * fix segfault when offline (#330) 2016-02-10 10:37:30 +00:00
hagen
7a0a45e9d2 * use IsDefault() to check explicitly set values 2016-02-10 10:37:30 +00:00
hagen
70f72a78f6 + i2p::config::IsDefault 2016-02-10 10:37:30 +00:00
orignal
e056c9c135 drop expired leasesand renew leaseset 2016-02-09 22:42:01 -05:00
orignal
c754b5ae18 fixed crash 2016-02-09 17:54:22 -05:00
orignal
481fafc11d invalidate excluded leases 2016-02-09 15:27:23 -05:00
orignal
7d927b0e28 shared_ptr for Lease 2016-02-09 10:46:27 -05:00
orignal
c314b07136 Merge pull request #371 from PurpleI2P/openssl
recent changes
2016-02-09 10:37:46 -05:00
orignal
16fe13bf4a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-02-08 20:30:53 -05:00
orignal
d19eda7e08 moved Config.cpp to libi2pd 2016-02-08 20:29:56 -05:00
orignal
6f0a136727 some cleanup 2016-02-08 20:29:34 -05:00
orignal
e2e101e4fb queue up out of sequence packets 2016-02-08 15:47:39 -05:00
orignal
74f03202b7 queue up out of sequence packets 2016-02-08 15:02:17 -05:00
orignal
3d19e92059 queue up out of sequence packets 2016-02-08 14:42:20 -05:00
orignal
bfff125cc5 Merge pull request #370 from PurpleI2P/openssl
recent changes
2016-02-08 14:41:40 -05:00
orignal
e90baf3ca6 correct required base64 buffer size 2016-02-07 21:35:06 -05:00
orignal
f3b277aeef doesn't store leases in netdb 2016-02-07 19:45:06 -05:00
orignal
76096747b6 cleanup incoming and outgoing tags together 2016-02-07 17:45:11 -05:00
orignal
4c6ef32d72 fixed #369 2016-02-06 08:52:02 -05:00
orignal
a8e12e624d fixed hanging connection 2016-02-05 21:38:03 -05:00
orignal
88a43bfc28 Merge pull request #368 from majestrate/fix_sam_b32_naming
allow resolving of .b32.i2p addresses in SAM name lookup
2016-02-05 16:37:00 -05:00
Jeff Becker
3b268fe3cc allow resolving of .b32.i2p addresses in SAM name lookup 2016-02-05 16:17:53 -05:00
orignal
4c72d43a8a use more efficient XOR over ChipherBlocks for win32 2016-02-05 15:58:14 -05:00
orignal
0a5f8527b2 Merge pull request #366 from majestrate/bug_fixes
Squash potential future bugs.
2016-02-05 13:12:08 -05:00
Jeff Becker
9f1b84d6f2 use const size_t instead of size_t 2016-02-05 12:39:17 -05:00
Jeff Becker
babcbcbcea use const size_t instead of size_t 2016-02-05 12:32:50 -05:00
orignal
823a6017fe Merge pull request #364 from 0niichan/patch-6
Added instructions for a 64-bit OS
2016-02-05 11:12:57 -05:00
0niichan
f034aef2ae Added instructions for a 64-bit OS 2016-02-05 22:58:04 +07:00
Jeff Becker
bf38bd5a1d * Fill padding with random in NTCP phase3
* Fill padding with random in NTCPSession::CreateMsgBuffer

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

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

This closes #186
2015-06-06 13:53:22 -05:00
orignal
d7e7823606 Merge pull request #197 from mlt/cmake-msvc
Fix Win32 build with CMake and MSVC
2015-06-06 14:19:38 -04:00
Mikhail Titov
2d3493a225 Perhaps bitness detection is an introspection
http://www.cmake.org/cmake/help/v3.0/command/find_library.html
2015-06-06 12:34:06 -05:00
Mikhail Titov
a3b08c0016 Fix Win32 build with CMake and MSVC 2015-06-06 12:21:35 -05:00
orignal
d9c0f52846 don't pick node for 5 minutes if declined 2015-06-05 22:09:16 -04:00
orignal
a96482b186 skip missing sections 2015-06-05 21:15:02 -04:00
orignal
10e78785cd additional statistics for profiling 2015-06-05 15:55:21 -04:00
orignal
da56397b39 fixed bug with zero-size clove 2015-06-04 11:31:22 -04:00
orignal
abc05b4485 version 0.9.20 2015-06-04 09:54:46 -04:00
orignal
09fd0baf78 replace Host: for server http tunnels 2015-06-03 12:30:15 -04:00
orignal
d7deb938c5 catch HTTP header of HTTP server tunnel connection 2015-06-02 16:21:38 -04:00
orignal
68834df271 use addresses in server tunnel configuration 2015-06-02 13:18:41 -04:00
orignal
8a3c276e66 I2PTunnelConnectionHTTP added 2015-06-02 13:03:22 -04:00
orignal
6a043649f5 use random msg_id for I2NP messages 2015-05-27 13:35:54 -04:00
orignal
019af7bd3a http server tunnel added 2015-05-20 16:00:09 -04:00
orignal
4f2f67d5b1 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-05-17 19:40:57 -04:00
orignal
2a59ae294d check length of garlic message 2015-05-17 19:40:46 -04:00
6d586bde6c Note that Boost 1.58 works 2015-05-14 08:29:17 +00:00
orignal
9510bba3b0 excluded dead reseeds 2015-05-12 11:56:42 -04:00
orignal
eb559f7b6a excluded dead reseeds 2015-05-12 11:51:03 -04:00
orignal
64dbd9abdf Merge pull request #195 from ipslot/master
Update Log.cpp
2015-05-12 06:28:47 -04:00
ipslot
dfd41385b1 Update Log.cpp
set default log to std::cerr stream
2015-05-12 13:27:02 +06:00
orignal
2b797fcd54 use shared_ptr for NetDb's I2NPMessages 2015-05-11 15:17:43 -04:00
orignal
5cd557ef9d check for I2NP message buffer boudary 2015-05-11 12:53:08 -04:00
orignal
8baab2de37 Merge pull request #191 from apprb/dev
CMakeLists.txt: compilation speed up
2015-05-11 06:33:36 -04:00
apprb
c266cff956 CMakeLists.txt: compilation speed up 2015-05-11 15:56:13 +06:00
orignal
53affa3303 Merge pull request #190 from multikatt/patch-1
typo: Gralic -> Garlic
2015-05-09 19:28:22 -04:00
David
ec772c5d46 typo: Gralic -> Garlic 2015-05-09 19:25:11 -04:00
orignal
7b5a7e10a9 fixed log crash at shutdown 2015-05-08 21:42:28 -04:00
orignal
188f1fcff8 rewrite tunnel path inversion code 2015-05-08 14:07:33 -04:00
orignal
39c346df10 created paired inbound tunnel after outbound 2015-05-07 16:03:12 -04:00
orignal
490e829083 Merge pull request #189 from hagen-i2p/gcc5-makefile
* add gcc 5.* to supported compilers
2015-05-07 11:16:20 -04:00
hagen
846128a791 * add gcc5 to supported compilers 2015-05-07 03:40:19 +00:00
orignal
6bad2daa62 fixed build errors for gcc 4.6 2015-05-06 19:18:00 -04:00
orignal
4c91d08cea pass TunnelConfig as shared_ptr 2015-05-06 16:17:48 -04:00
orignal
2442d0e910 moved UPnP instance to Transports. Use actual port from RouterContext 2015-05-06 12:19:20 -04:00
orignal
7c13194d5a don't recalculate timestamp for each log message 2015-05-06 11:24:35 -04:00
orignal
0ae7bbd34d Update README.md 2015-05-05 17:30:32 -04:00
orignal
0b2654f6b1 Update README.md 2015-05-05 17:30:14 -04:00
orignal
42d49bde86 handle tunnels quantity params 2015-05-05 12:32:13 -04:00
orignal
d2b4a6fd50 select first hop from existing connections if applicable 2015-05-05 10:33:19 -04:00
orignal
7f172964f6 check profile only once 2015-05-04 13:01:27 -04:00
b8b8d70c7f reseed certificate updates 2015-05-02 21:00:43 +00:00
orignal
969695f318 check garlic clove length 2015-04-21 18:59:35 -04:00
orignal
7ec701a816 uin32_t for elapsed time 2015-04-21 18:33:04 -04:00
orignal
c96b81206d changed some profiling parameters 2015-04-21 15:59:40 -04:00
orignal
5f8356741e fixed potential memory leak 2015-04-18 13:55:15 -04:00
orignal
3987d5e5a0 recreate tunnel after 9.5 minutes 2015-04-17 11:36:42 -04:00
orignal
fcb56db224 try to pick an outbound tunnel with same endpoint instead expired 2015-04-17 10:11:51 -04:00
orignal
873754c6ca select next lease with same gateway if possible 2015-04-16 11:38:36 -04:00
orignal
12465f840a check outbound tunnles for readiness 2015-04-15 18:25:05 -04:00
orignal
e8c9d2db10 double RTO after every resend attempt 2015-04-15 11:52:49 -04:00
orignal
a8b4f38865 router don't expire if less than 75 2015-04-15 07:30:37 -04:00
orignal
27bd193708 re-create tunnel before expiration 2015-04-14 21:37:21 -04:00
orignal
c56ddce2f6 some cleanup 2015-04-14 10:46:44 -04:00
orignal
5d2f9f9f0b fixed potential memory leak 2015-04-14 10:40:46 -04:00
orignal
864aba9f4e version 0.9.19 2015-04-13 18:54:13 -04:00
orignal
4d27399ce3 check profile for high bandwidth peer selection only 2015-04-13 18:51:31 -04:00
orignal
76c54ffdef always check profile for peer selection 2015-04-13 18:41:19 -04:00
orignal
c873e9dd68 don't send reset message due problem at other side 2015-04-13 11:38:23 -04:00
orignal
ce99357ebe check for zero ident 2015-04-12 16:59:59 -04:00
orignal
562de3514a check database lookup type 2015-04-12 15:54:28 -04:00
orignal
128a8f3b48 delete obsolete profiles 2015-04-11 15:39:23 -04:00
orignal
1839b85d97 Merge pull request #180 from 7histle/master
Fix for #179
2015-04-11 07:03:10 -04:00
7histle
f0f154cd10 Fix for #179 2015-04-11 13:47:49 +03:00
orignal
624bff3036 reduced log file size 2015-04-10 19:58:32 -04:00
orignal
1d2950b4a7 reduced CPU load at floodfill 2015-04-10 19:49:58 -04:00
orignal
9072a018dd reduced CPU load at floodfill 2015-04-10 18:13:11 -04:00
orignal
2a997d94bf GetClosestFloodfills added 2015-04-10 16:15:13 -04:00
orignal
2741e94a72 fixed infinite loop 2015-04-10 13:19:23 -04:00
orignal
7c660ee556 show local destination for SAM sessions 2015-04-10 12:11:10 -04:00
orignal
51b850aa85 show windows size and connection status 2015-04-10 11:52:14 -04:00
orignal
b29e94005d fixed crash 2015-04-10 09:58:08 -04:00
orignal
ddd506fde7 Merge pull request #178 from yuri-sevatz/master
Fix -lboost_date_time missing from CMakeLists.txt
2015-04-10 06:57:37 -04:00
Yuri Sevatz
20310cb109 Fix -lboost_date_time missing from CMakeLists.txt 2015-04-10 00:10:35 -04:00
orignal
11177d37ea send and handle RESET flag 2015-04-09 21:09:30 -04:00
orignal
da006a1d6e use AsyncSend 2015-04-09 18:40:23 -04:00
orignal
451b0382ea implemented AsyncSend 2015-04-09 15:07:25 -04:00
orignal
950f250d66 NetDb/NetDbRequests split 2015-04-09 12:45:00 -04:00
orignal
01913d2b14 EdDSA signer added 2015-04-09 10:03:21 -04:00
orignal
e0b19a6383 fixed crash 2015-04-08 19:06:47 -04:00
orignal
48289845df EdDSA signature type added 2015-04-08 16:28:52 -04:00
orignal
454f2dabbd EdDSA signature type added 2015-04-08 16:18:16 -04:00
orignal
8891d9aa4d Decode point 2015-04-08 15:31:13 -04:00
orignal
49d59fc116 IsOnCurve added 2015-04-08 14:07:45 -04:00
orignal
8c92c50f9a multiplication by integer 2015-04-08 13:49:27 -04:00
orignal
75d45ae988 initial code for Ed25519 added 2015-04-08 13:21:49 -04:00
orignal
d5e1d5db9c validate leaseset for zero leases 2015-04-08 10:34:16 -04:00
orignal
9ce9d9b7fc variable length buffer for LeaseSet 2015-04-08 09:39:02 -04:00
orignal
e9edc7b205 Merge pull request #177 from robertfoss/asan_1
Fixed memory leak: delete -> delete[]
2015-04-07 17:27:18 -04:00
orignal
56822d9424 fixed null pointer exception 2015-04-07 17:22:14 -04:00
Robert Foss
2c480bee9a Fixed memory leak: delete -> delete[] 2015-04-07 22:37:24 +02:00
orignal
3983838694 use unique_ptr for ElGamalEncryption 2015-04-07 16:02:07 -04:00
orignal
1e74ff8a85 use shared_ptr for CreateDatabaseStore 2015-04-07 15:15:27 -04:00
orignal
8c47bf9dd3 use shared_ptr for local LeaseSet 2015-04-07 15:02:00 -04:00
orignal
3a26383c4d made Encrypt const 2015-04-07 14:40:36 -04:00
orignal
634976cdde pass LeaseSet to callback of RequestDestination 2015-04-07 12:02:25 -04:00
orignal
bc21f5955f use shared_ptr for AddressReceiver 2015-04-06 15:02:37 -04:00
orignal
e72eb35cc2 use shared_ptr for socket in I2PTunnelConnection 2015-04-06 14:41:07 -04:00
orignal
fbe4e64e44 lookup always takes full address from LeaseSet 2015-04-06 12:22:13 -04:00
orignal
be301dc090 4 tags for LeaseSet request 2015-04-05 20:07:32 -04:00
orignal
250af7f247 fixed race condition 2015-04-05 13:56:41 -04:00
orignal
10577cd1e5 select tunnel from TunnelPool rather than from LeaseSet for DeliveryStatus 2015-04-05 12:54:15 -04:00
orignal
62593f60c5 fixed memory leak 2015-04-04 15:44:29 -04:00
orignal
89ed8c2173 set datagram receiver per port 2015-04-03 20:34:37 -04:00
orignal
321dd252ea fixed crash if no routers available 2015-04-03 10:02:45 -04:00
orignal
19325d552a fixed race condition 2015-04-02 15:10:02 -04:00
orignal
43f8ec46cc fixed crash if can't connect to a reseed 2015-04-02 10:27:07 -04:00
orignal
cb9f78540a Update README.md 2015-04-02 09:01:15 -04:00
orignal
0a15088572 RC4_SHA cipher suite 2015-04-01 20:23:06 -04:00
orignal
ec68338a20 fixed typo 2015-04-01 15:42:26 -04:00
orignal
173b35f77d https://netdb.rows.io:444/ added 2015-04-01 15:07:45 -04:00
orignal
f47831c339 RSA_WITH_AES_256_CBC_SHA support 2015-04-01 14:41:36 -04:00
orignal
e7d4f63884 use SendFinished 2015-04-01 13:55:50 -04:00
orignal
4e81083bb4 AES256 cipher template 2015-04-01 12:56:41 -04:00
orignal
0446a5ae73 moved encryption/decryption to TlsCipher 2015-04-01 12:03:58 -04:00
orignal
a2aec4340d Merge pull request #173 from hagen-i2p/readme-fix
* README.md
2015-04-01 10:37:05 -04:00
hagen
fda68c114c * README.md 2015-04-01 13:48:54 +00:00
orignal
de5c55160b count tunnel acceptance ratio for peer selection 2015-03-31 17:13:01 -04:00
316 changed files with 27405 additions and 27702 deletions

18
.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
@@ -230,3 +234,15 @@ pip-log.txt
#Mr Developer
.mr.developer.cfg
# Sphinx
docs/_build
/androidIdea/
# emacs files
*~
*\#*
# gdb files
.gdb_history

35
.travis.yml Normal file
View File

@@ -0,0 +1,35 @@
language: cpp
cache:
apt: true
os:
- linux
sudo: required
dist: trusty
addons:
apt:
packages:
- build-essential
- cmake
- g++
- clang
- libboost-chrono-dev
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-program-options-dev
- libboost-system-dev
- libboost-thread-dev
- libminiupnpc-dev
- libssl-dev
compiler:
- gcc
- clang
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install openssl miniupnpc ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink boost openssl && brew link boost openssl -f ; fi
env:
matrix:
- BUILD_TYPE=Release UPNP=ON
- BUILD_TYPE=Release UPNP=OFF
script:
- cd build && cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DWITH_UPNP=${UPNP} && make

File diff suppressed because it is too large Load Diff

View File

@@ -7,22 +7,25 @@
#include <vector>
#include <iostream>
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "base64.h"
#include "util.h"
#include "Base.h"
#include "Identity.h"
#include "Log.h"
#include "Destination.h"
namespace i2p
{
namespace client
{
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt";
const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes
const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours)
const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes
const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second
const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53;
const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54;
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
@@ -31,15 +34,21 @@ namespace client
public:
virtual ~AddressBookStorage () {};
virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0;
virtual void AddAddress (const i2p::data::IdentityEx& address) = 0;
virtual std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const = 0;
virtual void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address) = 0;
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
virtual bool Init () = 0;
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0;
virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0;
};
class AddressBookSubscription;
class AddressResolver;
class AddressBook
{
public:
@@ -47,37 +56,49 @@ namespace client
AddressBook ();
~AddressBook ();
void Start ();
void StartResolvers ();
void Stop ();
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity);
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address);
const i2p::data::IdentHash * FindAddress (const std::string& address);
void LookupAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (const i2p::data::IdentityEx& address);
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success);
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); }
std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); }
std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); }
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
private:
void StartSubscriptions ();
void StopSubscriptions ();
AddressBookStorage * CreateStorage ();
void LoadHosts ();
void LoadSubscriptions ();
void LoadLocal ();
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
void StartLookups ();
void StopLookups ();
void HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
private:
std::mutex m_AddressBookMutex;
std::map<std::string, i2p::data::IdentHash> m_Addresses;
std::map<i2p::data::IdentHash, std::shared_ptr<AddressResolver> > m_Resolvers; // local destination->resolver
std::mutex m_LookupsMutex;
std::map<uint32_t, std::string> m_Lookups; // nonce -> address
AddressBookStorage * m_Storage;
volatile bool m_IsLoaded, m_IsDownloading;
std::vector<AddressBookSubscription *> m_Subscriptions;
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;
};
@@ -86,16 +107,36 @@ namespace client
public:
AddressBookSubscription (AddressBook& book, const std::string& link);
void CheckSubscription ();
void CheckUpdates ();
private:
void Request ();
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 ""
};
class AddressResolver
{
public:
AddressResolver (std::shared_ptr<ClientDestination> destination);
~AddressResolver ();
void AddAddress (const std::string& name, const i2p::data::IdentHash& ident);
private:
void HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
private:
std::shared_ptr<ClientDestination> m_LocalDestination;
std::map<std::string, i2p::data::IdentHash> m_LocalAddresses;
};
}
}

235
BOB.cpp
View File

@@ -1,7 +1,7 @@
#include <string.h>
#include <boost/lexical_cast.hpp>
#include "Log.h"
#include "ClientContext.h"
#include "util.h"
#include "BOB.h"
namespace i2p
@@ -33,27 +33,22 @@ namespace client
void BOBI2PInboundTunnel::Accept ()
{
auto receiver = new AddressReceiver ();
receiver->socket = new boost::asio::ip::tcp::socket (GetService ());
auto receiver = std::make_shared<AddressReceiver> ();
receiver->socket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this,
std::placeholders::_1, receiver));
}
void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, AddressReceiver * receiver)
void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver)
{
if (!ecode)
{
Accept ();
ReceiveAddress (receiver);
}
else
{
delete receiver->socket;
delete receiver;
}
}
void BOBI2PInboundTunnel::ReceiveAddress (AddressReceiver * receiver)
void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr<AddressReceiver> receiver)
{
receiver->socket->async_read_some (boost::asio::buffer(
receiver->buffer + receiver->bufferOffset,
@@ -63,14 +58,10 @@ namespace client
}
void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
AddressReceiver * receiver)
std::shared_ptr<AddressReceiver> receiver)
{
if (ecode)
{
LogPrint ("BOB inbound tunnel read error: ", ecode.message ());
delete receiver->socket;
delete receiver;
}
LogPrint (eLogError, "BOB: inbound tunnel read error: ", ecode.message ());
else
{
receiver->bufferOffset += bytes_transferred;
@@ -79,15 +70,13 @@ 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;
if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident))
{
LogPrint (eLogError, "BOB address ", receiver->buffer, " not found");
delete receiver->socket;
delete receiver;
LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found");
return;
}
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
@@ -96,46 +85,32 @@ namespace client
else
GetLocalDestination ()->RequestDestination (ident,
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
this, std::placeholders::_1, receiver, ident));
this, std::placeholders::_1, receiver));
}
else
{
if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE)
ReceiveAddress (receiver);
else
{
LogPrint ("BOB missing inbound address ");
delete receiver->socket;
delete receiver;
}
LogPrint (eLogError, "BOB: missing inbound address");
}
}
}
void BOBI2PInboundTunnel::HandleDestinationRequestComplete (bool success, AddressReceiver * receiver, i2p::data::IdentHash ident)
void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver)
{
if (success)
{
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
if (leaseSet)
{
CreateConnection (receiver, leaseSet);
return;
}
else
LogPrint ("LeaseSet for BOB inbound destination not found");
}
delete receiver->socket;
delete receiver;
if (leaseSet)
CreateConnection (receiver, leaseSet);
else
LogPrint (eLogError, "BOB: LeaseSet for inbound destination not found");
}
void BOBI2PInboundTunnel::CreateConnection (AddressReceiver * receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
LogPrint ("New BOB inbound connection");
LogPrint (eLogDebug, "BOB: New inbound connection");
auto connection = std::make_shared<I2PTunnelConnection>(this, receiver->socket, leaseSet);
AddHandler (connection);
connection->I2PConnect (receiver->data, receiver->dataLen);
delete receiver;
}
BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port,
@@ -160,14 +135,14 @@ namespace client
if (localDestination)
localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1));
else
LogPrint ("Local destination not set for server tunnel");
LogPrint (eLogError, "BOB: Local destination not set for server tunnel");
}
void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint, m_IsQuiet);
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet);
AddHandler (conn);
conn->Connect ();
}
@@ -227,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)
{
}
@@ -254,7 +229,7 @@ namespace client
{
if (ecode)
{
LogPrint ("BOB command channel read error: ", ecode.message ());
LogPrint (eLogError, "BOB: command channel read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -281,7 +256,7 @@ namespace client
(this->*(it->second))(operand, eol - operand);
else
{
LogPrint (eLogError, "BOB unknown command ", m_ReceiveBuffer);
LogPrint (eLogError, "BOB: unknown command ", m_ReceiveBuffer);
SendReplyError ("unknown command");
}
@@ -294,7 +269,7 @@ namespace client
m_ReceiveBufferOffset = size;
else
{
LogPrint (eLogError, "Malformed input of the BOB command channel");
LogPrint (eLogError, "BOB: Malformed input of the command channel");
Terminate ();
}
}
@@ -313,7 +288,7 @@ namespace client
{
if (ecode)
{
LogPrint ("BOB command channel send error: ", ecode.message ());
LogPrint (eLogError, "BOB: command channel send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -379,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));
@@ -389,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)
@@ -409,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 ());
}
@@ -421,38 +409,44 @@ 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)
{
LogPrint (eLogDebug, "BOB: newkeys");
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys ();
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
}
void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: setkeys ", operand);
m_Keys.FromBase64 (operand);
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
}
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: getkeys");
SendReplyOK (m_Keys.ToBase64 ().c_str ());
}
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)
{
LogPrint (eLogDebug, "BOB: getdest");
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
}
void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
@@ -465,8 +459,11 @@ namespace client
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: outport ", operand);
m_OutPort = boost::lexical_cast<int>(operand);
SendReplyOK ("outbound port set");
m_OutPort = std::stoi(operand);
if (m_OutPort >= 0)
SendReplyOK ("outbound port set");
else
SendReplyError ("port out of range");
}
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
@@ -479,41 +476,71 @@ namespace client
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: inport ", operand);
m_InPort = boost::lexical_cast<int>(operand);
SendReplyOK ("inbound port set");
m_InPort = std::stoi(operand);
if (m_InPort >= 0)
SendReplyOK ("inbound port set");
else
SendReplyError ("port out of range");
}
void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: quiet");
m_IsQuiet = true;
SendReplyOK ("quiet");
if (m_Nickname.length () > 0)
{
if (!m_IsActive)
{
m_IsQuiet = true;
SendReplyOK ("Quiet set");
}
else
SendReplyError ("tunnel is active");
}
else
SendReplyError ("no nickname has been set");
}
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: lookup ", operand);
i2p::data::IdentityEx addr;
if (!context.GetAddressBook ().GetAddress (operand, addr))
i2p::data::IdentHash ident;
if (!context.GetAddressBook ().GetIdentHash (operand, ident))
{
SendReplyError ("Address Not found");
return;
}
SendReplyOK (addr.ToBase64 ().c_str ());
}
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
auto leaseSet = localDestination->FindLeaseSet (ident);
if (leaseSet)
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
else
{
auto s = shared_from_this ();
localDestination->RequestDestination (ident,
[s](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
s->SendReplyError ("LeaseSet Not found");
}
);
}
}
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: clear");
m_Owner.DeleteDestination (m_Nickname);
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");
}
@@ -524,18 +551,55 @@ 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 (int port):
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
{
// command -> handler
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
@@ -557,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;
}
@@ -576,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 ();
@@ -598,7 +663,7 @@ namespace client
}
catch (std::exception& ex)
{
LogPrint (eLogError, "BOB: ", ex.what ());
LogPrint (eLogError, "BOB: runtime exception: ", ex.what ());
}
}
}
@@ -641,11 +706,11 @@ namespace client
if (!ecode)
{
LogPrint (eLogInfo, "New BOB command connection from ", session->GetSocket ().remote_endpoint ());
LogPrint (eLogInfo, "BOB: New command connection from ", session->GetSocket ().remote_endpoint ());
session->SendVersion ();
}
else
LogPrint (eLogError, "BOB accept error: ", ecode.message ());
LogPrint (eLogError, "BOB: accept error: ", ecode.message ());
}
}
}

21
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";
@@ -57,9 +58,9 @@ namespace client
{
struct AddressReceiver
{
boost::asio::ip::tcp::socket * socket;
std::shared_ptr<boost::asio::ip::tcp::socket> socket;
char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address
uint8_t * data;
uint8_t * data; // pointer to buffer
size_t dataLen, bufferOffset;
AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {};
@@ -76,15 +77,15 @@ namespace client
private:
void Accept ();
void HandleAccept (const boost::system::error_code& ecode, AddressReceiver * receiver);
void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver);
void ReceiveAddress (AddressReceiver * receiver);
void ReceiveAddress (std::shared_ptr<AddressReceiver> receiver);
void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
AddressReceiver * receiver);
std::shared_ptr<AddressReceiver> receiver);
void HandleDestinationRequestComplete (bool success, AddressReceiver * receiver, i2p::data::IdentHash ident);
void HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver);
void CreateConnection (AddressReceiver * receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet);
void CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet);
private:
@@ -127,6 +128,7 @@ namespace client
void CreateInboundTunnel (int port);
void CreateOutboundTunnel (const std::string& address, int port, bool quiet);
const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); };
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDestination; };
private:
@@ -167,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:
@@ -185,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;
@@ -198,7 +201,7 @@ namespace client
{
public:
BOBCommandChannel (int port);
BOBCommandChannel (const std::string& address, int port);
~BOBCommandChannel ();
void Start ();

View File

@@ -1,10 +1,23 @@
#include <stdlib.h>
#include "base64.h"
#include <string.h>
#include "Base.h"
namespace i2p
{
namespace data
{
static const char T32[32] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '2', '3', '4', '5', '6', '7',
};
const char * GetBase32SubstitutionTable ()
{
return T32;
}
static void iT64Build(void);
@@ -16,7 +29,7 @@ namespace data
* Direct Substitution Table
*/
static char T64[64] = {
static const char T64[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
@@ -154,7 +167,7 @@ namespace data
if (isFirstTime) iT64Build();
n = InCount/4;
m = InCount%4;
if (!m)
if (InCount && !m)
outCount = 3*n;
else {
outCount = 0;
@@ -190,6 +203,13 @@ namespace data
return outCount;
}
size_t Base64EncodingBufferSize (const size_t input_size)
{
auto d = div (input_size, 3);
if (d.rem) d.quot++;
return 4*d.quot;
}
/*
*
* iT64
@@ -267,3 +287,4 @@ namespace data
}
}
}

View File

@@ -1,22 +1,25 @@
#ifndef BASE64_H
#define BASE64_H
#ifndef BASE_H__
#define BASE_H__
#include <inttypes.h>
#include <string.h>
namespace i2p
{
namespace data
{
#include <string>
#include <iostream>
namespace i2p {
namespace data {
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase32SubstitutionTable ();
const char * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
}
}
/**
Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
*/
size_t Base64EncodingBufferSize(const size_t input_size);
} // data
} // i2p
#endif

116
ChangeLog Normal file
View File

@@ -0,0 +1,116 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.10.0] - 2016-10-17
### Added
- Datagram i2p tunnels
- Unique local addresses for server tunnels
- Configurable list of reseed servers and initial addressbook
- Configurable netid
- Initial iOS support
### Changed
- Reduced file descriptiors usage
- Strict reseed checks enabled by default
## Fixed
- Multiple fixes in I2CP and BOB implementations
## [2.9.0] - 2016-08-12
### Changed
- Proxy refactoring & speedup
- Transmission-I2P support
- Graceful shutdown for Windows
- Android without QT
- Reduced number of timers in SSU
- ipv6 peer test support
- Reseed from SU3 file
## [2.8.0] - 2016-06-20
### Added
- Basic Android support
- I2CP implementation
- 'doxygen' target
### Changed
- I2PControl refactoring & fixes (proper jsonrpc responses on errors)
- boost::regex no more needed
### Fixed
- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional
- properly close NTCP sessions (memleak)
## [2.7.0] - 2016-05-18
### Added
- 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

@@ -2,9 +2,11 @@
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "util.h"
#include "Config.h"
#include "FS.h"
#include "Log.h"
#include "Identity.h"
#include "util.h"
#include "ClientContext.h"
namespace i2p
@@ -15,7 +17,7 @@ namespace client
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
m_BOBCommandChannel (nullptr), m_I2PControlService (nullptr)
m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr)
{
}
@@ -25,7 +27,7 @@ namespace client
delete m_SocksProxy;
delete m_SamBridge;
delete m_BOBCommandChannel;
delete m_I2PControlService;
delete m_I2CPServer;
}
void ClientContext::Start ()
@@ -33,132 +35,219 @@ namespace client
if (!m_SharedLocalDestination)
{
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination;
m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination;
m_SharedLocalDestination->Start ();
}
m_AddressBook.Start ();
std::shared_ptr<ClientDestination> localDestination;
// proxies
std::string proxyKeys = i2p::util::config::GetArg("-proxykeys", "");
if (proxyKeys.length () > 0)
localDestination = LoadLocalDestination (proxyKeys, false);
m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446), localDestination);
m_HttpProxy->Start();
LogPrint("HTTP Proxy started");
m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447), localDestination);
m_SocksProxy->Start();
LogPrint("SOCKS Proxy Started");
// I2P tunnels
std::string ircDestination = i2p::util::config::GetArg("-ircdest", "");
if (ircDestination.length () > 0) // ircdest is presented
{
localDestination = nullptr;
std::string ircKeys = i2p::util::config::GetArg("-irckeys", "");
if (ircKeys.length () > 0)
localDestination = LoadLocalDestination (ircKeys, false);
auto ircPort = i2p::util::config::GetArg("-ircport", 6668);
auto ircTunnel = new I2PClientTunnel (ircDestination, ircPort, localDestination);
ircTunnel->Start ();
m_ClientTunnels.insert (std::make_pair(ircPort, std::unique_ptr<I2PClientTunnel>(ircTunnel)));
LogPrint("IRC tunnel started");
}
std::string eepKeys = i2p::util::config::GetArg("-eepkeys", "");
if (eepKeys.length () > 0) // eepkeys file is presented
{
localDestination = LoadLocalDestination (eepKeys, true);
auto serverTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"),
i2p::util::config::GetArg("-eepport", 80), localDestination);
serverTunnel->Start ();
m_ServerTunnels.insert (std::make_pair(localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel)));
LogPrint("Server tunnel started");
bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy);
if (httproxy) {
std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys);
std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr);
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
if (httpProxyKeys.length () > 0)
{
i2p::data::PrivateKeys keys;
if(LoadPrivateKeys (keys, httpProxyKeys))
{
std::map<std::string, std::string> params;
ReadI2CPOptionsFromConfig ("httpproxy.", params);
localDestination = CreateNewLocalDestination (keys, false, &params);
}
else
LogPrint(eLogError, "Clients: failed to load HTTP Proxy key");
}
try {
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
m_HttpProxy->Start();
} catch (std::exception& e) {
LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what());
}
}
ReadTunnels ();
// SAM
int samPort = i2p::util::config::GetArg("-samport", 0);
if (samPort)
localDestination = nullptr;
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
if (socksproxy)
{
m_SamBridge = new SAMBridge (samPort);
m_SamBridge->Start ();
LogPrint("SAM bridge started");
std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys);
std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr);
uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort);
std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr);
uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort);
LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort);
if (socksProxyKeys.length () > 0)
{
i2p::data::PrivateKeys keys;
if (LoadPrivateKeys (keys, socksProxyKeys))
{
std::map<std::string, std::string> params;
ReadI2CPOptionsFromConfig ("socksproxy.", params);
localDestination = CreateNewLocalDestination (keys, false, &params);
}
else
LogPrint(eLogError, "Clients: failed to load SOCKS Proxy key");
}
try {
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
m_SocksProxy->Start();
} catch (std::exception& e) {
LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what());
}
}
// I2P tunnels
ReadTunnels ();
// SAM
bool sam; i2p::config::GetOption("sam.enabled", sam);
if (sam) {
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
try {
m_SamBridge = new SAMBridge (samAddr, samPort);
m_SamBridge->Start ();
} catch (std::exception& e) {
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
}
}
// BOB
int bobPort = i2p::util::config::GetArg("-bobport", 0);
if (bobPort)
{
m_BOBCommandChannel = new BOBCommandChannel (bobPort);
m_BOBCommandChannel->Start ();
LogPrint("BOB command channel started");
bool bob; i2p::config::GetOption("bob.enabled", bob);
if (bob) {
std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr);
uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort);
LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort);
try {
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
m_BOBCommandChannel->Start ();
} catch (std::exception& e) {
LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what());
}
}
// I2P Control
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
if (i2pcontrolPort)
// I2CP
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
if (i2cp)
{
m_I2PControlService = new I2PControlService (i2pcontrolPort);
m_I2PControlService->Start ();
LogPrint("I2PControl started");
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();
}
m_AddressBook.Start ();
}
void ClientContext::Stop ()
{
m_HttpProxy->Stop();
delete m_HttpProxy;
m_HttpProxy = nullptr;
LogPrint("HTTP Proxy stopped");
m_SocksProxy->Stop();
delete m_SocksProxy;
m_SocksProxy = nullptr;
LogPrint("SOCKS Proxy stopped");
if (m_HttpProxy)
{
LogPrint(eLogInfo, "Clients: stopping HTTP Proxy");
m_HttpProxy->Stop();
delete m_HttpProxy;
m_HttpProxy = nullptr;
}
if (m_SocksProxy)
{
LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy");
m_SocksProxy->Stop();
delete m_SocksProxy;
m_SocksProxy = nullptr;
}
for (auto& it: m_ClientTunnels)
{
LogPrint(eLogInfo, "Clients: stopping I2P client tunnel on port ", it.first);
it.second->Stop ();
LogPrint("I2P client tunnel on port ", it.first, " stopped");
}
m_ClientTunnels.clear ();
for (auto& it: m_ServerTunnels)
{
LogPrint(eLogInfo, "Clients: stopping I2P server tunnel");
it.second->Stop ();
LogPrint("I2P server tunnel stopped");
}
m_ServerTunnels.clear ();
if (m_SamBridge)
{
LogPrint(eLogInfo, "Clients: stopping SAM bridge");
m_SamBridge->Stop ();
delete m_SamBridge;
m_SamBridge = nullptr;
LogPrint("SAM brdige stopped");
}
if (m_BOBCommandChannel)
{
LogPrint(eLogInfo, "Clients: stopping BOB command channel");
m_BOBCommandChannel->Stop ();
delete m_BOBCommandChannel;
m_BOBCommandChannel = nullptr;
LogPrint("BOB command channel stopped");
}
if (m_I2PControlService)
}
if (m_I2CPServer)
{
m_I2PControlService->Stop ();
delete m_I2PControlService;
m_I2PControlService = nullptr;
LogPrint("I2PControl stopped");
}
m_AddressBook.Stop ();
for (auto it: m_Destinations)
LogPrint(eLogInfo, "Clients: stopping I2CP");
m_I2CPServer->Stop ();
delete m_I2CPServer;
m_I2CPServer = nullptr;
}
LogPrint(eLogInfo, "Clients: stopping AddressBook");
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;
}
std::shared_ptr<ClientDestination> ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic)
void ClientContext::ReloadConfig ()
{
i2p::data::PrivateKeys keys;
std::string fullPath = i2p::util::filesystem::GetFullPath (filename);
std::ifstream s(fullPath.c_str (), std::ifstream::binary);
std::string config; i2p::config::GetOption("conf", config);
i2p::config::ParseConfig(config);
Stop();
Start();
}
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 ())
{
s.seekg (0, std::ios::end);
@@ -166,14 +255,19 @@ 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 ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded");
}
else
{
LogPrint ("Can't open file ", fullPath, " Creating new one");
keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
LogPrint (eLogError, "Clients: can't open file ", fullPath, " Creating new one with signature type ", sigType);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
size_t len = keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
@@ -181,26 +275,34 @@ namespace client
f.write ((char *)buf, len);
delete[] buf;
LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created");
}
std::shared_ptr<ClientDestination> localDestination = nullptr;
std::unique_lock<std::mutex> l(m_DestinationsMutex);
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " alreday exists");
localDestination = it->second;
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
}
else
{
localDestination = std::make_shared<ClientDestination> (keys, isPublic);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
}
return localDestination;
return success;
}
std::vector<std::shared_ptr<DatagramSessionInfo> > ClientContext::GetForwardInfosFor(const i2p::data::IdentHash & destination)
{
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
for(const auto & c : m_ClientForwards)
{
if (c.second->IsLocalDestination(destination))
{
for (auto & i : c.second->GetSessions()) infos.push_back(i);
break;
}
}
for(const auto & s : m_ServerForwards)
{
if(std::get<0>(s.first) == destination)
{
for( auto & i : s.second->GetSessions()) infos.push_back(i);
break;
}
}
return infos;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
const std::map<std::string, std::string> * params)
{
@@ -230,10 +332,10 @@ namespace client
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params)
{
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists");
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
if (!it->second->IsRunning ())
{
it->second->Start ();
@@ -243,7 +345,7 @@ namespace client
}
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination;
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
}
@@ -256,16 +358,56 @@ namespace client
return nullptr;
}
template<typename Section, typename Type>
std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const
{
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
}
template<typename Section>
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
{
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH);
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH);
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
}
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
{
std::string value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value))
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value))
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value))
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value))
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value;
}
void ClientContext::ReadTunnels ()
{
boost::property_tree::ptree pt;
try
{
boost::property_tree::read_ini (i2p::util::filesystem::GetFullPath (TUNNELS_CONFIG_FILENAME), pt);
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
if (tunConf == "") {
// TODO: cleanup this in 2.8.0
tunConf = i2p::fs::DataDirPath ("tunnels.cfg");
if (i2p::fs::Exists(tunConf)) {
LogPrint(eLogWarning, "FS: please rename tunnels.cfg -> tunnels.conf here: ", tunConf);
} else {
tunConf = i2p::fs::DataDirPath ("tunnels.conf");
}
}
catch (std::exception& ex)
LogPrint(eLogDebug, "FS: tunnels config file: ", tunConf);
try
{
LogPrint (eLogWarning, "Can't read ", TUNNELS_CONFIG_FILENAME, ": ", ex.what ());
boost::property_tree::read_ini (tunConf, pt);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ());
return;
}
@@ -276,26 +418,61 @@ 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);
int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT);
// optional params
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1");
int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0);
i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
// I2CP
std::map<std::string, std::string> options;
ReadI2CPOptions (section, options);
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (keys.length () > 0)
localDestination = LoadLocalDestination (keys, false);
auto clientTunnel = new I2PClientTunnel (dest, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
clientTunnel->Start ();
else
LogPrint (eLogError, "I2P client tunnel with port ", port, " already exists");
numClientTunnels++;
{
i2p::data::PrivateKeys k;
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 = 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");
}
}
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER)
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);
@@ -303,10 +480,60 @@ namespace client
std::string keys = section.second.get<std::string> (I2P_SERVER_TUNNEL_KEYS);
// optional params
int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0);
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, "");
std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, "");
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true);
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
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);
auto localDestination = LoadLocalDestination (keys, true);
auto serverTunnel = new I2PServerTunnel (host, port, localDestination, inPort);
std::shared_ptr<ClientDestination> localDestination = nullptr;
i2p::data::PrivateKeys k;
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);
else if (type == I2P_TUNNELS_SECTION_TYPE_IRC)
serverTunnel = new I2PServerTunnelIRC (name, host, port, localDestination, webircpass, inPort, gzip);
else // regular server tunnel by default
serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip);
LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns);
serverTunnel->SetMaxConnsPerMinute(maxConns);
if (accessList.length () > 0)
{
std::set<i2p::data::IdentHash> idents;
@@ -322,23 +549,48 @@ namespace client
while (comma != std::string::npos);
serverTunnel->SetAccessList (idents);
}
if (m_ServerTunnels.insert (std::make_pair (localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
if (m_ServerTunnels.insert (std::make_pair (
std::make_pair (localDestination->GetIdentHash (), inPort),
std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
{
serverTunnel->Start ();
numServerTunnels++;
}
else
LogPrint (eLogError, "I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
numServerTunnels++;
LogPrint (eLogError, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists");
}
else
LogPrint (eLogWarning, "Unknown section type=", type, " of ", name, " in ", TUNNELS_CONFIG_FILENAME);
LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read tunnel ", name, " params: ", ex.what ());
LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ());
}
}
LogPrint (eLogInfo, numClientTunnels, " I2P client tunnels created");
LogPrint (eLogInfo, numServerTunnels, " I2P server tunnels created");
}
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

@@ -4,14 +4,16 @@
#include <map>
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "Destination.h"
#include "I2PService.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
#include "I2PTunnel.h"
#include "SAM.h"
#include "BOB.h"
#include "I2CP.h"
#include "AddressBook.h"
#include "I2PControl.h"
namespace i2p
{
@@ -20,17 +22,27 @@ namespace client
const char I2P_TUNNELS_SECTION_TYPE[] = "type";
const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client";
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc";
const char I2P_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";
const char I2P_CLIENT_TUNNEL_KEYS[] = "keys";
const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
const char I2P_SERVER_TUNNEL_HOST[] = "host";
const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride";
const char I2P_SERVER_TUNNEL_PORT[] = "port";
const char I2P_SERVER_TUNNEL_KEYS[] = "keys";
const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
const char I2P_SERVER_TUNNEL_INPORT[] = "inport";
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
const char TUNNELS_CONFIG_FILENAME[] = "tunnels.cfg";
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:
@@ -41,6 +53,8 @@ namespace client
void Start ();
void Stop ();
void ReloadConfig ();
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
std::shared_ptr<ClientDestination> CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
const std::map<std::string, std::string> * params = nullptr); // transient
@@ -48,15 +62,25 @@ 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;
std::shared_ptr<ClientDestination> LoadLocalDestination (const std::string& filename, bool isPublic);
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 ();
template<typename Section, typename Type>
std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const;
template<typename Section>
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const;
void CleanupUDP(const boost::system::error_code & ecode);
void ScheduleCleanupUDP();
private:
std::mutex m_DestinationsMutex;
@@ -67,15 +91,27 @@ namespace client
i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy;
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel
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;
I2PControlService * m_I2PControlService;
I2CPServer * m_I2CPServer;
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
public:
// for HTTP
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; };
const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; };
const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; }
const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; }
const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; }
};
extern ClientContext context;

270
Config.cpp Normal file
View File

@@ -0,0 +1,270 @@
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include "Config.h"
#include "version.h"
using namespace boost::program_options;
namespace i2p {
namespace config {
options_description m_OptionsDesc;
variables_map m_Options;
void Init() {
options_description general("General options");
general.add_options()
("help", "Show this message")
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)")
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
("loglevel", value<std::string>()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error)")
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
("nat", value<bool>()->zero_tokens()->default_value(true), "Should we assume we are behind NAT?")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", value<bool>()->zero_tokens()->default_value(true), "Enable communication through ipv4")
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", value<bool>()->zero_tokens()->default_value(false), "Router will go to background after start")
("service", value<bool>()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'")
("notransit", value<bool>()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup")
("floodfill", value<bool>()->zero_tokens()->default_value(false), "Router will be floodfill")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in kbps or letters: L (32), O (256), P (2048), X (>9000)")
("ntcp", value<bool>()->zero_tokens()->default_value(true), "Enable NTCP transport")
("ssu", value<bool>()->zero_tokens()->default_value(true), "Enable SSU transport")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") // TODO: add custom validator or something
#endif
;
options_description limits("Limits options");
limits.add_options()
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
;
options_description httpserver("HTTP Server options");
httpserver.add_options()
("http.enabled", value<bool>()->default_value(true), "Enable or disable webconsole")
("http.address", value<std::string>()->default_value("127.0.0.1"), "Webconsole listen address")
("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port")
("http.auth", value<bool>()->default_value(false), "Enable Basic HTTP auth for webconsole")
("http.user", value<std::string>()->default_value("i2pd"), "Username for basic auth")
("http.pass", value<std::string>()->default_value(""), "Password for basic auth (default: random, see logs)")
;
options_description httpproxy("HTTP Proxy options");
httpproxy.add_options()
("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy")
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
("httpproxy.keys", value<std::string>()->default_value(""), "File to persist HTTP Proxy keys")
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length")
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
;
options_description socksproxy("SOCKS Proxy options");
socksproxy.add_options()
("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy")
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys")
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length")
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
;
options_description sam("SAM bridge options");
sam.add_options()
("sam.enabled", value<bool>()->default_value(false), "Enable or disable SAM Application bridge")
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
;
options_description bob("BOB options");
bob.add_options()
("bob.enabled", value<bool>()->default_value(false), "Enable or disable BOB command channel")
("bob.address", value<std::string>()->default_value("127.0.0.1"), "BOB listen address")
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
;
options_description 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")
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
;
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",
#if defined(__x86_64__)
value<bool>()->default_value(false),
#else
value<bool>()->default_value(true),
#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")
("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")
;
options_description addressbook("AddressBook options");
addressbook.add_options()
("addressbook.defaulturl", value<std::string>()->default_value(
"http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"
), "AddressBook subscription URL for initial setup")
("addressbook.subscriptions", value<std::string>()->default_value(""),
"AddressBook subscriptions URLs, separated by comma");
options_description trust("Trust options");
trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?");
options_description websocket("Websocket Options");
websocket.add_options()
("websockets.enabled", value<bool>()->default_value(false), "enable websocket server")
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on")
("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on");
m_OptionsDesc
.add(general)
.add(limits)
.add(httpserver)
.add(httpproxy)
.add(socksproxy)
.add(sam)
.add(bob)
.add(i2cp)
.add(i2pcontrol)
.add(upnp)
.add(precomputation)
.add(reseed)
.add(addressbook)
.add(trust)
.add(websocket)
;
}
void ParseCmdline(int argc, char* argv[]) {
try {
auto style = boost::program_options::command_line_style::unix_style
| boost::program_options::command_line_style::allow_long_disguise;
style &= ~ boost::program_options::command_line_style::allow_guessing;
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
} catch (boost::program_options::error& e) {
std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
if (m_Options.count("help") || m_Options.count("h")) {
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
std::cout << m_OptionsDesc;
exit(EXIT_SUCCESS);
}
}
void ParseConfig(const std::string& path) {
if (path == "") return;
std::ifstream config(path, std::ios::in);
if (!config.is_open())
{
std::cerr << "missing/unreadable config file: " << path << std::endl;
exit(EXIT_FAILURE);
}
try
{
store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options);
}
catch (boost::program_options::error& e)
{
std::cerr << e.what() << std::endl;
exit(EXIT_FAILURE);
};
}
void Finalize() {
notify(m_Options);
}
bool IsDefault(const char *name) {
if (!m_Options.count(name))
throw "try to check non-existent option";
if (m_Options[name].defaulted())
return true;
return false;
}
} // namespace config
} // namespace i2p

113
Config.h Normal file
View File

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

836
Crypto.cpp Normal file
View File

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

View File

@@ -1,22 +1,81 @@
#ifndef AES_H__
#define AES_H__
#ifndef CRYPTO_H__
#define CRYPTO_H__
#include <inttypes.h>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include "Identity.h"
#include <string>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/aes.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Base.h"
#include "Tag.h"
namespace i2p
{
namespace crypto
{
{
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
// DSA
DSA * CreateDSA ();
// RSA
const BIGNUM * GetRSAE ();
// DH
class DHKeys
{
public:
DHKeys ();
~DHKeys ();
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
const uint8_t * GetPublicKey ();
void Agree (const uint8_t * pub, uint8_t * shared);
private:
DH * m_DH;
uint8_t m_PublicKey[256];
bool m_IsUpdated;
};
// ElGamal
class ElGamalEncryption
{
public:
ElGamalEncryption (const uint8_t * key);
~ElGamalEncryption ();
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const;
private:
BN_CTX * ctx;
BIGNUM * a, * b1;
};
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false);
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
// HMAC
typedef i2p::data::Tag<32> MACKey;
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
// AES
struct ChipherBlock
{
uint8_t buf[16];
void operator^=(const ChipherBlock& other) // XOR
{
#if defined(__x86_64__) // for Intel x64
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE
__asm__
(
"movups (%[buf]), %%xmm0 \n"
@@ -95,7 +154,7 @@ namespace crypto
typedef ECBEncryptionAESNI ECBEncryption;
typedef ECBDecryptionAESNI ECBDecryption;
#else // use crypto++
#else // use openssl
class ECBEncryption
{
@@ -103,16 +162,16 @@ namespace crypto
void SetKey (const AESKey& key)
{
m_Encryption.SetKey (key, 32);
AES_set_encrypt_key (key, 256, &m_Key);
}
void Encrypt (const ChipherBlock * in, ChipherBlock * out)
{
m_Encryption.ProcessData (out->buf, in->buf, 16);
AES_encrypt (in->buf, out->buf, &m_Key);
}
private:
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_Encryption;
AES_KEY m_Key;
};
class ECBDecryption
@@ -121,16 +180,16 @@ namespace crypto
void SetKey (const AESKey& key)
{
m_Decryption.SetKey (key, 32);
AES_set_decrypt_key (key, 256, &m_Key);
}
void Decrypt (const ChipherBlock * in, ChipherBlock * out)
{
m_Decryption.ProcessData (out->buf, in->buf, 16);
AES_decrypt (in->buf, out->buf, &m_Key);
}
private:
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_Decryption;
AES_KEY m_Key;
};
@@ -185,7 +244,7 @@ namespace crypto
m_IVEncryption.SetKey (ivKey);
}
void Encrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data)
void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
private:
@@ -207,7 +266,7 @@ namespace crypto
m_IVDecryption.SetKey (ivKey);
}
void Decrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data)
void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
private:
@@ -217,9 +276,34 @@ namespace crypto
#else
CBCDecryption m_LayerDecryption;
#endif
};
}
}
};
void InitCrypto (bool precomputation);
void TerminateCrypto ();
// take care about openssl version
#include <openssl/opensslv.h>
#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL
// define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{ d->p = p; d->q = q; d->g = g; return 1; }
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
{ d->pub_key = pub_key; d->priv_key = priv_key; return 1; }
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{ sig->r = r; sig->s = s; return 1; }
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
{ r->n = n; r->e = e; r->d = d; return 1; }
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
{ *n = r->n; *e = r->e; *d = r->d; }
#endif
}
}
#endif

View File

@@ -1,73 +0,0 @@
#include <inttypes.h>
#include "CryptoConst.h"
namespace i2p
{
namespace crypto
{
const uint8_t elgp_[256]=
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
const uint8_t dsap_[128]=
{
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
};
const uint8_t dsaq_[20]=
{
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
0x68, 0x40, 0x46, 0xb7
};
const uint8_t dsag_[128]=
{
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
};
const CryptoConstants& GetCryptoConstants ()
{
static CryptoConstants cryptoConstants =
{
{elgp_, 256}, // elgp
{2}, // elgg
{dsap_, 128}, // dsap
{dsaq_, 20}, // dsaq
{dsag_, 128} // dsag
};
return cryptoConstants;
}
}
}

View File

@@ -1,38 +0,0 @@
#ifndef CRYPTO_CONST_H__
#define CRYPTO_CONST_H__
#include <cryptopp/integer.h>
namespace i2p
{
namespace crypto
{
struct CryptoConstants
{
// DH/ElGamal
const CryptoPP::Integer elgp;
const CryptoPP::Integer elgg;
// DSA
const CryptoPP::Integer dsap;
const CryptoPP::Integer dsaq;
const CryptoPP::Integer dsag;
};
const CryptoConstants& GetCryptoConstants ();
// DH/ElGamal
#define elgp GetCryptoConstants ().elgp
#define elgg GetCryptoConstants ().elgg
// DSA
#define dsap GetCryptoConstants ().dsap
#define dsaq GetCryptoConstants ().dsaq
#define dsag GetCryptoConstants ().dsag
// RSA
const int rsae = 65537;
}
}
#endif

View File

@@ -1,27 +1,32 @@
#include <thread>
#include <memory>
#include "Daemon.h"
#include "Config.h"
#include "Log.h"
#include "base64.h"
#include "FS.h"
#include "Base.h"
#include "version.h"
#include "Transports.h"
#include "NTCPSession.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "HTTP.h"
#include "NetDb.h"
#include "Garlic.h"
#include "util.h"
#include "Streaming.h"
#include "Destination.h"
#include "HTTPServer.h"
#include "I2PControl.h"
#include "ClientContext.h"
#ifdef USE_UPNP
#include "Crypto.h"
#include "UPnP.h"
#endif
#include "util.h"
#include "Event.h"
#include "Websocket.h"
namespace i2p
{
@@ -30,126 +35,324 @@ namespace i2p
class Daemon_Singleton::Daemon_Singleton_Private
{
public:
Daemon_Singleton_Private() : httpServer(nullptr)
{};
~Daemon_Singleton_Private()
{
delete httpServer;
};
Daemon_Singleton_Private() {};
~Daemon_Singleton_Private() {};
i2p::util::HTTPServer *httpServer;
std::unique_ptr<i2p::http::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
std::unique_ptr<i2p::transport::UPnP> UPnP;
#ifdef WITH_EVENTS
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
#endif
};
Daemon_Singleton::Daemon_Singleton() : 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
{
bool service = false;
#ifndef _WIN32
return i2p::util::config::GetArg("-service", 0);
#else
return false;
i2p::config::GetOption("service", service);
#endif
return service;
}
bool Daemon_Singleton::init(int argc, char* argv[])
{
i2p::util::config::OptionParser(argc, argv);
i2p::config::Init();
i2p::config::ParseCmdline(argc, argv);
std::string config; i2p::config::GetOption("conf", config);
std::string datadir; i2p::config::GetOption("datadir", datadir);
i2p::fs::DetectDataDir(datadir, IsService());
i2p::fs::Init();
datadir = i2p::fs::GetDataDir();
// TODO: drop old name detection in v2.8.0
if (config == "")
{
config = i2p::fs::DataDirPath("i2p.conf");
if (i2p::fs::Exists (config)) {
LogPrint(eLogWarning, "Daemon: please rename i2p.conf to i2pd.conf here: ", config);
} else {
config = i2p::fs::DataDirPath("i2pd.conf");
if (!i2p::fs::Exists (config)) {
// use i2pd.conf only if exists
config = ""; /* reset */
}
}
}
i2p::config::ParseConfig(config);
i2p::config::Finalize();
i2p::config::GetOption("daemon", isDaemon);
std::string logs = ""; i2p::config::GetOption("log", logs);
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
/* setup logging */
if (isDaemon && (logs == "" || logs == "stdout"))
logs = "file";
i2p::log::Logger().SetLogLevel(loglevel);
if (logs == "file") {
if (logfile == "")
logfile = i2p::fs::DataDirPath("i2pd.log");
LogPrint(eLogInfo, "Log: will send messages to ", logfile);
i2p::log::Logger().SendTo (logfile);
#ifndef _WIN32
} else if (logs == "syslog") {
LogPrint(eLogInfo, "Log: will send messages to syslog");
i2p::log::Logger().SendTo("i2pd", LOG_DAEMON);
#endif
} else {
// use stdout -- default
}
i2p::log::Logger().Ready();
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir);
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
i2p::crypto::InitCrypto (precomputation);
int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID);
i2p::context.Init ();
LogPrint("\n\n\n\ni2pd starting\n");
LogPrint("Version ", VERSION);
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
isDaemon = i2p::util::config::GetArg("-daemon", 0);
isLogging = i2p::util::config::GetArg("-log", 1);
int port = i2p::util::config::GetArg("-port", 0);
if (port)
i2p::context.UpdatePort (port);
const char * host = i2p::util::config::GetCharArg("-host", "");
if (host && host[0])
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0));
i2p::context.SetFloodfill (i2p::util::config::GetArg("-floodfill", 0));
auto bandwidth = i2p::util::config::GetArg("-bandwidth", "");
if (bandwidth.length () > 0)
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
#ifdef MESHNET
// manual override for meshnet
ipv4 = false;
ipv6 = true;
#endif
uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port"))
{
if (bandwidth[0] > 'L')
i2p::context.SetHighBandwidth ();
else
i2p::context.SetLowBandwidth ();
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);
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
if (isFloodfill) {
LogPrint(eLogInfo, "Daemon: router will be floodfill");
i2p::context.SetFloodfill (true);
} else {
i2p::context.SetFloodfill (false);
}
/* this section also honors 'floodfill' flag, if set above */
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
if (bandwidth.length () > 0)
{
if (bandwidth[0] >= 'K' && bandwidth[0] <= 'X')
{
i2p::context.SetBandwidth (bandwidth[0]);
LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps");
}
else
{
auto value = std::atoi(bandwidth.c_str());
if (value > 0)
{
i2p::context.SetBandwidth (value);
LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), " KBps");
}
else
{
LogPrint(eLogInfo, "Daemon: unexpected bandwidth ", bandwidth, ". Set to 'low'");
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
}
}
}
else if (isFloodfill)
{
LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'");
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1);
}
else
{
LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'");
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
}
LogPrint("CMD parameters:");
for (int i = 0; i < argc; ++i)
LogPrint(i, " ", argv[i]);
std::string family; i2p::config::GetOption("family", family);
i2p::context.SetFamily (family);
if (family.length () > 0)
LogPrint(eLogInfo, "Daemon: family set to ", family);
return true;
bool trust; i2p::config::GetOption("trust.enabled", trust);
if (trust)
{
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
std::string fam; i2p::config::GetOption("trust.family", fam);
std::string routers; i2p::config::GetOption("trust.routers", routers);
bool restricted = false;
if (fam.length() > 0)
{
std::set<std::string> fams;
size_t pos = 0, comma;
do
{
comma = fam.find (',', pos);
fams.insert (fam.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
pos = comma + 1;
}
while (comma != std::string::npos);
i2p::transport::transports.RestrictRoutesToFamilies(fams);
restricted = fams.size() > 0;
}
if (routers.length() > 0) {
std::set<i2p::data::IdentHash> idents;
size_t pos = 0, comma;
do
{
comma = routers.find (',', pos);
i2p::data::IdentHash ident;
ident.FromBase64 (routers.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
idents.insert (ident);
pos = comma + 1;
}
while (comma != std::string::npos);
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routesrs");
i2p::transport::transports.RestrictRoutesToRouters(idents);
restricted = idents.size() > 0;
}
if(!restricted)
LogPrint(eLogError, "Daemon: no trusted routers of families specififed");
}
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
if (hidden)
{
LogPrint(eLogInfo, "Daemon: using hidden mode");
i2p::data::netdb.SetHidden(true);
}
return true;
}
bool Daemon_Singleton::start()
{
// initialize log
if (isLogging)
{
if (isDaemon)
{
std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string();
#ifndef _WIN32
logfile_path.append("/i2pd.log");
#else
logfile_path.append("\\i2pd.log");
#endif
StartLog (logfile_path);
}
else
StartLog (""); // write to stdout
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 ();
}
d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070));
d.httpServer->Start();
LogPrint("HTTP Server started");
i2p::data::netdb.Start();
LogPrint("NetDB started");
i2p::transport::transports.Start();
LogPrint("Transports started");
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);
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 Tunnels");
i2p::tunnel::tunnels.Start();
LogPrint("Tunnels started");
LogPrint(eLogInfo, "Daemon: starting Client");
i2p::client::context.Start ();
LogPrint("Client started");
#ifdef USE_UPNP
i2p::UPnP::upnpc.Start();
LogPrint("UPnP module loaded");
// I2P Control Protocol
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
if (i2pcontrol) {
std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr);
uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort);
LogPrint(eLogInfo, "Daemon: starting I2PControl at ", i2pcpAddr, ":", i2pcpPort);
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
d.m_I2PControlService->Start ();
}
#ifdef WITH_EVENTS
bool websocket; i2p::config::GetOption("websockets.enabled", websocket);
if(websocket) {
std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr);
uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort);
LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort);
d.m_WebsocketServer = std::unique_ptr<i2p::event::WebsocketServer>(new i2p::event::WebsocketServer (websocketAddr, websocketPort));
d.m_WebsocketServer->Start();
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
}
#endif
return true;
}
bool Daemon_Singleton::stop()
{
LogPrint("Shutdown started.");
i2p::client::context.Stop();
LogPrint("Client stopped");
i2p::tunnel::tunnels.Stop();
LogPrint("Tunnels stopped");
i2p::transport::transports.Stop();
LogPrint("Transports stopped");
i2p::data::netdb.Stop();
LogPrint("NetDB stopped");
d.httpServer->Stop();
LogPrint("HTTP Server stopped");
#ifdef USE_UPNP
i2p::UPnP::upnpc.Stop();
#ifdef WITH_EVENTS
i2p::event::core.SetListener(nullptr);
#endif
StopLog ();
LogPrint(eLogInfo, "Daemon: shutting down");
LogPrint(eLogInfo, "Daemon: stopping Client");
i2p::client::context.Stop();
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
i2p::tunnel::tunnels.Stop();
delete d.httpServer; d.httpServer = nullptr;
if (d.UPnP) {
d.UPnP->Stop ();
d.UPnP = nullptr;
}
LogPrint(eLogInfo, "Daemon: stopping Transports");
i2p::transport::transports.Stop();
LogPrint(eLogInfo, "Daemon: stopping NetDB");
i2p::data::netdb.Stop();
if (d.httpServer) {
LogPrint(eLogInfo, "Daemon: stopping HTTP Server");
d.httpServer->Stop();
d.httpServer = nullptr;
}
if (d.m_I2PControlService)
{
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
d.m_I2PControlService->Stop ();
d.m_I2PControlService = nullptr;
}
#ifdef WITH_EVENTS
if (d.m_WebsocketServer) {
LogPrint(eLogInfo, "Daemon: stopping Websocket server");
d.m_WebsocketServer->Stop();
d.m_WebsocketServer = nullptr;
}
#endif
i2p::crypto::TerminateCrypto ();
return true;
}
}
}
}

View File

@@ -1,11 +1,8 @@
#pragma once
#include <string>
#ifndef DAEMON_H__
#define DAEMON_H__
#ifdef _WIN32
#define Daemon i2p::util::DaemonWin32::Instance()
#else
#define Daemon i2p::util::DaemonLinux::Instance()
#endif
#include <memory>
#include <string>
namespace i2p
{
@@ -18,24 +15,52 @@ namespace i2p
virtual bool init(int argc, char* argv[]);
virtual bool start();
virtual bool stop();
virtual void run () {};
int isLogging;
int isDaemon;
int running;
bool isDaemon;
bool running;
protected:
Daemon_Singleton();
virtual ~Daemon_Singleton();
bool IsService () const;
bool IsService () const;
// d-pointer for httpServer, httpProxy, etc.
class Daemon_Singleton_Private;
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:
@@ -45,27 +70,38 @@ namespace i2p
return instance;
}
virtual bool init(int argc, char* argv[]);
virtual bool start();
virtual bool stop();
bool init(int argc, char* argv[]);
bool start();
bool stop();
void run ();
};
#else
class DaemonLinux : public Daemon_Singleton
#define Daemon i2p::util::DaemonLinux::Instance()
class DaemonLinux : public Daemon_Singleton
{
public:
static DaemonLinux& Instance()
{
static DaemonLinux instance;
return instance;
}
public:
static DaemonLinux& Instance()
{
static DaemonLinux instance;
return instance;
}
virtual bool start();
virtual bool stop();
private:
std::string pidfile;
int pidFilehandle;
bool start();
bool stop();
void run ();
private:
std::string pidfile;
int pidFH;
public:
int gracefulShutdownInterval; // in seconds
};
#endif
}
}
#endif // DAEMON_H__

View File

@@ -4,47 +4,51 @@
#include <signal.h>
#include <stdlib.h>
#include <thread>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include "Config.h"
#include "FS.h"
#include "Log.h"
#include "util.h"
#include "RouterContext.h"
#include "ClientContext.h"
void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
if (i2p::util::config::GetArg("daemon", 0) == 1)
{
static bool first=true;
if (first)
{
first=false;
return;
}
}
LogPrint("Reloading config.");
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
case SIGHUP:
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening logs and tunnel configuration...");
i2p::log::Logger().Reopen ();
i2p::client::context.ReloadConfig();
break;
case SIGABRT:
case SIGTERM:
case SIGINT:
Daemon.running = 0; // Exit loop
case SIGINT:
if (i2p::context.AcceptsTunnels () && !Daemon.gracefulShutdownInterval)
{
i2p::context.SetAcceptsTunnels (false);
Daemon.gracefulShutdownInterval = 10*60; // 10 minutes
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefulShutdownInterval, " seconds");
}
else
Daemon.running = 0;
break;
case SIGABRT:
case SIGTERM:
Daemon.running = 0; // Exit loop
break;
}
}
namespace i2p
{
namespace util
{
bool DaemonLinux::start()
{
if (isDaemon == 1)
if (isDaemon)
{
pid_t pid;
pid = fork();
@@ -52,44 +56,98 @@ namespace i2p
::exit (EXIT_SUCCESS);
if (pid < 0) // error
{
LogPrint(eLogError, "Daemon: could not fork: ", strerror(errno));
return false;
}
// child
umask(0);
umask(S_IWGRP | S_IRWXO); // 0027
int sid = setsid();
if (sid < 0)
{
LogPrint("Error, could not create process group.");
LogPrint(eLogError, "Daemon: could not create process group.");
return false;
}
std::string d = i2p::fs::GetDataDir();
if (chdir(d.c_str()) != 0)
{
LogPrint(eLogError, "Daemon: could not chdir: ", strerror(errno));
return false;
}
chdir(i2p::util::filesystem::GetDataDir().string().c_str());
// close stdin/stdout/stderr descriptors
::close (0);
::open ("/dev/null", O_RDWR);
::close (1);
::open ("/dev/null", O_RDWR);
::close (2);
::open ("/dev/null", O_RDWR);
#if !defined(__OpenBSD__)
// 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);
#endif
}
// set proc limits
struct rlimit limit;
uint16_t nfiles; i2p::config::GetOption("limits.openfiles", nfiles);
getrlimit(RLIMIT_NOFILE, &limit);
if (nfiles == 0) {
LogPrint(eLogInfo, "Daemon: using system limit in ", limit.rlim_cur, " max open files");
} else if (nfiles <= limit.rlim_max) {
limit.rlim_cur = nfiles;
if (setrlimit(RLIMIT_NOFILE, &limit) == 0) {
LogPrint(eLogInfo, "Daemon: set max number of open files to ",
nfiles, " (system limit is ", limit.rlim_max, ")");
} else {
LogPrint(eLogError, "Daemon: can't set max number of open files: ", strerror(errno));
}
} else {
LogPrint(eLogError, "Daemon: limits.openfiles exceeds system limit: ", limit.rlim_max);
}
uint32_t cfsize; i2p::config::GetOption("limits.coresize", cfsize);
if (cfsize) // core file size set
{
cfsize *= 1024;
getrlimit(RLIMIT_CORE, &limit);
if (cfsize <= limit.rlim_max) {
limit.rlim_cur = cfsize;
if (setrlimit(RLIMIT_CORE, &limit) != 0) {
LogPrint(eLogError, "Daemon: can't set max size of coredump: ", strerror(errno));
} else if (cfsize == 0) {
LogPrint(eLogInfo, "Daemon: coredumps disabled");
} else {
LogPrint(eLogInfo, "Daemon: set max size of core files to ", cfsize / 1024, "Kb");
}
} else {
LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max);
}
}
// Pidfile
pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string();
pidfile.append("/i2pd.pid");
pidFilehandle = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
if (pidFilehandle == -1)
{
LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?");
return false;
// this code is c-styled and a bit ugly, but we need fd for locking pidfile
std::string pidfile; i2p::config::GetOption("pidfile", pidfile);
if (pidfile == "") {
pidfile = i2p::fs::DataDirPath("i2pd.pid");
}
if (lockf(pidFilehandle, F_TLOCK, 0) == -1)
{
LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?");
return false;
if (pidfile != "") {
pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
if (pidFH < 0)
{
LogPrint(eLogError, "Daemon: could not create pid file ", pidfile, ": ", strerror(errno));
return false;
}
if (lockf(pidFH, F_TLOCK, 0) != 0)
{
LogPrint(eLogError, "Daemon: could not lock pid file ", pidfile, ": ", strerror(errno));
return false;
}
char pid[10];
sprintf(pid, "%d\n", getpid());
ftruncate(pidFH, 0);
if (write(pidFH, pid, strlen(pid)) < 0)
{
LogPrint(eLogError, "Daemon: could not write pidfile: ", strerror(errno));
return false;
}
}
char pid[10];
sprintf(pid, "%d\n", getpid());
write(pidFilehandle, pid, strlen(pid));
gracefulShutdownInterval = 0; // not specified
// Signal handler
struct sigaction sa;
@@ -106,12 +164,27 @@ namespace i2p
bool DaemonLinux::stop()
{
close(pidFilehandle);
unlink(pidfile.c_str());
i2p::fs::Remove(pidfile);
return Daemon_Singleton::stop();
}
void DaemonLinux::run ()
{
while (running)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
if (gracefulShutdownInterval)
{
gracefulShutdownInterval--; // - 1 second
if (gracefulShutdownInterval <= 0)
{
LogPrint(eLogInfo, "Graceful shutdown");
return;
}
}
}
}
}
}

View File

@@ -1,10 +1,16 @@
#include <thread>
#include <clocale>
#include "Config.h"
#include "Daemon.h"
#include "util.h"
#include "Log.h"
#ifdef _WIN32
#include "./Win32/Win32Service.h"
#include "Win32/Win32Service.h"
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#endif
namespace i2p
{
@@ -17,15 +23,13 @@ namespace i2p
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
if (!Daemon_Singleton::init(argc, argv)) return false;
if (I2PService::isService())
isDaemon = 1;
else
isDaemon = 0;
if (!Daemon_Singleton::init(argc, argv))
return false;
std::string serviceControl = i2p::util::config::GetArg("-service", "none");
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
@@ -34,50 +38,78 @@ namespace i2p
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
exit(0);
return false;
}
else if (serviceControl == "remove")
{
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
UninstallService(SERVICE_NAME);
exit(0);
return false;
}
else if (serviceControl != "none")
if (isDaemon)
{
printf(" --service=install to install the service.\n");
printf(" --service=remove to remove the service.\n");
}
if (isDaemon == 1)
{
LogPrint("Service session");
LogPrint(eLogDebug, "Daemon: running as service");
I2PService service(SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError());
exit(EXIT_FAILURE);
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
return false;
}
exit(EXIT_SUCCESS);
return false;
}
else
LogPrint("User session");
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;
return Daemon_Singleton::start();
// 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
#endif

View File

@@ -1,7 +1,6 @@
#include <string.h>
#include <vector>
#include <cryptopp/sha.h>
#include <cryptopp/gzip.h>
#include "Crypto.h"
#include "Log.h"
#include "TunnelBase.h"
#include "RouterContext.h"
@@ -12,81 +11,46 @@ namespace i2p
{
namespace datagram
{
DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner):
m_Owner (owner), m_Receiver (nullptr)
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
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];
CryptoPP::SHA256().CalculateDigest (hash, buf1, len);
m_Owner.Sign (hash, 32, signature);
SHA256(buf1, len, hash);
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, ident));
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto session = ObtainSession(ident);
session->SendMsg(msg);
}
void DatagramDestination::HandleLeaseSetRequestComplete (bool success, I2NPMessage * msg, i2p::data::IdentHash ident)
{
if (success)
{
auto remote = m_Owner.FindLeaseSet (ident);
if (remote)
{
SendMsg (msg, remote);
return;
}
}
DeleteI2NPMessage (msg);
}
void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty () && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
auto garlic = m_Owner.WrapMessage (remote, msg, true);
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");
DeleteI2NPMessage (msg);
}
}
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);
@@ -97,7 +61,7 @@ namespace datagram
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, buf + headerLen, len - headerLen);
SHA256(buf + headerLen, len - headerLen, hash);
verified = identity.Verify (hash, 32, signature);
}
else
@@ -105,51 +69,378 @@ namespace datagram
if (verified)
{
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
CryptoPP::Gunzip decompressor;
decompressor.Put (buf, len);
decompressor.MessageEnd();
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
auto uncompressedLen = decompressor.MaxRetrievable ();
if (uncompressedLen <= MAX_DATAGRAM_SIZE)
{
decompressor.Get (uncompressed, uncompressedLen);
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
else
LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size");
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen)
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{
I2NPMessage * msg = NewI2NPMessage ();
CryptoPP::Gzip compressor; // default level
compressor.Put (payload, len);
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
auto msg = NewI2NPMessage ();
uint8_t * buf = msg->GetPayload ();
htobe32buf (buf, size); // length
buf += 4;
compressor.Get (buf, size);
htobe16buf (buf + 4, fromPort); // source port
htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
msg->len += size + 4;
FillI2NPMessageHeader (msg, eI2NPData);
buf += 4; // reserve for length
size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
if (size)
{
htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, fromPort); // source port
htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData);
}
else
msg = nullptr;
return msg;
}
}
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

@@ -4,9 +4,12 @@
#include <inttypes.h>
#include <memory>
#include <functional>
#include <map>
#include "Base.h"
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
#include "Garlic.h"
namespace i2p
{
@@ -16,15 +19,100 @@ 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 (i2p::client::ClientDestination& owner);
~DatagramDestination () {};
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
@@ -32,18 +120,36 @@ namespace datagram
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
private:
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); };
void HandleLeaseSetRequestComplete (bool success, I2NPMessage * msg, i2p::data::IdentHash ident);
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
// clean up stale sessions
void CleanUp ();
private:
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void SendMsg (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);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
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:
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::client::ClientDestination& m_Owner;
Receiver m_Receiver;
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,10 +8,13 @@
#include <set>
#include <string>
#include <functional>
#ifdef I2LUA
#include <future>
#endif
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
#include "CryptoConst.h"
#include "Crypto.h"
#include "LeaseSet.h"
#include "Garlic.h"
#include "NetDb.h"
@@ -26,23 +29,34 @@ namespace client
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18;
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successfull publish
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
const int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
const int DESTINATION_CLEANUP_TIMEOUT = 20; // in minutes
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
// I2CP
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
const int DEFAULT_TAGS_TO_SEND = 40;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class ClientDestination: public i2p::garlic::GarlicDestination
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<LeaseSetDestination>
{
typedef std::function<void (bool success)> RequestComplete;
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found
struct LeaseSetRequest
{
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
@@ -50,70 +64,61 @@ namespace client
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
RequestComplete requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
};
public:
ClientDestination (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->HasNonExpiredLeases (); };
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);
// streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port); // 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
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);
// override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
void ProcessGarlicMessage (I2NPMessage * msg);
void ProcessDeliveryStatusMessage (I2NPMessage * msg);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
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 ();
void UpdateLeaseSet ();
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (I2NPMessage * msg);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, LeaseSetRequest * request);
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 CleanupRemoteLeaseSets ();
private:
@@ -121,28 +126,95 @@ namespace client
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, LeaseSetRequest *> m_LeaseSetRequests;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
i2p::data::LeaseSet * m_LeaseSet;
std::mutex m_LeaseSetMutex;
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_CleanupTimer;
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
public:
// for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
};
class ClientDestination: public LeaseSetDestination
{
public:
#ifdef I2LUA
// type for informing that a client destination is ready
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
// informs promise with shared_from_this() when this destination is ready to use
// if cancelled before ready, informs promise with nullptr
void Ready(ReadyPromise & p);
#endif
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
bool Start ();
bool Stop ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
// 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 ();
#ifdef I2LUA
void ScheduleCheckForReady(ReadyPromise * p);
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
#endif
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;
};
}
}

View File

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

32
Event.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include "Event.h"
#include "Log.h"
namespace i2p
{
namespace event
{
#ifdef WITH_EVENTS
EventCore core;
#endif
void EventCore::SetListener(EventListener * l)
{
m_listener = l;
LogPrint(eLogInfo, "Event: listener set");
}
void EventCore::QueueEvent(const EventType & ev)
{
if(m_listener)
m_listener->HandleEvent(ev);
}
}
}
void EmitEvent(const EventType & e)
{
#ifdef WITH_EVENTS
i2p::event::core.QueueEvent(e);
#endif
}

37
Event.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef EVENT_H__
#define EVENT_H__
#include <map>
#include <string>
#include <memory>
#include <boost/asio.hpp>
typedef std::map<std::string, std::string> EventType;
namespace i2p
{
namespace event
{
class EventListener {
public:
virtual ~EventListener() {};
virtual void HandleEvent(const EventType & ev) = 0;
};
class EventCore
{
public:
void QueueEvent(const EventType & ev);
void SetListener(EventListener * l);
private:
EventListener * m_listener = nullptr;
};
#ifdef WITH_EVENTS
extern EventCore core;
#endif
}
}
void EmitEvent(const EventType & ev);
#endif

192
FS.cpp Normal file
View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <algorithm>
#include <boost/filesystem.hpp>
#ifdef _WIN32
#include <shlobj.h>
#endif
#include "Base.h"
#include "FS.h"
#include "Log.h"
namespace i2p {
namespace fs {
std::string appName = "i2pd";
std::string dataDir = "";
#ifdef _WIN32
std::string dirSep = "\\";
#else
std::string dirSep = "/";
#endif
const std::string & GetAppName () {
return appName;
}
void SetAppName (const std::string& name) {
appName = name;
}
const std::string & GetDataDir () {
return dataDir;
}
void DetectDataDir(const std::string & cmdline_param, bool isService) {
if (cmdline_param != "") {
dataDir = cmdline_param;
return;
}
#if defined(WIN32) || defined(_WIN32)
char localAppData[MAX_PATH];
// check executable directory first
GetModuleFileName (NULL, localAppData, MAX_PATH);
auto execPath = boost::filesystem::path(localAppData).parent_path();
// if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
dataDir = execPath.string ();
else
{
// otherwise %appdata%
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
dataDir = std::string(localAppData) + "\\" + appName;
}
return;
#elif defined(MAC_OSX)
char *home = getenv("HOME");
dataDir = (home != NULL && strlen(home) > 0) ? home : "";
dataDir += "/Library/Application Support/" + appName;
return;
#else /* other unix */
#if defined(ANDROID)
const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext))
{
dataDir = std::string (ext) + "/" + appName;
return;
}
// otherwise use /data/files
#endif
char *home = getenv("HOME");
if (isService) {
dataDir = "/var/lib/" + appName;
} else if (home != NULL && strlen(home) > 0) {
dataDir = std::string(home) + "/." + appName;
} else {
dataDir = "/tmp/" + appName;
}
return;
#endif
}
bool Init() {
if (!boost::filesystem::exists(dataDir))
boost::filesystem::create_directory(dataDir);
std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations))
boost::filesystem::create_directory(destinations);
return true;
}
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path))
return false;
boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
files.push_back(it->path().string());
}
return true;
}
bool Exists(const std::string & path) {
return boost::filesystem::exists(path);
}
bool Remove(const std::string & path) {
if (!boost::filesystem::exists(path))
return false;
return boost::filesystem::remove(path);
}
bool CreateDirectory (const std::string& path)
{
if (boost::filesystem::exists(path) &&
boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
return boost::filesystem::create_directory(path);
}
void HashedStorage::SetPlace(const std::string &path) {
root = path + i2p::fs::dirSep + name;
}
bool HashedStorage::Init(const char * chars, size_t count) {
if (!boost::filesystem::exists(root)) {
boost::filesystem::create_directories(root);
}
for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p))
continue;
if (boost::filesystem::create_directory(p))
continue; /* ^ throws exception on failure */
return false;
}
return true;
}
std::string HashedStorage::Path(const std::string & ident) const {
std::string safe_ident = ident;
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
std::stringstream t("");
t << this->root << i2p::fs::dirSep;
t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
t << prefix2 << safe_ident << "." << suffix;
return t.str();
}
void HashedStorage::Remove(const std::string & ident) {
std::string path = Path(ident);
if (!boost::filesystem::exists(path))
return;
boost::filesystem::remove(path);
}
void HashedStorage::Traverse(std::vector<std::string> & files) {
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;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
v(t);
}
}
} // fs
} // i2p

159
FS.h Normal file
View File

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

181
Family.cpp Normal file
View File

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

38
Family.h Normal file
View File

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

View File

@@ -2,12 +2,14 @@
#include "I2PEndian.h"
#include <map>
#include <string>
#include "Crypto.h"
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Transports.h"
#include "Timestamp.h"
#include "Destination.h"
#include "Log.h"
#include "Garlic.h"
namespace i2p
@@ -15,17 +17,18 @@ namespace i2p
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags):
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (numTags > 0 ? eLeaseSetUpdated : eLeaseSetUpToDate)
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
{
// create new session tags and session key
m_Rnd.GenerateBlock (m_SessionKey, 32);
RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
}
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetUpToDate)
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
@@ -35,18 +38,40 @@ namespace garlic
GarlicRoutingSession::~GarlicRoutingSession ()
{
for (auto it: m_UnconfirmedTagsMsgs)
delete it.second;
m_UnconfirmedTagsMsgs.clear ();
}
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
{
if (!m_SharedRoutingPath) return nullptr;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED ||
!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
ts*1000LL > m_SharedRoutingPath->remoteLease->endDate ||
ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT)
m_SharedRoutingPath = nullptr;
if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++;
return m_SharedRoutingPath;
}
void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path)
{
if (path && path->outboundTunnel && path->remoteLease)
{
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
path->numTimesUsed = 0;
}
else
path = nullptr;
m_SharedRoutingPath = path;
}
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
{
auto tags = new UnconfirmedTags (m_NumTags);
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++)
{
m_Rnd.GenerateBlock (tags->sessionTags[i], 32);
RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
}
return tags;
@@ -58,7 +83,7 @@ namespace garlic
if (msgID == m_LeaseSetUpdateMsgID)
{
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
LogPrint (eLogInfo, "LeaseSet update confirmed");
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
CleanupExpiredTags ();
@@ -66,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;
}
}
@@ -89,27 +123,35 @@ 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_EXPIRATION_TIMEOUT)
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveCreatedSession (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;
}
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg)
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
I2NPMessage * m = NewI2NPMessage ();
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
size_t len = 0;
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
@@ -136,19 +178,19 @@ namespace garlic
// create message
if (!tagFound) // new session
{
LogPrint ("No garlic tags available. Use ElGamal");
LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal");
if (!m_Destination)
{
LogPrint ("Can't use ElGamal for unknown destination");
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
return nullptr;
}
// create ElGamal block
ElGamalBlock elGamal;
memcpy (elGamal.sessionKey, m_SessionKey, 32);
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
RAND_bytes (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
SHA256(elGamal.preIV, 32, iv);
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Encryption.SetIV (iv);
buf += 514;
len += 514;
@@ -158,7 +200,7 @@ namespace garlic
// session tag
memcpy (buf, tag, 32);
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, tag, 32);
SHA256(tag, 32, iv);
m_Encryption.SetIV (iv);
buf += 32;
len += 32;
@@ -167,13 +209,11 @@ namespace garlic
len += CreateAESBlock (buf, msg);
htobe32buf (m->GetPayload (), len);
m->len += len + 4;
FillI2NPMessageHeader (m, eI2NPGarlic);
if (msg)
DeleteI2NPMessage (msg);
m->FillI2NPMessageHeader (eI2NPGarlic);
return m;
}
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, const I2NPMessage * msg)
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
{
size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
@@ -196,7 +236,7 @@ namespace garlic
blockSize++;
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
htobe32buf (payloadSize, len);
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
SHA256(buf + blockSize, len, payloadHash);
blockSize += len;
size_t rem = blockSize % 16;
if (rem)
@@ -205,10 +245,11 @@ namespace garlic
return blockSize;
}
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags)
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint32_t msgID = m_Rnd.GenerateWord32 ();
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
size_t size = 0;
uint8_t * numCloves = payload + size;
*numCloves = 0;
@@ -217,35 +258,40 @@ 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
{
// clove is DeliveryStatus
size += CreateDeliveryStatusClove (payload + size, msgID);
if (size > 0) // successive?
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
if (cloveSize > 0) // successive?
{
size += cloveSize;
(*numCloves)++;
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
LogPrint ("DeliveryStatus clove was not created");
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
}
// attach LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
{
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);
DeleteI2NPMessage (leaseSet);
size += CreateGarlicClove (payload + size, leaseSet, false);
(*numCloves)++;
}
}
@@ -259,14 +305,14 @@ 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;
}
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination)
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
size_t size = 0;
if (isDestination && m_Destination)
{
@@ -283,7 +329,9 @@ namespace garlic
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4;
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
@@ -297,35 +345,35 @@ namespace garlic
size_t size = 0;
if (m_Owner)
{
auto leases = m_Owner->GetLeaseSet ()->GetNonExpiredLeases ();
if (!leases.empty ())
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
if (inboundTunnel)
{
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
size++;
uint32_t i = m_Rnd.GenerateWord32 (0, leases.size () - 1);
// hash and tunnelID sequence is reversed for Garlic
memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash
memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash
size += 32;
htobe32buf (buf + size, leases[i].tunnelID); // tunnelID
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
size += 4;
// create msg
I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
auto msg = CreateDeliveryStatusMsg (msgID);
if (m_Owner)
{
//encrypt
uint8_t key[32], tag[32];
m_Rnd.GenerateBlock (key, 32); // random session key
m_Rnd.GenerateBlock (tag, 32); // random session tag
RAND_bytes (key, 32); // random session key
RAND_bytes (tag, 32); // random session tag
m_Owner->SubmitSessionKey (key, tag);
GarlicRoutingSession garlic (key, tag);
msg = garlic.WrapSingleMessage (msg);
}
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
DeleteI2NPMessage (msg);
// fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4;
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
@@ -333,10 +381,10 @@ namespace garlic
size += 3;
}
else
LogPrint ("All tunnels of local LeaseSet expired");
LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus");
}
else
LogPrint ("Missing local LeaseSet");
LogPrint (eLogWarning, "Garlic: Missing local LeaseSet");
return size;
}
@@ -362,62 +410,49 @@ namespace garlic
return true;
}
void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg)
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
uint8_t * buf = msg->GetPayload ();
uint32_t length = bufbe32toh (buf);
if (length > msg->GetLength ())
{
LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ());
return;
}
buf += 4; // length
auto it = m_Tags.find (SessionTag(buf));
if (it != m_Tags.end ())
{
// tag found. Use AES
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, buf, 32);
it->second->SetIV (iv);
it->second->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
m_Tags.erase (it); // tag might be used only once
if (length >= 32)
{
uint8_t iv[32]; // IV is first 16 bytes
SHA256(buf, 32, iv);
it->second->SetIV (iv);
it->second->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
}
else
LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes");
m_Tags.erase (it); // tag might be used only once
}
else
{
// tag not found. Use ElGamal
ElGamalBlock elGamal;
if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
{
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
decryption->SetKey (elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
SHA256(elGamal.preIV, 32, iv);
decryption->SetIV (iv);
decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
}
else
LogPrint ("Failed to decrypt garlic");
LogPrint (eLogError, "Garlic: Failed to decrypt message");
}
DeleteI2NPMessage (msg);
// cleanup expired tags
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
if (m_LastTagsCleanupTime)
{
int numExpiredTags = 0;
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
{
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
numExpiredTags++;
it = m_Tags.erase (it);
}
else
it++;
}
LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
}
m_LastTagsCleanupTime = ts;
}
}
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
@@ -429,7 +464,7 @@ namespace garlic
{
if (tagCount*32 > len)
{
LogPrint (eLogError, "Tag count ", tagCount, " exceeds length ", len);
LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len);
return ;
}
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
@@ -441,7 +476,7 @@ namespace garlic
uint32_t payloadSize = bufbe32toh (buf);
if (payloadSize > len)
{
LogPrint (eLogError, "Unexpected payload size ", payloadSize);
LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize);
return;
}
buf += 4;
@@ -452,9 +487,11 @@ namespace garlic
buf++; // flag
// payload
if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match
uint8_t digest[32];
SHA256 (buf, payloadSize, digest);
if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match
{
LogPrint ("Wrong payload hash");
LogPrint (eLogError, "Garlic: wrong payload hash");
return;
}
HandleGarlicPayload (buf, payloadSize, from);
@@ -462,8 +499,9 @@ namespace garlic
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
const uint8_t * buf1 = buf;
int numCloves = buf[0];
LogPrint (numCloves," cloves");
LogPrint (eLogDebug, "Garlic: ", numCloves," cloves");
buf++;
for (int i = 0; i < numCloves; i++)
{
@@ -473,139 +511,169 @@ namespace garlic
if (flag & 0x80) // encrypted?
{
// TODO: implement
LogPrint ("Clove encrypted");
LogPrint (eLogWarning, "Garlic: clove encrypted");
buf += 32;
}
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
switch (deliveryType)
{
case eGarlicDeliveryTypeLocal:
LogPrint ("Garlic type local");
LogPrint (eLogDebug, "Garlic: type local");
HandleI2NPMessage (buf, len, from);
break;
case eGarlicDeliveryTypeDestination:
LogPrint ("Garlic type destination");
LogPrint (eLogDebug, "Garlic: type destination");
buf += 32; // destination. check it later or for multiple destinations
HandleI2NPMessage (buf, len, from);
break;
case eGarlicDeliveryTypeTunnel:
{
LogPrint ("Garlic type tunnel");
LogPrint (eLogDebug, "Garlic: type tunnel");
// gwHash and gwTunnel sequence is reverted
uint8_t * gwHash = buf;
buf += 32;
uint32_t gwTunnel = 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
{
I2NPMessage * msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
}
else
LogPrint ("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 ("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 ("Unknow garlic delivery type ", (int)deliveryType);
LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
}
buf += GetI2NPMessageLength (buf); // I2NP
buf += 4; // CloveID
buf += 8; // Date
buf += 3; // Certificate
if (buf - buf1 > (int)len)
{
LogPrint (eLogError, "Garlic: clove is too long");
break;
}
}
}
I2NPMessage * GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
I2NPMessage * msg, bool attachLeaseSet)
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
{
if (attachLeaseSet) // we should maintain this session
{
auto session = GetRoutingSession (destination, 32); // 32 tags by default
return session->WrapSingleMessage (msg);
}
else // one time session
{
GarlicRoutingSession session (this, destination, 0); // don't use tag if no LeaseSet
return session.WrapSingleMessage (msg);
}
auto session = GetRoutingSession (destination, attachLeaseSet);
return session->WrapSingleMessage (msg);
}
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags)
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{
auto it = m_Sessions.find (destination->GetIdentHash ());
std::shared_ptr<GarlicRoutingSession> session;
if (it != m_Sessions.end ())
session = it->second;
GarlicRoutingSessionPtr session;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (destination->GetIdentHash ());
if (it != m_Sessions.end ())
session = it->second;
}
if (!session)
{
session = std::make_shared<GarlicRoutingSession> (this, destination, numTags);
session = std::make_shared<GarlicRoutingSession> (this, destination,
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
}
void GarlicDestination::CleanupRoutingSessions ()
void GarlicDestination::CleanupExpiredTags ()
{
// incoming
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numExpiredTags = 0;
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
{
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
numExpiredTags++;
it = m_Tags.erase (it);
}
else
++it;
}
if (numExpiredTags > 0)
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
// outgoing
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
{
it->second->GetSharedRoutingPath (); // delete shared path if necessary
if (!it->second->CleanupExpiredTags ())
{
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
it = m_Sessions.erase (it);
}
else
it++;
++it;
}
}
void GarlicDestination::RemoveCreatedSession (uint32_t msgID)
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
{
m_CreatedSessions.erase (msgID);
m_DeliveryStatusSessions.erase (msgID);
}
void GarlicDestination::DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID)
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
{
m_CreatedSessions[msgID] = session;
}
m_DeliveryStatusSessions[msgID] = session;
}
void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload ());
{
auto it = m_CreatedSessions.find (msgID);
if (it != m_CreatedSessions.end ())
auto it = m_DeliveryStatusSessions.find (msgID);
if (it != m_DeliveryStatusSessions.end ())
{
it->second->MessageConfirmed (msgID);
m_CreatedSessions.erase (it);
LogPrint (eLogInfo, "Garlic message ", msgID, " acknowledged");
}
m_DeliveryStatusSessions.erase (it);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
}
DeleteI2NPMessage (msg);
}
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 ();
}
void GarlicDestination::ProcessGarlicMessage (I2NPMessage * msg)
void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleGarlicMessage (msg);
}
void GarlicDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg)
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleDeliveryStatusMessage (msg);
}

103
Garlic.h
View File

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

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__ */

431
HTTP.cpp Normal file
View File

@@ -0,0 +1,431 @@
/*
* 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 "util.h"
#include "HTTP.h"
#include <algorithm>
#include <ctime>
namespace i2p {
namespace http {
const std::vector<std::string> HTTP_METHODS = {
"GET", "HEAD", "POST", "PUT", "PATCH",
"DELETE", "OPTIONS", "CONNECT"
};
const std::vector<std::string> HTTP_VERSIONS = {
"HTTP/1.0", "HTTP/1.1"
};
inline bool is_http_version(const std::string & str) {
return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
}
inline bool is_http_method(const std::string & str) {
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
}
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) {
std::size_t count = 0;
std::stringstream ss(line);
std::string token;
while (1) {
count++;
if (limit > 0 && count >= limit)
delim = '\n'; /* reset delimiter */
if (!std::getline(ss, token, delim))
break;
tokens.push_back(token);
}
}
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 ((pos + len) < max && isspace(line.at(pos + len)))
len++;
std::string name = line.substr(0, pos);
std::string value = line.substr(pos + len);
headers[name] = value;
return true;
}
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);
}
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) != '/' || pos_p > 0) {
std::size_t pos_s = 0;
/* schema */
pos_c = url.find("://");
if (pos_c != std::string::npos) {
schema = url.substr(0, pos_c);
pos_p = pos_c + 3;
}
/* user[:pass] */
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);
delim += 1;
pass = url.substr(delim, pos_c - delim);
} else {
user = url.substr(pos_p, pos_c - pos_p);
}
pos_p = pos_c + 1;
}
/* hostname[:port][/path] */
pos_c = url.find_first_of(":/", pos_p);
if (pos_c == std::string::npos) {
/* only hostname, without post and path */
host = url.substr(pos_p, std::string::npos);
return true;
} else if (url.at(pos_c) == ':') {
host = url.substr(pos_p, pos_c - pos_p);
/* port[/path] */
pos_p = pos_c + 1;
pos_c = url.find('/', pos_p);
std::string port_str = (pos_c == std::string::npos)
? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */
for (char c : port_str) {
if (c < '0' || c > '9')
return false;
port *= 10;
port += c - '0';
}
if (pos_c == std::string::npos)
return true; /* no path part */
pos_p = pos_c;
} else {
/* start of path part found */
host = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c;
}
}
/* pos_p now at start of path part */
pos_c = url.find_first_of("?#", pos_p);
if (pos_c == std::string::npos) {
/* only path, without fragment and query */
path = url.substr(pos_p, std::string::npos);
return true;
} else if (url.at(pos_c) == '?') {
/* found query part */
path = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c + 1;
pos_c = url.find('#', pos_p);
if (pos_c == std::string::npos) {
/* no fragment */
query = url.substr(pos_p, std::string::npos);
return true;
} else {
query = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c + 1;
}
} else {
/* found fragment part */
path = url.substr(pos_p, pos_c - pos_p);
pos_p = pos_c + 1;
}
/* pos_p now at start of fragment part */
frag = url.substr(pos_p, std::string::npos);
return true;
}
bool URL::parse_query(std::map<std::string, std::string> & params) {
std::vector<std::string> tokens;
strsplit(query, tokens, '&');
params.clear();
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));
params.insert(e);
} else {
auto e = std::pair<std::string, std::string>(it, "");
params.insert(e);
}
}
return true;
}
std::string URL::to_string() {
std::string out = "";
if (schema != "") {
out = schema + "://";
if (user != "" && pass != "") {
out += user + ":" + pass + "@";
} else if (user != "") {
out += user + "@";
}
if (port) {
out += host + ":" + std::to_string(port);
} else {
out += host;
}
}
out += path;
if (query != "")
out += "?" + query;
if (frag != "")
out += "#" + frag;
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);
}
int HTTPReq::parse(const std::string& str) {
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
URL url;
if (eoh == std::string::npos)
return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == REQ_LINE) {
std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
strsplit(line, tokens, ' ');
if (tokens.size() != 3)
return -1;
if (!is_http_method(tokens[0]))
return -1;
if (!is_http_version(tokens[2]))
return -1;
if (!url.parse(tokens[1]))
return -1;
/* all ok */
method = tokens[0];
uri = tokens[1];
version = tokens[2];
expect = HEADER_LINE;
} else {
std::string line = str.substr(pos, eol - pos);
if (!parse_header_line(line, headers))
return -1;
}
pos = eol + strlen(CRLF);
if (pos >= eoh)
break;
}
return eoh + strlen(HTTP_EOH);
}
std::string HTTPReq::to_string() {
std::stringstream ss;
ss << method << " " << uri << " " << version << CRLF;
for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF;
}
ss << CRLF;
return ss.str();
}
bool HTTPRes::is_chunked() {
auto it = headers.find("Transfer-Encoding");
if (it == headers.end())
return false;
if (it->second.find("chunked") == std::string::npos)
return true;
return false;
}
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())
return -1;
errno = 0;
length = std::strtoul(it->second.c_str(), (char **) NULL, 10);
if (errno != 0)
return -1;
return length;
}
int HTTPRes::parse(const char *buf, size_t len) {
std::string str(buf, len);
return parse(str);
}
int HTTPRes::parse(const std::string& str) {
enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
if (eoh == std::string::npos)
return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == RES_LINE) {
std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
strsplit(line, tokens, ' ', 3);
if (tokens.size() != 3)
return -1;
if (!is_http_version(tokens[0]))
return -1;
code = atoi(tokens[1].c_str());
if (code < 100 || code >= 600)
return -1;
/* all ok */
version = tokens[0];
status = tokens[2];
expect = HEADER_LINE;
} else {
std::string line = str.substr(pos, eol - pos);
if (!parse_header_line(line, headers))
return -1;
}
pos = eol + strlen(CRLF);
if (pos >= eoh)
break;
}
return eoh + strlen(HTTP_EOH);
}
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();
}
const char * HTTPCodeToStatus(int code) {
const char *ptr;
switch (code) {
case 105: ptr = "Name Not Resolved"; break;
/* success */
case 200: ptr = "OK"; break;
case 206: ptr = "Partial Content"; break;
/* redirect */
case 301: ptr = "Moved Permanently"; break;
case 302: ptr = "Found"; break;
case 304: ptr = "Not Modified"; break;
case 307: ptr = "Temporary Redirect"; break;
/* client error */
case 400: ptr = "Bad Request"; break;
case 401: ptr = "Unauthorized"; break;
case 403: ptr = "Forbidden"; break;
case 404: ptr = "Not Found"; break;
case 407: ptr = "Proxy Authentication Required"; break;
case 408: ptr = "Request Timeout"; break;
/* server error */
case 500: ptr = "Internal Server Error"; break;
case 502: ptr = "Bad Gateway"; break;
case 503: ptr = "Not Implemented"; break;
case 504: ptr = "Gateway Timeout"; break;
default: ptr = "Unknown Status"; break;
}
return ptr;
}
std::string UrlDecode(const std::string& data, bool allow_null) {
std::string decoded(data);
size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos) {
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
if (c == '\0' && !allow_null) {
pos += 3;
continue;
}
decoded.replace(pos, 3, 1, c);
pos++;
}
return decoded;
}
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
std::string hexLen;
while (!in.eof ()) {
std::getline (in, hexLen);
errno = 0;
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
if (errno != 0)
return false; /* conversion error */
if (len == 0)
return true; /* end of stream */
if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */
return false; /* too large chunk */
char * buf = new char[len];
in.read (buf, len);
out.write (buf, len);
delete[] buf;
std::getline (in, hexLen); // read \r\n after chunk
}
return true;
}
} // http
} // i2p

151
HTTP.h Normal file
View File

@@ -0,0 +1,151 @@
/*
* 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 HTTP_H__
#define HTTP_H__
#include <cstring>
#include <map>
#include <sstream>
#include <string>
#include <vector>
namespace i2p {
namespace http {
const char CRLF[] = "\r\n"; /**< HTTP line terminator */
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */
extern const std::vector<std::string> HTTP_METHODS; /**< list of valid HTTP methods */
extern const std::vector<std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
struct URL {
std::string schema;
std::string user;
std::string pass;
std::string host;
unsigned short int port;
std::string path;
std::string query;
std::string frag;
URL(): schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {};
/**
* @brief Tries to parse url from string
* @return true on success, false on invalid url
*/
bool parse (const char *str, std::size_t len = 0);
bool parse (const std::string& url);
/**
* @brief Parse query part of url to key/value map
* @note Honestly, this should be implemented with std::multimap
*/
bool parse_query(std::map<std::string, std::string> & params);
/**
* @brief Serialize URL structure to url
* @note Returns relative url if schema if empty, absolute url otherwise
*/
std::string to_string ();
};
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;
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {};
/**
* @brief Tries to parse HTTP request from string
* @return -1 on error, 0 on incomplete query, >0 on success
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string& buf);
/** @brief Serialize HTTP request to string */
std::string to_string();
};
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) {}
/**
* @brief Tries to parse HTTP response from string
* @return -1 on error, 0 on incomplete query, >0 on success
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string& buf);
/**
* @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 Checks that response contains compressed data */
bool is_gzipped();
};
/**
* @brief returns HTTP status string by integer code
* @param code HTTP code [100, 599]
* @return Immutable string with status
*/
const char * HTTPCodeToStatus(int code);
/**
* @brief Replaces %-encoded characters in string with their values
* @param data Source string
* @param null If set to true - decode also %00 sequence, otherwise - skip
* @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
#endif /* HTTP_H__ */

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"
@@ -12,288 +17,320 @@
#include "ClientContext.h"
#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 ExtractRequest();
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];
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
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;
public:
HTTPProxyHandler(HTTPProxyServer * parent, 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,"--- HTTP Proxy async sock read");
if(m_sock) {
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError,"--- HTTP Proxy no socket for read");
LogPrint(eLogDebug, "HTTPProxy: async sock read");
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) {
LogPrint(eLogDebug,"--- HTTP Proxy close sock");
if (m_sock)
{
LogPrint(eLogDebug, "HTTPProxy: close sock");
m_sock->close();
delete m_sock;
m_sock = nullptr;
}
Done(shared_from_this());
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
{
std::string 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::EnterState(HTTPProxyHandler::state nstate)
{
m_state = nstate;
}
void HTTPProxyHandler::ExtractRequest()
{
LogPrint(eLogDebug,"--- HTTP Proxy method is: ", m_method, "\nRequest is: ", 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,"--- HTTP Proxy server is: ",server, " port is: ", port, "\n path is: ",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,"--- HTTP Proxy 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;
}
else
{
if (addressHelperPos2 == std::string::npos)
addressHelperPos = addressHelperPos1;
else if ( addressHelperPos1 > addressHelperPos2 )
addressHelperPos = addressHelperPos1;
else
addressHelperPos = addressHelperPos2;
}
auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1));
base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded
LogPrint (eLogDebug,"Jump service for ", m_address, " found at ", base64, ". Inserting to address book");
//TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/
//TODO: we could redirect the user again to avoid dirtiness in the browser
i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64);
m_path.erase(addressHelperPos);
}
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
{
ExtractRequest(); //TODO: parse earlier
if (!ValidateHTTPRequest()) return false;
HandleJumpServices();
m_request = m_method;
m_request.push_back(' ');
m_request += m_path;
m_request.push_back(' ');
m_request += m_version;
m_request.push_back('\r');
m_request.push_back('\n');
m_request.append("Connection: close\r\n");
m_request.append(reinterpret_cast<const char *>(http_buff),len);
return true;
}
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
{
assert(len); // This should always be called with a least a byte left to parse
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,"--- HTTP Proxy rejected invalid request ending with: ", ((int)*http_buff));
HTTPRequestFailed(); //TODO: add correct code
return false;
}
break;
default:
LogPrint(eLogError,"--- HTTP Proxy invalid state: ", m_state);
HTTPRequestFailed(); //TODO: add correct code 500
return false;
/* 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 */
}
http_buff++;
len--;
if (m_state == DONE)
return CreateHTTPRequest(http_buff,len);
}
for (const auto& header : toErase) {
req.headers.erase(header);
}
/* 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 */
}
/**
* @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()
{
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;
}
/* check dest_host really exists and inside I2P network */
i2p::data::IdentHash identHash;
if (str_rmatch(dest_host, ".i2p")) {
if (!i2p::client::context.GetAddressBook ().GetIdentHash (dest_host, identHash)) {
HostNotFound(dest_host);
return true; /* request processed */
}
/* 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;
}
/* 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;
}
void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, 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)
{
LogPrint(eLogDebug,"--- HTTP Proxy sock recv: ", len);
LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length());
if(ecode)
{
LogPrint(eLogWarning," --- HTTP Proxy sock recv got error: ", ecode);
Terminate();
LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
Terminate();
return;
}
if (HandleData(m_http_buff, len))
{
if (m_state == DONE)
{
LogPrint(eLogInfo,"--- HTTP Proxy 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)
Terminate();
else
{
LogPrint (eLogError,"--- HTTP Proxy Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
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,"--- HTTP Proxy New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
Done(shared_from_this());
}
else
{
LogPrint (eLogError,"--- HTTP Proxy Issue when creating the stream, check the previous warnings for more info.");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
if (!stream) {
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
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(int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor(port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
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(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(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(boost::asio::ip::tcp::socket * socket);
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

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,48 @@
#ifndef HTTP_SERVER_H__
#define HTTP_SERVER_H__
#include <sstream>
#include <thread>
#include <memory>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include "LeaseSet.h"
#include "Streaming.h"
namespace i2p
{
namespace util
{
namespace i2p {
namespace http {
extern const char *itoopieFavicon;
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
class HTTPConnection
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{
protected:
struct header
{
std::string name;
std::string value;
};
struct request
{
std::string method;
std::string uri;
std::string host;
int port;
int http_version_major;
int http_version_minor;
std::vector<header> headers;
};
struct reply
{
std::vector<header> headers;
std::string content;
std::vector<boost::asio::const_buffer> to_buffers (int status);
};
public:
HTTPConnection (boost::asio::ip::tcp::socket * socket):
m_Socket (socket), m_Timer (socket->get_io_service ()),
m_Stream (nullptr), m_BufferLen (0) { Receive (); };
virtual ~HTTPConnection() { delete m_Socket; }
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void Receive ();
private:
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void Terminate (const boost::system::error_code& ecode);
void RunRequest ();
bool CheckAuth (const HTTPReq & req);
void HandleRequest (const HTTPReq & req);
void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
void SendReply (HTTPRes & res, std::string & content);
private:
void Terminate ();
void Receive ();
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void AsyncStreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleWriteReply(const boost::system::error_code& ecode);
void HandleWrite (const boost::system::error_code& ecode);
void SendReply (const std::string& content, int status = 200);
void HandleRequest (const std::string& address);
void HandleCommand (const std::string& command, std::stringstream& s);
void ShowTransports (std::stringstream& s);
void ShowTunnels (std::stringstream& s);
void ShowTransitTunnels (std::stringstream& s);
void ShowLocalDestinations (std::stringstream& s);
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
void ShowSAMSessions (std::stringstream& s);
void ShowSAMSession (const std::string& id, std::stringstream& s);
void StartAcceptingTunnels (std::stringstream& s);
void StopAcceptingTunnels (std::stringstream& s);
void FillContent (std::stringstream& s);
std::string ExtractAddress ();
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
protected:
boost::asio::ip::tcp::socket * m_Socket;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
boost::asio::deadline_timer m_Timer;
std::shared_ptr<i2p::stream::Stream> m_Stream;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen;
request m_Request;
reply m_Reply;
protected:
virtual void RunRequest ();
void HandleDestinationRequest(const std::string& address, const std::string& uri);
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
void SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len);
public:
static const std::string itoopieImage;
static const std::string itoopieFavicon;
std::string m_SendBuffer;
bool needAuth;
std::string user;
std::string pass;
};
class HTTPServer
{
public:
HTTPServer (int port);
virtual ~HTTPServer ();
HTTPServer (const std::string& address, int port);
~HTTPServer ();
void Start ();
void Stop ();
@@ -117,22 +51,19 @@ namespace util
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode);
void HandleAccept(const boost::system::error_code& ecode,
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
private:
std::thread * m_Thread;
bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ip::tcp::socket * m_NewSocket;
protected:
virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket);
};
}
}
#endif
} // http
} // i2p
#endif /* HTTP_SERVER_H__ */

733
I2CP.cpp Normal file
View File

@@ -0,0 +1,733 @@
/*
* 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
{
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 ();
}
void I2CPSession::Stop ()
{
Terminate ();
}
void I2CPSession::ReadProtocolByte ()
{
if (m_Socket)
{
auto s = shared_from_this ();
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_Header[0] == I2CP_PROTOCOL_BYTE)
s->ReceiveHeader ();
else
s->Terminate ();
});
}
}
void I2CPSession::ReceiveHeader ()
{
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::HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
Terminate ();
else
{
m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET);
if (m_PayloadLen > 0)
{
m_Payload = new uint8_t[m_PayloadLen];
ReceivePayload ();
}
else // no following payload
{
HandleMessage ();
ReceiveHeader (); // next message
}
}
}
void I2CPSession::ReceivePayload ()
{
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
{
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;
}
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_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);
}
}
}

204
I2CP.h Normal file
View File

@@ -0,0 +1,204 @@
/*
* 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_PROTOCOL_BYTE = 0x2A;
const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
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:
#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 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<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);
class I2CPServer
{
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];
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:
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
};
}
}
#endif

View File

@@ -1,88 +1,97 @@
#include <string.h>
#include <atomic>
#include "Base.h"
#include "Log.h"
#include "Crypto.h"
#include "I2PEndian.h"
#include <cryptopp/gzip.h>
#include "ElGamal.h"
#include "Timestamp.h"
#include "RouterContext.h"
#include "NetDb.h"
#include "Tunnel.h"
#include "base64.h"
#include "Transports.h"
#include "Garlic.h"
#include "I2NPProtocol.h"
#include "version.h"
using namespace i2p::transport;
namespace i2p
{
I2NPMessage * NewI2NPMessage ()
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
{
return new I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE>();
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> >();
}
I2NPMessage * NewI2NPShortMessage ()
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ()
{
return new I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE>();
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
}
I2NPMessage * NewI2NPMessage (size_t len)
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
{
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
}
void DeleteI2NPMessage (I2NPMessage * msg)
{
delete msg;
}
static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID)
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID)
{
msg->SetTypeID (msgType);
if (replyMsgID) // for tunnel creation
msg->SetMsgID (replyMsgID);
else
{
msg->SetMsgID (I2NPmsgID);
I2NPmsgID++;
}
msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
msg->UpdateSize ();
msg->UpdateChks ();
SetTypeID (msgType);
if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4);
SetMsgID (replyMsgID);
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
UpdateSize ();
UpdateChks ();
}
void RenewI2NPMessageHeader (I2NPMessage * msg)
void I2NPMessage::RenewI2NPMessageHeader ()
{
if (msg)
{
msg->SetMsgID (I2NPmsgID);
I2NPmsgID++;
msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000);
}
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
SetMsgID (msgID);
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
}
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID)
bool I2NPMessage::IsExpired () const
{
I2NPMessage * msg = NewI2NPMessage (len);
memcpy (msg->GetPayload (), buf, len);
msg->len += len;
FillI2NPMessageHeader (msg, msgType, replyMsgID);
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
auto exp = GetExpiration ();
return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
}
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
auto msg = NewI2NPMessage (len);
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length ", msg->maxLen);
msg->FillI2NPMessageHeader (msgType, replyMsgID);
return msg;
}
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
I2NPMessage * msg = NewI2NPMessage ();
memcpy (msg->GetBuffer (), buf, len);
msg->len = msg->offset + len;
msg->from = from;
auto msg = NewI2NPMessage ();
if (msg->offset + len < msg->maxLen)
{
memcpy (msg->GetBuffer (), buf, len);
msg->len = msg->offset + len;
msg->from = from;
}
else
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length");
return msg;
}
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
I2NPMessage * m = NewI2NPShortMessage ();
if (!msg) return nullptr;
auto newMsg = NewI2NPMessage (msg->len);
newMsg->offset = msg->offset;
*newMsg = *msg;
return newMsg;
}
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
{
auto m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
if (msgID)
{
@@ -91,18 +100,19 @@ namespace i2p
}
else // for SSU establishment
{
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ());
}
m->len += DELIVERY_STATUS_SIZE;
FillI2NPMessageHeader (m, eI2NPDeliveryStatus);
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
return m;
}
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{
I2NPMessage * m = NewI2NPShortMessage ();
auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, key, 32); // key
buf += 32;
@@ -140,26 +150,27 @@ namespace i2p
}
m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
return m;
}
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
{
I2NPMessage * m = NewI2NPShortMessage ();
int cnt = excludedFloodfills.size ();
auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, dest, 32); // key
buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 5;
*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
int cnt = excludedFloodfills.size ();
htobe16buf (buf, cnt);
buf += 2;
if (cnt > 0)
@@ -172,28 +183,26 @@ 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;
m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
return m;
}
}
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
std::vector<i2p::data::IdentHash> routers)
{
I2NPMessage * m = NewI2NPShortMessage ();
auto m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
size_t len = 0;
memcpy (buf, ident, 32);
len += 32;
buf[len] = routers.size ();
len++;
for (auto it: routers)
for (const auto& it: routers)
{
memcpy (buf + len, it, 32);
len += 32;
@@ -201,16 +210,16 @@ namespace i2p
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
len += 32;
m->len += len;
FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply);
return m;
}
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router, uint32_t replyToken)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
{
if (!router) // we send own RouterInfo
router = &context.GetRouterInfo ();
router = context.GetSharedRouterInfo ();
I2NPMessage * m = NewI2NPShortMessage ();
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
@@ -225,38 +234,55 @@ namespace i2p
buf += 32;
}
CryptoPP::Gzip compressor;
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
compressor.MessageEnd();
auto size = compressor.MaxRetrievable ();
htobe16buf (buf, size); // size
uint8_t * sizePtr = buf;
buf += 2;
// TODO: check if size doesn't exceed buffer
compressor.Get (buf, size);
buf += size;
m->len += (buf - payload); // payload size
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
i2p::data::GzipDeflator deflator;
size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
if (size)
{
htobe16buf (sizePtr, size); // size
m->len += size;
}
else
m = nullptr;
if (m)
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
if (!leaseSet) return nullptr;
I2NPMessage * m = NewI2NPShortMessage ();
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 ();
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, 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
@@ -265,10 +291,26 @@ namespace i2p
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen ();
m->len += size;
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg)
{
if (!msg || msg->GetTypeID () != eI2NPDatabaseStore) return false;
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
}
static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO:
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels)
{
if (maxNumTransitTunnels > 0 && maxNumTransitTunnels <= 10000 && g_MaxNumTransitTunnels != maxNumTransitTunnels)
{
LogPrint (eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels);
g_MaxNumTransitTunnels = maxNumTransitTunnels;
}
}
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
{
for (int i = 0; i < num; i++)
@@ -276,16 +318,15 @@ namespace i2p
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint ("Record ",i," is ours");
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
// replace record to reply
if (i2p::context.AcceptsTunnels () &&
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS &&
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
!i2p::transport::transports.IsBandwidthExceeded ())
{
i2p::tunnel::TransitTunnel * transitTunnel =
i2p::tunnel::CreateTransitTunnel (
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
@@ -300,8 +341,8 @@ namespace i2p
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
//TODO: fill filler
CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET,
record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
// encrypt reply
i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++)
@@ -320,22 +361,27 @@ namespace i2p
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
int num = buf[0];
LogPrint ("VariableTunnelBuild ", num, " records");
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
{
LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
if (tunnel)
{
// endpoint of inbound tunnel
LogPrint ("VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
}
else
{
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
@@ -362,6 +408,11 @@ namespace i2p
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
if (len < NUM_TUNNEL_BUILD_RECORDS*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE)
{
LogPrint (eLogError, "TunnelBuild message is too short ", len);
return;
}
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
{
@@ -382,60 +433,74 @@ namespace i2p
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
{
LogPrint (eLogError, "VaribleTunnelBuildReply message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
if (tunnel)
{
// reply for outbound tunnel
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
}
else
{
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
LogPrint ("Pending tunnel for message ", replyMsgID, " not found");
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
}
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf)
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{
I2NPMessage * msg = NewI2NPShortMessage ();
memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
FillI2NPMessageHeader (msg, eI2NPTunnelData);
auto msg = NewI2NPShortMessage ();
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
}
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
{
I2NPMessage * msg = NewI2NPShortMessage ();
memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
auto msg = NewI2NPShortMessage ();
htobe32buf (msg->GetPayload (), tunnelID);
msg->len += 4; // tunnelID
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
}
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
{
auto msg = NewI2NPShortMessage ();
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
FillI2NPMessageHeader (msg, eI2NPTunnelData);
return msg;
}
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
{
I2NPMessage * msg = NewI2NPMessage (len);
auto msg = NewI2NPMessage (len);
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len);
msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
msg->len += TUNNEL_GATEWAY_HEADER_SIZE;
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
return msg;
}
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg)
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
{
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
{
@@ -446,33 +511,29 @@ namespace i2p
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
return msg;
}
else
{
I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
DeleteI2NPMessage (msg);
return msg1;
}
return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
}
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
I2NPMessage * msg = NewI2NPMessage (len);
auto msg = NewI2NPMessage (len);
size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->offset += gatewayMsgOffset;
msg->len += gatewayMsgOffset;
memcpy (msg->GetPayload (), buf, len);
msg->len += len;
FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message
len = msg->GetLength ();
msg->offset -= gatewayMsgOffset;
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message
msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message
return msg;
}
@@ -485,62 +546,55 @@ namespace i2p
{
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
uint8_t * buf = msg + I2NP_HEADER_SIZE;
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
switch (typeID)
{
case eI2NPVariableTunnelBuild:
LogPrint ("VariableTunnelBuild");
HandleVariableTunnelBuildMsg (msgID, buf, size);
break;
case eI2NPVariableTunnelBuildReply:
LogPrint ("VariableTunnelBuildReply");
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break;
case eI2NPTunnelBuild:
LogPrint ("TunnelBuild");
HandleTunnelBuildMsg (buf, size);
break;
case eI2NPTunnelBuildReply:
LogPrint ("TunnelBuildReply");
// TODO:
break;
default:
LogPrint ("Unexpected message ", (int)typeID);
LogPrint (eLogWarning, "I2NP: Unexpected message ", (int)typeID);
}
}
void HandleI2NPMessage (I2NPMessage * msg)
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{
switch (msg->GetTypeID ())
uint8_t typeID = msg->GetTypeID ();
LogPrint (eLogDebug, "I2NP: Handling message with type ", (int)typeID);
switch (typeID)
{
case eI2NPTunnelData:
LogPrint ("TunnelData");
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
case eI2NPTunnelGateway:
LogPrint ("TunnelGateway");
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
case eI2NPGarlic:
LogPrint ("Garlic");
{
if (msg->from)
{
if (msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
else
{
LogPrint (eLogInfo, "Local destination for garlic doesn't exist anymore");
DeleteI2NPMessage (msg);
}
LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore");
}
else
i2p::context.ProcessGarlicMessage (msg);
break;
break;
}
case eI2NPDatabaseStore:
case eI2NPDatabaseSearchReply:
case eI2NPDatabaseLookup:
@@ -548,12 +602,13 @@ namespace i2p
i2p::data::netdb.PostI2NPMsg (msg);
break;
case eI2NPDeliveryStatus:
LogPrint ("DeliveryStatus");
{
if (msg->from && msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg);
else
i2p::context.ProcessDeliveryStatusMessage (msg);
break;
break;
}
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
@@ -563,7 +618,6 @@ namespace i2p
break;
default:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
DeleteI2NPMessage (msg);
}
}
}
@@ -573,7 +627,7 @@ namespace i2p
Flush ();
}
void I2NPMessagesHandler::PutNextMessage (I2NPMessage * msg)
void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{

View File

@@ -5,7 +5,7 @@
#include <string.h>
#include <set>
#include <memory>
#include <cryptopp/sha.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
@@ -90,15 +90,13 @@ 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
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
const int MAX_NUM_TRANSIT_TUNNELS = 2500;
namespace tunnel
{
class InboundTunnel;
@@ -107,6 +105,9 @@ namespace tunnel
const size_t I2NP_MAX_MESSAGE_SIZE = 32768;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60*1000; // 1 minute in milliseconds
struct I2NPMessage
{
uint8_t * buf;
@@ -132,12 +133,13 @@ namespace tunnel
void UpdateChks ()
{
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ());
SHA256(GetPayload (), GetPayloadLength (), hash);
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
}
// payload
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; };
const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; };
uint8_t * GetBuffer () { return buf + offset; };
const uint8_t * GetBuffer () const { return buf + offset; };
size_t GetLength () const { return len - offset; };
@@ -154,6 +156,15 @@ namespace tunnel
}
}
size_t Concat (const uint8_t * buf1, size_t len1)
{
// make sure with don't write beyond maxLen
if (len + len1 > maxLen) len1 = maxLen - len;
memcpy (buf + len, buf1, len1);
len += len1;
return len1;
}
I2NPMessage& operator=(const I2NPMessage& other)
{
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
@@ -161,7 +172,7 @@ namespace tunnel
from = other.from;
return *this;
}
// for SSU only
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
@@ -183,64 +194,73 @@ namespace tunnel
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
}
void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0);
void RenewI2NPMessageHeader ();
bool IsExpired () const;
};
template<int sz>
struct I2NPMessageBuffer: public I2NPMessage
{
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
uint8_t m_Buffer[sz + 16];
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
};
I2NPMessage * NewI2NPMessage ();
I2NPMessage * NewI2NPShortMessage ();
I2NPMessage * NewI2NPMessage (size_t len);
void DeleteI2NPMessage (I2NPMessage * msg);
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID = 0);
void RenewI2NPMessageHeader (I2NPMessage * msg);
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> NewI2NPMessage ();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr, uint32_t replyToken = 0);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0);
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); // 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);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf);
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ();
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
size_t GetI2NPMessageLength (const uint8_t * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (I2NPMessage * msg);
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler
{
public:
~I2NPMessagesHandler ();
void PutNextMessage (I2NPMessage * msg);
void PutNextMessage (std::shared_ptr<I2NPMessage> msg);
void Flush ();
private:
std::vector<I2NPMessage *> m_TunnelMsgs, m_TunnelGatewayMsgs;
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
};
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500;
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
}
#endif

View File

@@ -1,15 +1,21 @@
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 7))
#include "I2PControl.h"
#include <stdio.h>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp>
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp>
#endif
#include "Crypto.h"
#include "FS.h"
#include "Log.h"
#include "HTTP.h"
#include "Config.h"
#include "NetDb.h"
#include "RouterContext.h"
#include "Daemon.h"
@@ -17,38 +23,71 @@
#include "Timestamp.h"
#include "Transports.h"
#include "version.h"
#include "util.h"
#include "I2PControl.h"
namespace i2p
{
namespace client
{
I2PControlService::I2PControlService (int port):
m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
I2PControlService::I2PControlService (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_SSLContext (m_Service, boost::asio::ssl::context::sslv23),
m_ShutdownTimer (m_Service)
{
m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler;
i2p::config::GetOption("i2pcontrol.password", m_Password);
// certificate / keys
std::string i2pcp_crt; i2p::config::GetOption("i2pcontrol.cert", i2pcp_crt);
std::string i2pcp_key; i2p::config::GetOption("i2pcontrol.key", i2pcp_key);
if (i2pcp_crt.at(0) != '/')
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
if (i2pcp_key.at(0) != '/')
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) {
LogPrint (eLogInfo, "I2PControl: creating new certificate for control connection");
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
} else {
LogPrint(eLogDebug, "I2PControl: using cert from ", i2pcp_crt);
}
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem);
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem);
// handlers
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
// I2PControl
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
// RouterInfo
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_UPTIME] = &I2PControlService::UptimeHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_VERSION] = &I2PControlService::VersionHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_STATUS] = &I2PControlService::StatusHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NET_STATUS] = &I2PControlService::NetStatusHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_IB_1S] = &I2PControlService::InboundBandwidth1S ;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_OB_1S] = &I2PControlService::OutboundBandwidth1S ;
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
// RouterManager
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler;
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler;
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler;
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler;
// NetworkSetting
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlService::InboundBandwidthLimit;
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlService::OutboundBandwidthLimit;
}
I2PControlService::~I2PControlService ()
@@ -71,199 +110,222 @@ namespace client
if (m_IsRunning)
{
m_IsRunning = false;
m_Acceptor.cancel ();
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
}
void I2PControlService::Run ()
{
void I2PControlService::Run ()
{
while (m_IsRunning)
{
try
{
try {
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl: ", ex.what ());
} catch (std::exception& ex) {
LogPrint (eLogError, "I2PControl: runtime exception: ", ex.what ());
}
}
}
void I2PControlService::Accept ()
{
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
m_Acceptor.async_accept (*newSocket, std::bind (&I2PControlService::HandleAccept, this,
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
{
if (ecode != boost::asio::error::operation_aborted)
Accept ();
if (!ecode)
{
LogPrint (eLogInfo, "New I2PControl request from ", socket->remote_endpoint ());
std::this_thread::sleep_for (std::chrono::milliseconds(5));
ReadRequest (socket);
if (ecode) {
LogPrint (eLogError, "I2PControl: accept error: ", ecode.message ());
return;
}
else
LogPrint (eLogError, "I2PControl accept error: ", ecode.message ());
LogPrint (eLogDebug, "I2PControl: new request from ", socket->lowest_layer ().remote_endpoint ());
Handshake (socket);
}
void I2PControlService::ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket)
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
{
socket->async_handshake(boost::asio::ssl::stream_base::server,
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
}
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
{
if (ecode) {
LogPrint (eLogError, "I2PControl: handshake error: ", ecode.message ());
return;
}
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
ReadRequest (socket);
}
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
{
auto request = std::make_shared<I2PControlBuffer>();
socket->async_read_some (
#if BOOST_VERSION >= 104900
boost::asio::buffer (*request),
#if defined(BOOST_ASIO_HAS_STD_ARRAY)
boost::asio::buffer (*request),
#else
boost::asio::buffer (request->data (), request->size ()),
#endif
std::bind(&I2PControlService::HandleRequestReceived, this,
boost::asio::buffer (request->data (), request->size ()),
#endif
std::bind(&I2PControlService::HandleRequestReceived, this,
std::placeholders::_1, std::placeholders::_2, socket, request));
}
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
size_t bytes_transferred, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode)
{
LogPrint (eLogError, "I2PControl read error: ", ecode.message ());
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;
while (!ss.eof () && header != "\r")
std::getline(ss, header);
if (ss.eof ())
{
LogPrint (eLogError, "Malformed I2PControl request. HTTP header expected");
return; // TODO:
}
}
/* 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, "json_read is not supported due bug in boost 1.49 with gcc 4.7");
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
boost::property_tree::ptree pt;
boost::property_tree::read_json (ss, pt);
/* now try to parse json itself */
try {
boost::property_tree::ptree pt;
boost::property_tree::read_json (json, pt);
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ())
{
std::ostringstream response;
response << "{\"id\":" << pt.get<std::string>(I2P_CONTROL_PROPERTY_ID) << ",\"result\":{";
(this->*(it->second))(pt.get_child (I2P_CONTROL_PROPERTY_PARAMS), response);
response << "},\"jsonrpc\":\"2.0\"}";
SendResponse (socket, buf, response, isHtml);
}
else
LogPrint (eLogWarning, "Unknown I2PControl method ", method);
#endif
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl handle request: ", ex.what ());
}
catch (...)
{
LogPrint (eLogError, "I2PControl handle request unknown exception");
std::string id = pt.get<std::string>("id");
std::string method = pt.get<std::string>("method");
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ()) {
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
{
ss << "\"" << name << "\":" << value;
}
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const
{
ss << "\"" << name << "\":";
ss << "\"" << name << "\":";
if (value.length () > 0)
ss << "\"" << value << "\"";
else
ss << "null";
}
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
{
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
}
void I2PControlService::SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
std::stringstream ss;
ss << "{\"id\":null,\"error\":";
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
ss << "\"jsonrpc\":\"2.0\"}";
content = ss.str();
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
{
size_t len = response.str ().length (), offset = 0;
if (isHtml)
{
std::ostringstream header;
header << "HTTP/1.1 200 OK\r\n";
header << "Connection: close\r\n";
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
header << "Content-Type: application/json\r\n";
header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
header.imbue(std::locale (header.getloc(), facet));
header << boost::posix_time::second_clock::local_time() << "\r\n";
header << "\r\n";
offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset);
}
memcpy (buf->data () + offset, response.str ().c_str (), len);
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
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;
}
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::bind(&I2PControlService::HandleResponseSent, this,
std::placeholders::_1, std::placeholders::_2, socket, buf));
}
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf)
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode)
LogPrint (eLogError, "I2PControl write error: ", ecode.message ());
socket->close ();
if (ecode) {
LogPrint (eLogError, "I2PControl: write error: ", ecode.message ());
}
}
// handlers
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
int api = params.get<int> (I2P_CONTROL_PARAM_API);
auto password = params.get<std::string> (I2P_CONTROL_PARAM_PASSWORD);
LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password);
if (password != m_Password)
LogPrint (eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", m_Password);
InsertParam (results, I2P_CONTROL_PARAM_API, api);
int api = params.get<int> ("API");
auto password = params.get<std::string> ("Password");
LogPrint (eLogDebug, "I2PControl: Authenticate API=", api, " Password=", password);
if (password != m_Password) {
LogPrint (eLogError, "I2PControl: Authenticate - Invalid password: ", password);
return;
}
InsertParam (results, "API", api);
results << ",";
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
m_Tokens.insert (token);
InsertParam (results, I2P_CONTROL_PARAM_TOKEN, token);
InsertParam (results, "Token", token);
}
void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
auto echo = params.get<std::string> (I2P_CONTROL_PARAM_ECHO);
auto echo = params.get<std::string> ("Echo");
LogPrint (eLogDebug, "I2PControl Echo Echo=", echo);
InsertParam (results, I2P_CONTROL_PARAM_RESULT, echo);
InsertParam (results, "Result", echo);
}
@@ -271,107 +333,129 @@ namespace client
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl I2PControl");
for (auto& it: params)
{
LogPrint (eLogDebug, it.first);
LogPrint (eLogDebug, "I2PControl: I2PControl request: ", it.first);
auto it1 = m_I2PControlHandlers.find (it.first);
if (it1 != m_I2PControlHandlers.end ())
(this->*(it1->second))(it.second.data ());
{
(this->*(it1->second))(it.second.data ());
InsertParam (results, it.first, "");
}
else
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first);
LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first);
}
}
void I2PControlService::PasswordHandler (const std::string& value)
{
LogPrint (eLogWarning, "I2PControl: new password=", value, ", to make it persistent you should update your config!");
m_Password = value;
m_Tokens.clear ();
}
// RouterInfo
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl RouterInfo");
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, it->first);
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
if (it1 != m_RouterInfoHandlers.end ())
(this->*(it1->second))(results);
{
if (it != params.begin ()) results << ",";
(this->*(it1->second))(results);
}
else
LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it->first);
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
}
}
void I2PControlService::UptimeHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime ()*1000);
InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000);
}
void I2PControlService::VersionHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_VERSION, VERSION);
InsertParam (results, "i2p.router.version", VERSION);
}
void I2PControlService::StatusHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_STATUS, "???"); // TODO:
InsertParam (results, "i2p.router.status", "???"); // TODO:
}
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters ());
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
}
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS, (int)i2p::transport::transports.GetPeers ().size ());
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
}
void I2PControlService::NetStatusHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus ());
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
}
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING, (int)i2p::tunnel::tunnels.GetTransitTunnels ().size ());
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
}
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_IB_1S, (double)i2p::transport::transports.GetInBandwidth ());
double bw = i2p::transport::transports.GetInBandwidth ();
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
}
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_OB_1S, (double)i2p::transport::transports.GetOutBandwidth ());
double bw = i2p::transport::transports.GetOutBandwidth ();
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
}
void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
}
void I2PControlService::NetTotalSentBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
}
// RouterManager
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl RouterManager");
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, it->first);
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
auto it1 = m_RouterManagerHandlers.find (it->first);
if (it1 != m_RouterManagerHandlers.end ())
if (it1 != m_RouterManagerHandlers.end ()) {
(this->*(it1->second))(results);
else
LogPrint (eLogError, "I2PControl RouterManager unknown request ", it->first);
} else
LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first);
}
}
}
void I2PControlService::ShutdownHandler (std::ostringstream& results)
void I2PControlService::ShutdownHandler (std::ostringstream& results)
{
LogPrint (eLogInfo, "Shutdown requested");
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
LogPrint (eLogInfo, "I2PControl: Shutdown requested");
InsertParam (results, "Shutdown", "");
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
Daemon.running = 0;
});
}
@@ -379,38 +463,101 @@ namespace client
{
i2p::context.SetAcceptsTunnels (false);
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout ();
LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds");
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL, "");
LogPrint (eLogInfo, "I2PControl: Graceful shutdown requested, ", timeout, " seconds remains");
InsertParam (results, "ShutdownGraceful", "");
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
Daemon.running = 0;
});
}
void I2PControlService::ReseedHandler (std::ostringstream& results)
{
LogPrint (eLogInfo, "Reseed requested");
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
LogPrint (eLogInfo, "I2PControl: Reseed requested");
InsertParam (results, "Reseed", "");
i2p::data::netdb.Reseed ();
}
// network setting
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl NetworkSetting");
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, it->first);
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
auto it1 = m_NetworkSettingHandlers.find (it->first);
if (it1 != m_NetworkSettingHandlers.end ())
if (it1 != m_NetworkSettingHandlers.end ()) {
(this->*(it1->second))(it->second.data (), results);
else
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it->first);
} else
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
}
}
void I2PControlService::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
{
if (value != "null")
i2p::context.SetBandwidth (std::atoi(value.c_str()));
int bw = i2p::context.GetBandwidthLimit();
InsertParam (results, "i2p.router.net.bw.in", bw);
}
void I2PControlService::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
{
if (value != "null")
i2p::context.SetBandwidth (std::atoi(value.c_str()));
int bw = i2p::context.GetBandwidthLimit();
InsertParam (results, "i2p.router.net.bw.out", bw);
}
// certificate
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
{
FILE *f = NULL;
EVP_PKEY * pkey = EVP_PKEY_new ();
RSA * rsa = RSA_new ();
BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ());
RSA_generate_key_ex (rsa, 4096, e, NULL);
BN_free (e);
if (rsa)
{
EVP_PKEY_assign_RSA (pkey, rsa);
X509 * x509 = X509_new ();
ASN1_INTEGER_set (X509_get_serialNumber (x509), 1);
X509_gmtime_adj (X509_get_notBefore (x509), 0);
X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration
X509_set_pubkey (x509, pkey); // public key
X509_NAME * name = X509_get_subject_name (x509);
X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"RU", -1, -1, 0); // country (Russia by default)
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
X509_set_issuer_name (x509, name); // set issuer to ourselves
X509_sign (x509, pkey, EVP_sha1 ()); // sign
// save cert
if ((f = fopen (crt_path, "wb")) != NULL) {
LogPrint (eLogInfo, "I2PControl: saving new cert to ", crt_path);
PEM_write_X509 (f, x509);
fclose (f);
} else {
LogPrint (eLogError, "I2PControl: can't write cert: ", strerror(errno));
}
// save key
if ((f = fopen (key_path, "wb")) != NULL) {
LogPrint (eLogInfo, "I2PControl: saving cert key to ", key_path);
PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL);
fclose (f);
} else {
LogPrint (eLogError, "I2PControl: can't write key: ", strerror(errno));
}
X509_free (x509);
} else {
LogPrint (eLogError, "I2PControl: can't create RSA key for certificate");
}
EVP_PKEY_free (pkey);
}
}
}

View File

@@ -10,6 +10,7 @@
#include <map>
#include <set>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/property_tree/ptree.hpp>
namespace i2p
@@ -17,56 +18,18 @@ namespace i2p
namespace client
{
const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024;
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie";
const char I2P_CONTROL_PROPERTY_ID[] = "id";
const char I2P_CONTROL_PROPERTY_METHOD[] = "method";
const char I2P_CONTROL_PROPERTY_PARAMS[] = "params";
const char I2P_CONTROL_PROPERTY_RESULT[] = "result";
// methods
const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate";
const char I2P_CONTROL_METHOD_ECHO[] = "Echo";
const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl";
const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo";
const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager";
const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting";
// params
const char I2P_CONTROL_PARAM_API[] = "API";
const char I2P_CONTROL_PARAM_PASSWORD[] = "Password";
const char I2P_CONTROL_PARAM_TOKEN[] = "Token";
const char I2P_CONTROL_PARAM_ECHO[] = "Echo";
const char I2P_CONTROL_PARAM_RESULT[] = "Result";
// I2PControl
const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address";
const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password";
const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port";
// RouterInfo requests
const char I2P_CONTROL_ROUTER_INFO_UPTIME[] = "i2p.router.uptime";
const char I2P_CONTROL_ROUTER_INFO_VERSION[] = "i2p.router.version";
const char I2P_CONTROL_ROUTER_INFO_STATUS[] = "i2p.router.status";
const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers";
const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers";
const char I2P_CONTROL_ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status";
const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating";
const char I2P_CONTROL_ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s";
const char I2P_CONTROL_ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s";
// RouterManager requests
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown";
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful";
const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed";
const long I2P_CONTROL_CERTIFICATE_VALIDITY = 365*10; // 10 years
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
class I2PControlService
{
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
public:
I2PControlService (int port);
I2PControlService (const std::string& address, int port);
~I2PControlService ();
void Start ();
@@ -76,14 +39,19 @@ namespace client
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
void Handshake (std::shared_ptr<ssl_socket> socket);
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
void ReadRequest (std::shared_ptr<ssl_socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void BuildErrorResponse (std::string & content, int code, const char *message);
void SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::string& response, bool isHtml);
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void CreateCertificate (const char *crt_path, const char *key_path);
private:
@@ -99,10 +67,11 @@ namespace client
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
// I2PControl
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
void PasswordHandler (const std::string& value);
// RouterInfo
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results);
@@ -110,11 +79,13 @@ namespace client
void VersionHandler (std::ostringstream& results);
void StatusHandler (std::ostringstream& results);
void NetDbKnownPeersHandler (std::ostringstream& results);
void NetDbActivePeersHandler (std::ostringstream& results);
void NetStatusHandler (std::ostringstream& results);
void NetDbActivePeersHandler (std::ostringstream& results);
void NetStatusHandler (std::ostringstream& results);
void TunnelsParticipatingHandler (std::ostringstream& results);
void InboundBandwidth1S (std::ostringstream& results);
void OutboundBandwidth1S (std::ostringstream& results);
void NetTotalReceivedBytes (std::ostringstream& results);
void NetTotalSentBytes (std::ostringstream& results);
// RouterManager
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
@@ -123,19 +94,22 @@ namespace client
void ReseedHandler (std::ostringstream& results);
// NetworkSetting
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
void InboundBandwidthLimit (const std::string& value, std::ostringstream& results);
void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results);
private:
std::string m_Password;
bool m_IsRunning;
std::thread * m_Thread;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ssl::context m_SSLContext;
boost::asio::deadline_timer m_ShutdownTimer;
std::set<std::string> m_Tokens;
std::map<std::string, MethodHandler> m_MethodHandlers;
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;

View File

@@ -3,7 +3,7 @@
#include <inttypes.h>
#include <string.h>
#if defined(__linux__) || defined(__FreeBSD_kernel__)
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
#include <endian.h>
#elif __FreeBSD__
#include <sys/endian.h>

View File

@@ -3,7 +3,6 @@
#include "ClientContext.h"
#include "I2PService.h"
namespace i2p
{
namespace client
@@ -28,11 +27,148 @@ namespace client
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "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)
{
boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
upstream->set_option(option);
downstream->set_option(option);
}
TCPIPPipe::~TCPIPPipe()
{
Terminate();
}
void TCPIPPipe::Start()
{
AsyncReceiveUpstream();
AsyncReceiveDownstream();
}
void TCPIPPipe::Terminate()
{
if(Kill()) return;
Done(shared_from_this());
if (m_up) {
if (m_up->is_open()) {
m_up->close();
}
m_up = nullptr;
}
if (m_down) {
if (m_down->is_open()) {
m_down->close();
}
m_down = nullptr;
}
}
void TCPIPPipe::AsyncReceiveUpstream()
{
if (m_up) {
m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
}
}
void TCPIPPipe::AsyncReceiveDownstream()
{
if (m_down) {
m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
}
}
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
{
if (m_up) {
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleUpstreamWrite,
shared_from_this(),
std::placeholders::_1)
);
} else {
LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
}
}
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
{
if (m_down) {
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleDownstreamWrite,
shared_from_this(),
std::placeholders::_1)
);
} else {
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: ", (int) bytes_transfered, " bytes received");
if (ecode) {
LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
UpstreamWrite(m_upstream_buf, bytes_transfered);
}
AsyncReceiveDownstream();
}
}
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
}
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
}
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
if (ecode) {
LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered);
DownstreamWrite(m_upstream_buf, bytes_transfered);
}
AsyncReceiveUpstream();
}
}
void TCPIPAcceptor::Start ()
{
m_Acceptor.listen ();
@@ -48,33 +184,31 @@ namespace client
void TCPIPAcceptor::Accept ()
{
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (!ecode)
{
LogPrint(eLogDebug,"--- ",GetName()," accepted");
LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted");
auto handler = CreateHandler(socket);
if (handler) {
if (handler)
{
AddHandler(handler);
handler->Handle();
} else {
}
else
socket->close();
delete socket;
}
Accept();
}
else
{
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogError,"--- ",GetName()," Closing socket on accept because: ", ecode.message ());
delete socket;
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
}
}
}
}

View File

@@ -38,6 +38,7 @@ namespace client
}
inline std::shared_ptr<ClientDestination> GetLocalDestination () { return m_LocalDestination; }
inline std::shared_ptr<const ClientDestination> GetLocalDestination () const { return m_LocalDestination; }
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest) { m_LocalDestination = dest; }
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
@@ -76,30 +77,57 @@ namespace client
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
};
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192;
// bidirectional pipe for 2 tcp/ip sockets
class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> {
public:
TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream);
~TCPIPPipe();
void Start();
protected:
void Terminate();
void AsyncReceiveUpstream();
void AsyncReceiveDownstream();
void HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
void HandleUpstreamWrite(const boost::system::error_code & ecode);
void HandleDownstreamWrite(const boost::system::error_code & ecode);
void UpstreamWrite(const uint8_t * buf, size_t len);
void DownstreamWrite(const uint8_t * buf, size_t len);
private:
uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE];
uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE];
std::shared_ptr<boost::asio::ip::tcp::socket> m_up, m_down;
};
/* TODO: support IPv6 too */
//This is a service that listens for connections on the IP network and interacts with I2P
class TCPIPAcceptor: public I2PService
{
public:
TCPIPAcceptor (int port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
TCPIPAcceptor (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
I2PService(localDestination),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
m_Timer (GetService ()) {}
TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) :
TCPIPAcceptor (const std::string& address, int port, i2p::data::SigningKeyType kt) :
I2PService(kt),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
m_Timer (GetService ()) {}
virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); }
//If you override this make sure you call it from the children
void Start ();
//If you override this make sure you call it from the children
void Stop ();
const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; };
protected:
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket) = 0;
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
private:
void Accept();
void HandleAccept(const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
};

View File

@@ -1,5 +1,5 @@
#include <cassert>
#include "base64.h"
#include "Base.h"
#include "Log.h"
#include "Destination.h"
#include "ClientContext.h"
@@ -9,7 +9,18 @@ namespace i2p
{
namespace client
{
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, boost::asio::ip::tcp::socket * socket,
/** 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 ()),
m_IsQuiet (true)
@@ -18,14 +29,14 @@ namespace client
}
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
boost::asio::ip::tcp::socket * socket, std::shared_ptr<i2p::stream::Stream> stream):
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
{
}
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (target), m_IsQuiet (quiet)
{
@@ -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 ()
@@ -78,15 +107,24 @@ namespace client
{
if (ecode)
{
LogPrint ("I2PTunnel read error: ", ecode.message ());
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (m_Stream)
m_Stream->Send (m_Buffer, bytes_transferred);
Receive ();
{
auto s = shared_from_this ();
m_Stream->AsyncSend (m_Buffer, bytes_transferred,
[s](const boost::system::error_code& ecode)
{
if (!ecode)
s->Receive ();
else
s->Terminate ();
});
}
}
}
@@ -94,7 +132,7 @@ namespace client
{
if (ecode)
{
LogPrint ("I2PTunnel write error: ", ecode.message ());
LogPrint (eLogError, "I2PTunnel: write error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -105,43 +143,68 @@ namespace client
void I2PTunnelConnection::StreamReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
I2P_TUNNEL_CONNECTION_MAX_IDLE);
{
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
{
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
I2P_TUNNEL_CONNECTION_MAX_IDLE);
}
else // closed by peer
{
// get remaning data
auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
if (len > 0) // still some data
Write (m_StreamBuffer, len);
else // no more data
Terminate ();
}
}
}
void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint ("I2PTunnel stream read error: ", ecode.message ());
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
{
if (bytes_transferred > 0)
Write (m_StreamBuffer, bytes_transferred); // postpone termination
else
Terminate ();
}
else
Terminate ();
}
else
{
boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
}
Write (m_StreamBuffer, bytes_transferred);
}
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
{
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
}
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
{
if (ecode)
{
LogPrint ("I2PTunnel connect error: ", ecode.message ());
LogPrint (eLogError, "I2PTunnel: connect error: ", ecode.message ());
Terminate ();
}
else
{
LogPrint ("I2PTunnel connected");
LogPrint (eLogDebug, "I2PTunnel: connected");
if (m_IsQuiet)
StreamReceive ();
else
{
// send destination first like received from I2P
std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 ();
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
dest += "\n";
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
HandleStreamReceive (boost::system::error_code (), dest.size ());
@@ -150,12 +213,109 @@ namespace client
}
}
I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_From (stream->GetRemoteIdentity ())
{
}
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
{
if (m_HeaderSent)
I2PTunnelConnection::Write (buf, len);
else
{
m_InHeader.clear ();
m_InHeader.write ((const char *)buf, len);
std::string line;
bool endOfHeader = false;
while (!endOfHeader)
{
std::getline(m_InHeader, line);
if (!m_InHeader.fail ())
{
if (line == "\r") endOfHeader = true;
else
{
if (m_Host.length () > 0 && line.find ("Host:") != std::string::npos)
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
else
m_OutHeader << line << "\n";
}
}
else
break;
}
// add X-I2P fields
if (m_From)
{
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
}
if (endOfHeader)
{
m_OutHeader << "\r\n"; // end of header
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
}
}
}
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass):
I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()),
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
{
}
void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len)
{
m_OutPacket.str ("");
if (m_NeedsWebIrc)
{
m_NeedsWebIrc = false;
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " 127.0.0.1\n";
}
m_InPacket.clear ();
m_InPacket.write ((const char *)buf, len);
while (!m_InPacket.eof () && !m_InPacket.fail ())
{
std::string line;
std::getline (m_InPacket, line);
if (line.length () == 0 && m_InPacket.eof ())
m_InPacket.str ("");
auto pos = line.find ("USER");
if (!pos) // start of line
{
pos = line.find (" ");
pos++;
pos = line.find (" ", pos);
pos++;
auto nextpos = line.find (" ", pos);
m_OutPacket << line.substr (0, pos);
m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ());
m_OutPacket << line.substr (nextpos) << '\n';
}
else
m_OutPacket << line << '\n';
}
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
}
/* This handler tries to stablish a connection with the desired server and dies if it fails to do so */
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
{
public:
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
int destinationPort, boost::asio::ip::tcp::socket * socket):
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
I2PServiceHandler(parent), m_DestinationIdentHash(destination),
m_DestinationPort (destinationPort), m_Socket(socket) {};
void Handle();
@@ -164,7 +324,7 @@ namespace client
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
i2p::data::IdentHash m_DestinationIdentHash;
int m_DestinationPort;
boost::asio::ip::tcp::socket * m_Socket;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
};
void I2PClientTunnelHandler::Handle()
@@ -179,7 +339,7 @@ namespace client
if (stream)
{
if (Kill()) return;
LogPrint (eLogInfo,"New I2PTunnel connection");
LogPrint (eLogDebug, "I2PTunnel: new connection");
auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect ();
@@ -187,7 +347,7 @@ namespace client
}
else
{
LogPrint (eLogError,"I2P Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
Terminate();
}
}
@@ -198,15 +358,17 @@ namespace client
if (m_Socket)
{
m_Socket->close();
delete m_Socket;
m_Socket = nullptr;
}
Done(shared_from_this());
}
I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
TCPIPAcceptor (port,localDestination), m_Destination (destination), m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort)
{}
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort)
{
}
void I2PClientTunnel::Start ()
{
@@ -231,12 +393,12 @@ namespace client
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
else
LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found");
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
}
return m_DestinationIdentHash;
}
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(boost::asio::ip::tcp::socket * socket)
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
const i2p::data::IdentHash *identHash = GetIdentHash();
if (identHash)
@@ -245,16 +407,30 @@ namespace client
return nullptr;
}
I2PServerTunnel::I2PServerTunnel (const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, int inport):
I2PService (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsAccessList (false)
I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address,
int port, std::shared_ptr<ClientDestination> localDestination, int inport, bool gzip):
I2PService (localDestination), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false)
{
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port);
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip);
}
void I2PServerTunnel::Start ()
{
Accept ();
m_Endpoint.port (m_Port);
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (m_Address, ec);
if (!ec)
{
m_Endpoint.address (addr);
Accept ();
}
else
{
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ());
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""),
std::bind (&I2PServerTunnel::HandleResolve, this,
std::placeholders::_1, std::placeholders::_2, resolver));
}
}
void I2PServerTunnel::Stop ()
@@ -262,6 +438,20 @@ namespace client
ClearHandlers ();
}
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
{
if (!ecode)
{
auto addr = (*it).endpoint ().address ();
LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr);
m_Endpoint.address (addr);
Accept ();
}
else
LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ());
}
void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList)
{
m_AccessList = accessList;
@@ -280,7 +470,7 @@ namespace client
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
}
else
LogPrint ("Local destination not set for server tunnel");
LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel");
}
void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
@@ -289,17 +479,284 @@ namespace client
{
if (m_IsAccessList)
{
if (!m_AccessList.count (stream->GetRemoteIdentity ().GetIdentHash ()))
if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ()))
{
LogPrint (eLogWarning, "Address ", stream->GetRemoteIdentity ().GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
stream->Close ();
return;
}
}
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
AddHandler (conn);
conn->Connect ();
CreateI2PConnection (stream);
}
}
}
}
void I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
AddHandler (conn);
conn->Connect ();
}
I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address,
int port, std::shared_ptr<ClientDestination> localDestination,
const std::string& host, int inport, bool gzip):
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
m_Host (host)
{
}
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (this, stream,
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
AddHandler (conn);
conn->Connect ();
}
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
int port, std::shared_ptr<ClientDestination> localDestination,
const std::string& webircpass, int inport, bool gzip):
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
m_WebircPass (webircpass)
{
}
void I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
AddHandler (conn);
conn->Connect ();
}
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

@@ -5,9 +5,11 @@
#include <string>
#include <set>
#include <memory>
#include <sstream>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Destination.h"
#include "Datagram.h"
#include "Streaming.h"
#include "I2PService.h"
@@ -18,28 +20,32 @@ namespace client
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
// for HTTP tunnels
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
{
public:
I2PTunnelConnection (I2PService * owner, boost::asio::ip::tcp::socket * socket,
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
I2PTunnelConnection (I2PService * owner, boost::asio::ip::tcp::socket * socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API :)
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket,
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect ();
private:
protected:
void Terminate ();
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
void HandleWrite (const boost::system::error_code& ecode);
void StreamReceive ();
@@ -49,61 +55,269 @@ namespace client
private:
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
std::unique_ptr<boost::asio::ip::tcp::socket> m_Socket;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
std::shared_ptr<i2p::stream::Stream> m_Stream;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsQuiet; // don't send destination
};
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
{
public:
I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host);
protected:
void Write (const uint8_t * buf, size_t len);
private:
std::string m_Host;
std::stringstream m_InHeader, m_OutHeader;
bool m_HeaderSent;
std::shared_ptr<const i2p::data::IdentityEx> m_From;
};
class I2PTunnelConnectionIRC: public I2PTunnelConnection
{
public:
I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass);
protected:
void Write (const uint8_t * buf, size_t len);
private:
std::shared_ptr<const i2p::data::IdentityEx> m_From;
std::stringstream m_OutPacket, m_InPacket;
bool m_NeedsWebIrc;
std::string m_WebircPass;
};
class I2PClientTunnel: public TCPIPAcceptor
{
protected:
// Implements TCPIPAcceptor
std::shared_ptr<I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
const char* GetName() { return "I2P Client Tunnel"; }
std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
public:
I2PClientTunnel (const std::string& destination, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort = 0);
I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort = 0);
~I2PClientTunnel () {}
void Start ();
void Stop ();
const char* GetName() { return m_Name.c_str (); }
private:
const i2p::data::IdentHash * GetIdentHash ();
std::string m_Destination;
private:
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:
I2PServerTunnel (const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, int inport = 0);
I2PServerTunnel (const std::string& name, const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, int inport = 0, bool gzip = true);
void Start ();
void Stop ();
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
private:
const std::string& GetAddress() const { return m_Address; }
int GetPort () const { return m_Port; };
uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); };
const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; }
const char* GetName() { return m_Name.c_str (); }
void SetMaxConnsPerMinute(const uint32_t conns) { m_PortDestination->SetMaxConnsPerMinute(conns); }
private:
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
void Accept ();
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
virtual void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
std::string m_Name, m_Address;
int m_Port;
boost::asio::ip::tcp::endpoint m_Endpoint;
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
std::set<i2p::data::IdentHash> m_AccessList;
bool m_IsAccessList;
bool m_IsAccessList;
};
class I2PServerTunnelHTTP: public I2PServerTunnel
{
public:
I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, const std::string& host,
int inport = 0, bool gzip = true);
private:
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
std::string m_Host;
};
class I2PServerTunnelIRC: public I2PServerTunnel
{
public:
I2PServerTunnelIRC (const std::string& name, const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, const std::string& webircpass,
int inport = 0, bool gzip = true);
private:
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
std::string m_WebircPass;
};
}
}

View File

@@ -1,14 +1,9 @@
#include <time.h>
#include <stdio.h>
#include <cryptopp/sha.h>
#include <cryptopp/osrng.h>
#include <cryptopp/dsa.h>
#include "base64.h"
#include "CryptoConst.h"
#include "ElGamal.h"
#include "RouterContext.h"
#include "Identity.h"
#include "Crypto.h"
#include "I2PEndian.h"
#include "Log.h"
#include "Identity.h"
namespace i2p
{
@@ -18,12 +13,16 @@ namespace data
{
// copy public and signing keys together
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
memset (&certificate, 0, sizeof (certificate));
memset (certificate, 0, sizeof (certificate));
return *this;
}
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
{
if ( len < DEFAULT_IDENTITY_SIZE ) {
// buffer too small, don't overflow
return 0;
}
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
return DEFAULT_IDENTITY_SIZE;
}
@@ -31,16 +30,17 @@ namespace data
IdentHash Identity::Hash () const
{
IdentHash hash;
CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE);
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
return hash;
}
IdentityEx::IdentityEx ():
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_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)
@@ -52,14 +52,14 @@ namespace data
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding);
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding);
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
break;
}
@@ -94,14 +94,21 @@ namespace data
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
}
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break;
}
default:
LogPrint ("Signing key type ", (int)type, " is not supported");
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
}
m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length
// fill certificate
m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY;
m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen);
m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY;
htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen);
// fill extended buffer
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
htobe16buf (m_ExtendedBuffer, type);
@@ -114,13 +121,13 @@ namespace data
// calculate ident hash
uint8_t * buf = new uint8_t[GetFullLen ()];
ToBuffer (buf, GetFullLen ());
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
SHA256(buf, GetFullLen (), m_IdentHash);
delete[] buf;
}
else // DSA-SHA1
{
memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey));
memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
m_IdentHash = m_StandardIdentity.Hash ();
m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
@@ -129,20 +136,25 @@ namespace data
}
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
FromBuffer (buf, len);
}
IdentityEx::IdentityEx (const IdentityEx& other):
m_Verifier (nullptr), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = other;
}
IdentityEx::IdentityEx (const Identity& standard):
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = standard;
}
IdentityEx::~IdentityEx ()
{
delete m_Verifier;
delete[] m_ExtendedBuffer;
}
@@ -161,8 +173,8 @@ namespace data
else
m_ExtendedBuffer = nullptr;
delete m_Verifier;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -176,8 +188,8 @@ namespace data
m_ExtendedBuffer = nullptr;
m_ExtendedLen = 0;
delete m_Verifier;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -186,15 +198,17 @@ namespace data
{
if (len < DEFAULT_IDENTITY_SIZE)
{
LogPrint (eLogError, "Identity buffer length ", len, " is too small");
LogPrint (eLogError, "Identity: buffer length ", len, " is too small");
return 0;
}
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
delete[] m_ExtendedBuffer;
if (m_StandardIdentity.certificate.length)
if(m_ExtendedBuffer) delete[] m_ExtendedBuffer;
m_ExtendedBuffer = nullptr;
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
if (m_ExtendedLen)
{
m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length);
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
{
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
@@ -202,7 +216,8 @@ namespace data
}
else
{
LogPrint (eLogError, "Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
m_ExtendedLen = 0;
return 0;
}
}
@@ -211,37 +226,40 @@ namespace data
m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
}
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
SHA256(buf, GetFullLen (), m_IdentHash);
delete m_Verifier;
m_Verifier = nullptr;
return GetFullLen ();
}
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
{
{
const size_t fullLen = GetFullLen();
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
return GetFullLen ();
return fullLen;
}
size_t IdentityEx::FromBase64(const std::string& s)
{
uint8_t buf[1024];
auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024);
return FromBuffer (buf, len);
const size_t slen = s.length();
std::vector<uint8_t> buf(slen); // binary data can't exceed base64
const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen);
return FromBuffer (buf.data(), len);
}
std::string IdentityEx::ToBase64 () const
{
uint8_t buf[1024];
char str[1536];
size_t l = ToBuffer (buf, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536);
str[l1] = 0;
return std::string (str);
const size_t bufLen = GetFullLen();
const size_t strLen = Base64EncodingBufferSize(bufLen);
std::vector<uint8_t> buf(bufLen);
std::vector<char> str(strLen);
size_t l = ToBuffer (buf.data(), bufLen);
size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen);
return std::string (str.data(), l1);
}
size_t IdentityEx::GetSigningPublicKeyLen () const
@@ -265,7 +283,7 @@ namespace data
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->GetSignatureLen ();
return 40;
return i2p::crypto::DSA_SIGNATURE_LENGTH;
}
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
@@ -277,36 +295,37 @@ namespace data
SigningKeyType IdentityEx::GetSigningKeyType () const
{
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return bufbe16toh (m_ExtendedBuffer); // signing key
return SIGNING_KEY_TYPE_DSA_SHA1;
}
CryptoKeyType IdentityEx::GetCryptoKeyType () const
{
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
return CRYPTO_KEY_TYPE_ELGAMAL;
}
void IdentityEx::CreateVerifier () const
{
if (m_Verifier) return; // don't create again
auto keyType = GetSigningKeyType ();
switch (keyType)
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey);
UpdateVerifier (new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding);
UpdateVerifier (new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding));
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding);
UpdateVerifier (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
@@ -315,7 +334,7 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey);
UpdateVerifier (new i2p::crypto::ECDSAP521Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
@@ -324,7 +343,7 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey);
UpdateVerifier (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
@@ -333,7 +352,7 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey);
UpdateVerifier (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
@@ -342,27 +361,56 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey);
UpdateVerifier (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
break;
}
}
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
UpdateVerifier (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
break;
}
default:
LogPrint ("Signing key type ", (int)keyType, " is not supported");
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
}
}
void IdentityEx::DropVerifier ()
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
{
auto verifier = m_Verifier;
m_Verifier = nullptr; // TODO: make this atomic
delete verifier;
if (!m_Verifier)
{
auto created = m_IsVerifierCreated.exchange (true);
if (!created)
m_Verifier.reset (verifier);
else
{
delete verifier;
int count = 0;
while (!m_Verifier && count < 500) // 5 seconds
{
std::this_thread::sleep_for (std::chrono::milliseconds(10));
count++;
}
if (!m_Verifier)
LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds");
}
}
else
delete verifier;
}
void IdentityEx::DropVerifier () const
{
// TODO: potential race condition with Verify
m_IsVerifierCreated = false;
m_Verifier = nullptr;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{
m_Public = Identity (keys);
m_Public = std::make_shared<IdentityEx>(Identity (keys));
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ());
delete m_Signer;
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_Signer = nullptr;
CreateSigner ();
return *this;
@@ -370,10 +418,9 @@ namespace data
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
{
m_Public = other.m_Public;
m_Public = std::make_shared<IdentityEx>(*other.m_Public);
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ());
delete m_Signer;
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_Signer = nullptr;
CreateSigner ();
return *this;
@@ -381,13 +428,14 @@ namespace data
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
{
size_t ret = m_Public.FromBuffer (buf, len);
m_Public = std::make_shared<IdentityEx>(buf, len);
size_t ret = m_Public->GetFullLen ();
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen ();
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(signingPrivateKeySize + ret > len) return 0; // overflow
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
ret += signingPrivateKeySize;
delete m_Signer;
m_Signer = nullptr;
CreateSigner ();
return ret;
@@ -395,10 +443,11 @@ namespace data
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
{
size_t ret = m_Public.ToBuffer (buf, len);
size_t ret = m_Public->ToBuffer (buf, len);
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen ();
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(ret + signingPrivateKeySize > len) return 0; // overflow
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize;
return ret;
@@ -428,37 +477,42 @@ namespace data
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
if (m_Signer)
m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
if (!m_Signer)
CreateSigner();
m_Signer->Sign (buf, len, signature);
}
void PrivateKeys::CreateSigner ()
void PrivateKeys::CreateSigner () const
{
switch (m_Public.GetSigningKeyType ())
if (m_Signer) return;
switch (m_Public->GetSigningKeyType ())
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey);
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey);
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey);
m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey);
m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey);
m_Signer.reset (new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey);
m_Signer.reset (new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey);
m_Signer.reset (new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey));
break;
default:
LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported");
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
}
}
@@ -467,39 +521,40 @@ namespace data
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
PrivateKeys keys;
auto& rnd = i2p::context.GetRandomNumberGenerator ();
// signature
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
default:
LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
}
// encryption
uint8_t publicKey[256];
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey);
i2p::crypto::GenerateElGamalKeyPair (keys.m_PrivateKey, publicKey);
// identity
keys.m_Public = IdentityEx (publicKey, signingPublicKey, type);
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type);
keys.CreateSigner ();
return keys;
@@ -510,11 +565,10 @@ namespace data
Keys CreateRandomKeys ()
{
Keys keys;
auto& rnd = i2p::context.GetRandomNumberGenerator ();
// encryption
i2p::crypto::GenerateElGamalKeyPair(rnd, keys.privateKey, keys.publicKey);
i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey);
// signing
i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey);
i2p::crypto::CreateDSARandomKeys (keys.signingPrivateKey, keys.signingKey);
return keys;
}
@@ -532,7 +586,7 @@ namespace data
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#endif
IdentHash key;
CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40);
SHA256(buf, 40, key);
return key;
}

View File

@@ -4,74 +4,21 @@
#include <inttypes.h>
#include <string.h>
#include <string>
#include "base64.h"
#include "ElGamal.h"
#include <memory>
#include <atomic>
#include "Base.h"
#include "Signature.h"
namespace i2p
{
namespace data
{
template<int sz>
class Tag
{
public:
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
Tag (const Tag<sz>& ) = default;
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
Tag (Tag<sz>&& ) = default;
#endif
Tag () = default;
Tag<sz>& operator= (const Tag<sz>& ) = default;
#ifndef _WIN32
Tag<sz>& operator= (Tag<sz>&& ) = default;
#endif
uint8_t * operator()() { return m_Buf; };
const uint8_t * operator()() const { return m_Buf; };
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
const uint64_t * GetLL () const { return ll; };
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
std::string ToBase64 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
std::string ToBase32 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
typedef Tag<32> IdentHash;
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
{
return ident.ToBase64 ().substr (0, 4);
}
#pragma pack(1)
struct Keys
{
uint8_t privateKey[256];
@@ -91,11 +38,7 @@ namespace data
{
uint8_t publicKey[256];
uint8_t signingKey[128];
struct
{
uint8_t type;
uint16_t length;
} certificate;
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
Identity () = default;
Identity (const Keys& keys) { *this = keys; };
@@ -103,7 +46,7 @@ namespace data
size_t FromBuffer (const uint8_t * buf, size_t len);
IdentHash Hash () const;
};
#pragma pack()
Keys CreateRandomKeys ();
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes
@@ -116,6 +59,7 @@ namespace data
const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
typedef uint16_t SigningKeyType;
typedef uint16_t CryptoKeyType;
@@ -128,6 +72,7 @@ namespace data
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
IdentityEx (const uint8_t * buf, size_t len);
IdentityEx (const IdentityEx& other);
IdentityEx (const Identity& standard);
~IdentityEx ();
IdentityEx& operator=(const IdentityEx& other);
IdentityEx& operator=(const Identity& standard);
@@ -138,6 +83,7 @@ namespace data
std::string ToBase64 () const;
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; };
const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; };
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
size_t GetSigningPublicKeyLen () const;
size_t GetSigningPrivateKeyLen () const;
@@ -145,17 +91,21 @@ namespace data
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
SigningKeyType GetSigningKeyType () const;
CryptoKeyType GetCryptoKeyType () const;
void DropVerifier (); // to save memory
void DropVerifier () const; // to save memory
bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); }
private:
void CreateVerifier () const;
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
private:
Identity m_StandardIdentity;
IdentHash m_IdentHash;
mutable i2p::crypto::Verifier * m_Verifier;
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;
};
@@ -164,19 +114,19 @@ namespace data
{
public:
PrivateKeys (): m_Signer (nullptr) {};
PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; };
PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; };
PrivateKeys () = default;
PrivateKeys (const PrivateKeys& other) { *this = other; };
PrivateKeys (const Keys& keys) { *this = keys; };
PrivateKeys& operator=(const Keys& keys);
PrivateKeys& operator=(const PrivateKeys& other);
~PrivateKeys () { delete m_Signer; };
~PrivateKeys () = default;
const IdentityEx& GetPublic () const { return m_Public; };
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSigningPrivateKeyLen (); };
size_t GetFullLen () const { return m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); };
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
@@ -187,14 +137,14 @@ namespace data
private:
void CreateSigner ();
void CreateSigner () const;
private:
IdentityEx m_Public;
std::shared_ptr<IdentityEx> m_Public;
uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
i2p::crypto::Signer * m_Signer;
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
};
// kademlia
@@ -219,23 +169,12 @@ namespace data
{
public:
RoutingDestination (): m_ElGamalEncryption (nullptr) {};
virtual ~RoutingDestination () { delete m_ElGamalEncryption; };
RoutingDestination () {};
virtual ~RoutingDestination () {};
virtual const IdentHash& GetIdentHash () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual bool IsDestination () const = 0; // for garlic
i2p::crypto::ElGamalEncryption * GetElGamalEncryption () const
{
if (!m_ElGamalEncryption)
m_ElGamalEncryption = new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ());
return m_ElGamalEncryption;
}
private:
mutable i2p::crypto::ElGamalEncryption * m_ElGamalEncryption; // use lazy initialization
};
class LocalDestination
@@ -243,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;
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);
};
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
};
}
}

View File

@@ -1,12 +1,10 @@
#include <string.h>
#include "I2PEndian.h"
#include <cryptopp/dsa.h>
#include <cryptopp/osrng.h>
#include "CryptoConst.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "TunnelPool.h"
#include "Tunnel.h"
#include "LeaseSet.h"
namespace i2p
@@ -14,73 +12,68 @@ namespace i2p
namespace data
{
LeaseSet::LeaseSet (const uint8_t * buf, int len)
LeaseSet::LeaseSet (const uint8_t * buf, size_t len, bool storeLeases):
m_IsValid (true), m_StoreLeases (storeLeases), m_ExpirationTime (0)
{
m_Buffer = new uint8_t[len];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
}
LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool)
void LeaseSet::Update (const uint8_t * buf, size_t len)
{
// header
const i2p::data::LocalDestination * localDestination = pool.GetLocalDestination ();
if (!localDestination)
if (len > m_BufferLen)
{
m_BufferLen = 0;
LogPrint (eLogError, "Destination for local LeaseSet doesn't exist");
return;
auto oldBuffer = m_Buffer;
m_Buffer = new uint8_t[len];
delete[] oldBuffer;
}
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;
auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
m_BufferLen++;
// leases
CryptoPP::AutoSeededRandomPool rnd;
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
ts += rnd.GenerateWord32 (0, 5); // + random milliseconds
htobe64buf (m_Buffer + m_BufferLen, ts);
m_BufferLen += 8; // end date
}
// signature
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += localDestination->GetIdentity ().GetSignatureLen ();
LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created");
ReadFromBuffer ();
}
void LeaseSet::Update (const uint8_t * buf, int len)
{
m_Leases.clear ();
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
ReadFromBuffer (false);
}
void LeaseSet::ReadFromBuffer ()
void LeaseSet::PopulateLeases ()
{
m_StoreLeases = true;
ReadFromBuffer (false);
}
void LeaseSet::ReadFromBuffer (bool readIdentity)
{
size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen);
if (readIdentity || !m_Identity)
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t size = m_Identity->GetFullLen ();
if (size > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", m_BufferLen);
m_IsValid = false;
return;
}
memcpy (m_EncryptionKey, m_Buffer + size, 256);
size += 256; // encryption key
size += m_Identity.GetSigningPublicKeyLen (); // unused signing key
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
uint8_t num = m_Buffer[size];
size++; // num
LogPrint ("LeaseSet num=", (int)num);
LogPrint (eLogDebug, "LeaseSet: read num=", (int)num);
if (!num || num > MAX_NUM_LEASES)
{
LogPrint (eLogError, "LeaseSet: incorrect number of leases", (int)num);
m_IsValid = false;
return;
}
// reset existing leases
if (m_StoreLeases)
for (auto& it: m_Leases)
it->isUpdated = false;
else
m_Leases.clear ();
// process leases
m_ExpirationTime = 0;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
const uint8_t * leases = m_Buffer + size;
for (int i = 0; i < num; i++)
{
@@ -89,53 +82,181 @@ namespace data
leases += 32; // gateway
lease.tunnelID = bufbe32toh (leases);
leases += 4; // tunnel ID
lease.endDate = bufbe64toh (leases);
lease.endDate = bufbe64toh (leases);
leases += 8; // end date
m_Leases.push_back (lease);
// check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway))
{
// if not found request it
LogPrint ("Lease's tunnel gateway not found. Requested");
netdb.RequestDestination (lease.tunnelGateway);
}
if (ts < lease.endDate + LEASE_ENDDATE_THRESHOLD)
{
if (lease.endDate > m_ExpirationTime)
m_ExpirationTime = lease.endDate;
if (m_StoreLeases)
{
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
if (!ret.second) *(*ret.first) = lease; // update existing
(*ret.first)->isUpdated = true;
// check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway))
{
// if not found request it
LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting");
netdb.RequestDestination (lease.tunnelGateway);
}
}
}
else
LogPrint (eLogWarning, "LeaseSet: Lease is expired already ");
}
if (!m_ExpirationTime)
{
LogPrint (eLogWarning, "LeaseSet: all leases are expired. Dropped");
m_IsValid = false;
return;
}
m_ExpirationTime += LEASE_ENDDATE_THRESHOLD;
// delete old leases
if (m_StoreLeases)
{
for (auto it = m_Leases.begin (); it != m_Leases.end ();)
{
if (!(*it)->isUpdated)
{
(*it)->endDate = 0; // somebody might still hold it
m_Leases.erase (it++);
}
else
++it;
}
}
// verify
if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases))
LogPrint ("LeaseSet verification failed");
if (!m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
{
LogPrint (eLogWarning, "LeaseSet: verification failed");
m_IsValid = false;
}
}
const std::vector<Lease> LeaseSet::GetNonExpiredLeases (bool withThreshold) const
uint64_t LeaseSet::ExtractTimestamp (const uint8_t * buf, size_t len) const
{
if (!m_Identity) return 0;
size_t size = m_Identity->GetFullLen ();
if (size > len) return 0;
size += 256; // encryption key
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
if (size > len) return 0;
uint8_t num = buf[size];
size++; // num
if (size + num*LEASE_SIZE > len) return 0;
uint64_t timestamp= 0 ;
for (int i = 0; i < num; i++)
{
size += 36; // gateway (32) + tunnelId(4)
auto endDate = bufbe64toh (buf + size);
size += 8; // end date
if (!timestamp || endDate < timestamp)
timestamp = endDate;
}
return timestamp;
}
bool LeaseSet::IsNewer (const uint8_t * buf, size_t len) const
{
return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen);
}
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<Lease> leases;
for (auto& it: m_Leases)
std::vector<std::shared_ptr<const Lease> > leases;
for (const auto& it: m_Leases)
{
auto endDate = it.endDate;
if (!withThreshold)
endDate -= i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000;
if (ts < endDate)
auto endDate = it->endDate;
if (withThreshold)
endDate += LEASE_ENDDATE_THRESHOLD;
else
endDate -= LEASE_ENDDATE_THRESHOLD;
if (ts < endDate && !exclude(*it))
leases.push_back (it);
}
return leases;
}
bool LeaseSet::HasExpiredLeases () const
{
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto& it: m_Leases)
if (ts >= it.endDate) return true;
for (const auto& it: m_Leases)
if (ts >= it->endDate) return true;
return false;
}
}
bool LeaseSet::HasNonExpiredLeases () const
bool LeaseSet::IsExpired () const
{
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 ();
for (auto& it: m_Leases)
if (ts < it.endDate) return true;
return false;
return ts > m_ExpirationTime;
}
}
}

View File

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

225
Log.cpp
View File

@@ -1,43 +1,190 @@
/*
* 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 "Log.h"
#include <boost/date_time/posix_time/posix_time.hpp>
Log * g_Log = nullptr;
static const char * g_LogLevelStr[eNumLogLevels] =
{
"error", // eLogError
"warn", // eLogWarning
"info", // eLogInfo
"debug" // eLogDebug
};
void LogMsg::Process()
{
output << boost::posix_time::second_clock::local_time().time_of_day () <<
"/" << g_LogLevelStr[level] << " - ";
output << s.str();
}
void Log::Flush ()
{
if (m_LogStream)
m_LogStream->flush();
}
void Log::SetLogFile (const std::string& fullFilePath)
{
auto logFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
if (logFile->is_open ())
namespace i2p {
namespace log {
static Log logger;
/**
* @brief Maps our loglevel to their symbolic name
*/
static const char * g_LogLevelStr[eNumLogLevels] =
{
SetLogStream (logFile);
LogPrint("Logging to file ", fullFilePath, " enabled.");
}
else
delete logFile;
}
"error", // eLogError
"warn", // eLogWarn
"info", // eLogInfo
"debug" // eLogDebug
};
void Log::SetLogStream (std::ostream * logStream)
{
if (m_LogStream) delete m_LogStream;
m_LogStream = logStream;
}
/**
* @brief Colorize log output -- array of terminal control sequences
* @note Using ISO 6429 (ANSI) color sequences
*/
#ifdef _WIN32
static const char *LogMsgColors[] = { "", "", "", "", "" };
#else /* UNIX */
static const char *LogMsgColors[] = {
[eLogError] = "\033[1;31m", /* red */
[eLogWarning] = "\033[1;33m", /* yellow */
[eLogInfo] = "\033[1;36m", /* cyan */
[eLogDebug] = "\033[1;34m", /* blue */
[eNumLogLevels] = "\033[0m", /* reset */
};
#endif
#ifndef _WIN32
/**
* @brief Maps our log levels to syslog one
* @return syslog priority LOG_*, as defined in syslog.h
*/
static inline int GetSyslogPrio (enum LogLevel l) {
int priority = LOG_DEBUG;
switch (l) {
case eLogError : priority = LOG_ERR; break;
case eLogWarning : priority = LOG_WARNING; break;
case eLogInfo : priority = LOG_INFO; break;
case eLogDebug : priority = LOG_DEBUG; break;
default : priority = LOG_DEBUG; break;
}
return priority;
}
#endif
Log::Log():
m_Destination(eLogStdout), m_MinLevel(eLogInfo),
m_LogStream (nullptr), m_Logfile(""), m_IsReady(false), m_HasColors(true)
{
}
Log::~Log ()
{
switch (m_Destination) {
#ifndef _WIN32
case eLogSyslog :
closelog();
break;
#endif
case eLogFile:
case eLogStream:
if (m_LogStream) m_LogStream->flush();
break;
default:
/* do nothing */
break;
}
Process();
}
void Log::SetLogLevel (const std::string& level) {
if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; }
else if (level == "info") { m_MinLevel = eLogInfo; }
else if (level == "debug") { m_MinLevel = eLogDebug; }
else {
LogPrint(eLogError, "Log: unknown loglevel: ", level);
return;
}
LogPrint(eLogInfo, "Log: min messages level set to ", level);
}
const char * Log::TimeAsString(std::time_t t) {
if (t != m_LastTimestamp) {
strftime(m_LastDateTime, sizeof(m_LastDateTime), "%H:%M:%S", localtime(&t));
m_LastTimestamp = t;
}
return m_LastDateTime;
}
/**
* @note This function better to be run in separate thread due to disk i/o.
* Unfortunately, with current startup process with late fork() this
* will give us nothing but pain. Maybe later. See in NetDb as example.
*/
void Log::Process() {
std::unique_lock<std::mutex> l(m_OutputLock);
std::hash<std::thread::id> hasher;
unsigned short short_tid;
while (1) {
auto msg = m_Queue.GetNextWithTimeout (1);
if (!msg)
break;
short_tid = (short) (hasher(msg->tid) % 1000);
switch (m_Destination) {
#ifndef _WIN32
case eLogSyslog:
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
break;
#endif
case eLogFile:
case eLogStream:
if (m_LogStream)
*m_LogStream << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << g_LogLevelStr[msg->level]
<< " - " << msg->text << std::endl;
break;
case eLogStdout:
default:
std::cout << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
<< " - " << msg->text << std::endl;
break;
} // switch
} // while
}
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg) {
m_Queue.Put(msg);
if (!m_IsReady)
return;
Process();
}
void Log::SendTo (const std::string& path)
{
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 ())
{
m_HasColors = false;
m_Logfile = path;
m_Destination = eLogFile;
m_LogStream = os;
return;
}
LogPrint(eLogError, "Log: can't open file ", path);
}
void Log::SendTo (std::shared_ptr<std::ostream> os) {
m_HasColors = false;
m_Destination = eLogStream;
m_LogStream = os;
}
#ifndef _WIN32
void Log::SendTo(const char *name, int facility) {
m_HasColors = false;
m_Destination = eLogSyslog;
m_LogStream = nullptr;
openlog(name, LOG_CONS | LOG_PID, facility);
}
#endif
void Log::Reopen() {
if (m_Destination == eLogFile)
SendTo(m_Logfile);
}
Log & Logger() {
return logger;
}
} // log
} // i2p

271
Log.h
View File

@@ -1,13 +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 LOG_H__
#define LOG_H__
#include <ctime>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <functional>
#include <sstream>
#include <chrono>
#include <memory>
#include "Queue.h"
#ifndef _WIN32
#include <syslog.h>
#endif
enum LogLevel
{
eLogError = 0,
@@ -17,101 +31,160 @@ enum LogLevel
eNumLogLevels
};
struct LogMsg
{
std::stringstream s;
std::ostream& output;
LogLevel level;
LogMsg (std::ostream& o = std::cout, LogLevel l = eLogInfo): output (o), level (l) {};
void Process();
};
class Log: public i2p::util::MsgQueue<LogMsg>
{
public:
Log (): m_LogStream (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); };
~Log () { delete m_LogStream; };
void SetLogFile (const std::string& fullFilePath);
void SetLogStream (std::ostream * logStream);
std::ostream * GetLogStream () const { return m_LogStream; };
private:
void Flush ();
private:
std::ostream * m_LogStream;
};
extern Log * g_Log;
inline void StartLog (const std::string& fullFilePath)
{
if (!g_Log)
{
g_Log = new Log ();
if (fullFilePath.length () > 0)
g_Log->SetLogFile (fullFilePath);
}
}
inline void StartLog (std::ostream * s)
{
if (!g_Log)
{
g_Log = new Log ();
if (s)
g_Log->SetLogStream (s);
}
}
inline void StopLog ()
{
if (g_Log)
{
delete g_Log;
g_Log = nullptr;
}
}
template<typename TValue>
void LogPrint (std::stringstream& s, TValue arg)
{
s << arg;
}
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
{
LogPrint (s, arg);
LogPrint (s, args...);
}
template<typename... TArgs>
void LogPrint (LogLevel level, TArgs... args)
{
LogMsg * msg = (g_Log && g_Log->GetLogStream ()) ? new LogMsg (*g_Log->GetLogStream (), level) :
new LogMsg (std::cout, level);
LogPrint (msg->s, args...);
msg->s << std::endl;
if (g_Log)
g_Log->Put (msg);
else
{
msg->Process ();
delete msg;
}
}
template<typename... TArgs>
void LogPrint (TArgs... args)
{
LogPrint (eLogInfo, args...);
}
enum LogType {
eLogStdout = 0,
eLogStream,
eLogFile,
#ifndef _WIN32
eLogSyslog,
#endif
};
namespace i2p {
namespace log {
struct LogMsg; /* forward declaration */
class Log
{
private:
enum LogType m_Destination;
enum LogLevel m_MinLevel;
std::shared_ptr<std::ostream> m_LogStream;
std::string m_Logfile;
std::time_t m_LastTimestamp;
char m_LastDateTime[64];
i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue;
volatile bool m_IsReady;
bool m_HasColors;
mutable std::mutex m_OutputLock;
private:
/** prevent making copies */
Log (const Log &);
const Log& operator=(const Log&);
/**
* @brief process stored messages in queue
*/
void Process ();
/**
* @brief Makes formatted string from unix timestamp
* @param ts Second since epoch
*
* This function internally caches the result for last provided value
*/
const char * TimeAsString(std::time_t ts);
public:
Log ();
~Log ();
LogType GetLogType () { return m_Destination; };
LogLevel GetLogLevel () { return m_MinLevel; };
/**
* @brief Sets minimal allowed level for log messages
* @param level String with wanted minimal msg level
*/
void SetLogLevel (const std::string& level);
/**
* @brief Sets log destination to logfile
* @param path Path to logfile
*/
void SendTo (const std::string &path);
/**
* @brief Sets log destination to given output stream
* @param os Output stream
*/
void SendTo (std::shared_ptr<std::ostream> os);
#ifndef _WIN32
/**
* @brief Sets log destination to syslog
* @param name Wanted program name
* @param facility Wanted log category
*/
void SendTo (const char *name, int facility);
#endif
/**
* @brief Format log message and write to output stream/syslog
* @param msg Pointer to processed message
*/
void Append(std::shared_ptr<i2p::log::LogMsg> &);
/** @brief Allow log output */
void Ready() { m_IsReady = true; }
/** @brief Flushes the output log stream */
void Flush();
/** @brief Reopen log file */
void Reopen();
};
/**
* @struct LogMsg
* @brief Log message container
*
* We creating it somewhere with LogPrint(),
* then put in MsgQueue for later processing.
*/
struct LogMsg {
std::time_t timestamp;
std::string text; /**< message text as single string */
LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */
LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {};
};
Log & Logger();
} // log
}
/** internal usage only -- folding args array to single string */
template<typename TValue>
void LogPrint (std::stringstream& s, TValue&& arg) noexcept
{
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) noexcept
{
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
}
/**
* @brief Create log message and send it to queue
* @param level Message level (eLogError, eLogInfo, ...)
* @param args Array of message parts
*/
template<typename... TArgs>
void LogPrint (LogLevel level, TArgs&&... args) noexcept
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
return;
// fold message to single string
std::stringstream ss("");
LogPrint (ss, std::forward<TArgs>(args)...);
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
msg->tid = std::this_thread::get_id();
log.Append(msg);
}
#endif // LOG_H__

View File

@@ -1,33 +1,54 @@
UNAME := $(shell uname -s)
SHLIB := libi2pd.so
I2PD := i2p
GREP := fgrep
ARLIB := libi2pd.a
SHLIB_CLIENT := libi2pdclient.so
ARLIB_CLIENT := libi2pdclient.a
I2PD := i2pd
GREP := grep
DEPS := obj/make.dep
include filelist.mk
USE_AESNI := yes
USE_STATIC := no
USE_MESHNET := no
USE_UPNP := no
ifeq ($(WEBSOCKETS),1)
NEEDED_CXXFLAGS += -DWITH_EVENTS
DAEMON_SRC += Websocket.cpp
endif
ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.osx
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
ifeq ($(HOMEBREW),1)
include Makefile.homebrew
else
include Makefile.osx
endif
else ifeq ($(shell echo $(UNAME) | $(GREP) -Ec '(Free|Open)BSD'),1)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.bsd
else ifeq ($(UNAME),Linux)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.linux
else # win32
DAEMON_SRC += DaemonWin32.cpp
else # win32 mingw
DAEMON_SRC += DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp
include Makefile.mingw
endif
all: mk_build_dir $(SHLIB) $(I2PD)
ifeq ($(USE_MESHNET),yes)
NEEDED_CXXFLAGS += -DMESHNET
endif
mk_build_dir:
mkdir -p obj
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
api: $(SHLIB)
mk_obj_dir:
@mkdir -p obj
@mkdir -p obj/Win32
api: mk_obj_dir $(SHLIB) $(ARLIB)
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build.
@@ -36,19 +57,18 @@ api: $(SHLIB)
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time.
deps:
@mkdir -p obj
deps: mk_obj_dir
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
obj/%.o : %.cpp
@mkdir -p obj
obj/%.o: %.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
# '-' is 'ignore if missing' on first run
-include $(DEPS)
$(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
@@ -56,18 +76,41 @@ ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
endif
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
ar -r $@ $^
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
ar -r $@ $^
clean:
rm -rf obj
$(RM) $(I2PD) $(SHLIB)
rm -rf docs/generated
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
strip $^
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
BRANCH=$(shell git branch --no-color | cut -c 3-)
dist:
git archive --format=tar.gz -9 --worktree-attributes \
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
last-dist:
git archive --format=tar.gz -9 --worktree-attributes \
--prefix=i2pd_$(LATEST_TAG)/ $(BRANCH) -o ../i2pd_$(LATEST_TAG).orig.tar.gz
doxygen:
doxygen -s docs/Doxyfile
.PHONY: all
.PHONY: clean
.PHONY: deps
.PHONY: doxygen
.PHONY: dist
.PHONY: api
.PHONY: mk_build_dir
.PHONY: api_client
.PHONY: mk_obj_dir

View File

@@ -1,4 +1,4 @@
CXX = g++
CXX = clang++
CXXFLAGS = -O2
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build.
@@ -6,7 +6,7 @@ CXXFLAGS = -O2
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time.
NEEDED_CXXFLAGS = -std=c++11
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 = -lcryptopp -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

29
Makefile.homebrew Normal file
View File

@@ -0,0 +1,29 @@
# root directory holding homebrew
BREWROOT = /usr/local/
BOOSTROOT = ${BREWROOT}/opt/boost
SSLROOT = ${BREWROOT}/opt/libressl
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_program_options -lpthread
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif
# OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
# note from psi: 2009 macbook does not have aesni
#ifeq ($(USE_AESNI),yes)
# CXXFLAGS += -maes -DAESNI
#endif
# Disabled, since it will be the default make rule. I think its better
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
#install: all
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
# cp -r i2p ${PREFIX}/

View File

@@ -1,5 +1,6 @@
CXXFLAGS = -g -Wall
INCFLAGS =
# set defaults instead redefine
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
## **without** overwriting the CXXFLAGS which we need in order to build.
@@ -8,15 +9,17 @@ INCFLAGS =
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time.
# detect proper flag for c++11 support by gcc
# detect proper flag for c++11 support by compilers
CXXVER := $(shell $(CXX) -dumpversion)
ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # >= 4.10
ifeq ($(shell expr match $(CXX) 'clang'),5)
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
NEEDED_CXXFLAGS += -std=c++11
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match $(CXX) 'clang'),5)
else ifeq ($(shell expr match ${CXXVER} "[5-6]\.[0-9]"),3) # gcc >= 5.0
NEEDED_CXXFLAGS += -std=c++11
else # not supported
$(error Compiler too old)
@@ -25,22 +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)/libcryptopp.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl
USE_AESNI := no
else
LDLIBS = -lcryptopp -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
@@ -48,7 +55,7 @@ IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64")
ifeq ($(USE_AESNI),yes)
ifeq ($(IS_64),1)
#check if AES-NI is supported by CPU
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
CPU_FLAGS = -maes -DAESNI
endif
endif

53
Makefile.mingw Normal file
View File

@@ -0,0 +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
# 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

@@ -1,11 +1,11 @@
CXX = clang++
CXXFLAGS = -g -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM -DMAC_OSX
#CXXFLAGS = -g -O2 -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM
INCFLAGS = -I/usr/local/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
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_program_options -lpthread
ifeq ($(USE_UPNP),1)
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif

View File

@@ -1,16 +1,19 @@
#include <string.h>
#include <stdlib.h>
#include "I2PEndian.h"
#include <cryptopp/dh.h>
#include "base64.h"
#include "Base.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "CryptoConst.h"
#include "I2NPProtocol.h"
#include "RouterContext.h"
#include "Transports.h"
#include "NetDb.h"
#include "NTCPSession.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
using namespace i2p::crypto;
@@ -19,11 +22,11 @@ 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_DHKeysPair = transports.GetNextDHKeysPair ();
m_Establisher = new Establisher;
}
@@ -34,15 +37,9 @@ namespace transport
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
{
CryptoPP::DH dh (elgp, elgg);
uint8_t sharedKey[256];
if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey))
{
LogPrint (eLogError, "Couldn't create shared key");
Terminate ();
return;
};
m_DHKeysPair->Agree (pubKey, sharedKey);
uint8_t * aesKey = key;
if (sharedKey[0] & 0x80)
{
@@ -60,7 +57,7 @@ namespace transport
nonZero++;
if (nonZero - sharedKey > 32)
{
LogPrint (eLogWarning, "First 32 bytes of shared key is all zeros. Ignored");
LogPrint (eLogWarning, "NTCP: First 32 bytes of shared key is all zeros, ignored");
return;
}
}
@@ -82,16 +79,9 @@ namespace transport
m_Socket.close ();
transports.PeerDisconnected (shared_from_this ());
m_Server.RemoveNTCPSession (shared_from_this ());
for (auto it: m_SendQueue)
DeleteI2NPMessage (it);
m_SendQueue.clear ();
if (m_NextMessage)
{
i2p::DeleteI2NPMessage (m_NextMessage);
m_NextMessage = nullptr;
}
m_TerminationTimer.cancel ();
LogPrint (eLogInfo, "NTCP session terminated");
m_NextMessage = nullptr;
LogPrint (eLogDebug, "NTCP: session terminated");
}
}
@@ -102,12 +92,9 @@ namespace transport
delete m_Establisher;
m_Establisher = nullptr;
delete m_DHKeysPair;
m_DHKeysPair = nullptr;
SendTimeSyncMessage ();
PostI2NPMessage (CreateDatabaseStoreMsg ()); // we tell immediately who we are
SendTimeSyncMessage ();
transports.PeerConnected (shared_from_this ());
}
@@ -116,16 +103,15 @@ namespace transport
if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair ();
// send Phase1
const uint8_t * x = m_DHKeysPair->publicKey;
const uint8_t * x = m_DHKeysPair->GetPublicKey ();
memcpy (m_Establisher->phase1.pubKey, x, 256);
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256);
const uint8_t * ident = m_RemoteIdentity.GetIdentHash ();
SHA256(x, 256, m_Establisher->phase1.HXxorHI);
const uint8_t * ident = m_RemoteIdentity->GetIdentHash ();
for (int i = 0; i < 32; i++)
m_Establisher->phase1.HXxorHI[i] ^= ident[i];
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 ()
@@ -139,15 +125,15 @@ 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 ();
}
}
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
(void) bytes_transferred;
if (ecode)
{
LogPrint (eLogError, "Couldn't send Phase 1 message: ", ecode.message ());
LogPrint (eLogInfo, "NTCP: couldn't send Phase 1 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -161,9 +147,10 @@ namespace transport
void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
(void) bytes_transferred;
if (ecode)
{
LogPrint (eLogError, "Phase 1 read error: ", ecode.message ());
LogPrint (eLogInfo, "NTCP: phase 1 read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -171,13 +158,13 @@ namespace transport
{
// verify ident
uint8_t digest[32];
CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256);
const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash ();
SHA256(m_Establisher->phase1.pubKey, 256, digest);
const uint8_t * ident = i2p::context.GetIdentHash ();
for (int i = 0; i < 32; i++)
{
if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i])
{
LogPrint (eLogError, "Wrong ident");
LogPrint (eLogError, "NTCP: phase 1 error: ident mismatch");
Terminate ();
return;
}
@@ -191,15 +178,15 @@ namespace transport
{
if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair ();
const uint8_t * y = m_DHKeysPair->publicKey;
const uint8_t * y = m_DHKeysPair->GetPublicKey ();
memcpy (m_Establisher->phase2.pubKey, y, 256);
uint8_t xy[512];
memcpy (xy, m_Establisher->phase1.pubKey, 256);
memcpy (xy + 256, y, 256);
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512);
SHA256(xy, 512, m_Establisher->phase2.encrypted.hxy);
uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ());
m_Establisher->phase2.encrypted.timestamp = tsB;
// TODO: fill filler
memcpy (m_Establisher->phase2.encrypted.timestamp, &tsB, 4);
RAND_bytes (m_Establisher->phase2.encrypted.filler, 12);
i2p::crypto::AESKey aesKey;
CreateAESKey (m_Establisher->phase1.pubKey, aesKey);
@@ -216,9 +203,10 @@ namespace transport
void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
{
(void) bytes_transferred;
if (ecode)
{
LogPrint (eLogError, "Couldn't send Phase 2 message: ", ecode.message ());
LogPrint (eLogInfo, "NTCP: Couldn't send Phase 2 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -232,13 +220,14 @@ namespace transport
void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
(void) bytes_transferred;
if (ecode)
{
LogPrint (eLogError, "Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
LogPrint (eLogInfo, "NTCP: Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
if (ecode != boost::asio::error::operation_aborted)
{
// this RI is not valid
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
transports.ReuseDHKeysPair (m_DHKeysPair);
m_DHKeysPair = nullptr;
Terminate ();
@@ -256,11 +245,13 @@ namespace transport
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
// verify
uint8_t xy[512];
memcpy (xy, m_DHKeysPair->publicKey, 256);
memcpy (xy, m_DHKeysPair->GetPublicKey (), 256);
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512))
uint8_t digest[32];
SHA256 (xy, 512, digest);
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
{
LogPrint (eLogError, "Incorrect hash");
LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash");
transports.ReuseDHKeysPair (m_DHKeysPair);
m_DHKeysPair = nullptr;
Terminate ();
@@ -274,19 +265,20 @@ namespace transport
{
auto keys = i2p::context.GetPrivateKeys ();
uint8_t * buf = m_ReceiveBuffer;
htobe16buf (buf, keys.GetPublic ().GetFullLen ());
htobe16buf (buf, keys.GetPublic ()->GetFullLen ());
buf += 2;
buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE);
buf += i2p::context.GetIdentity ()->ToBuffer (buf, NTCP_BUFFER_SIZE);
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
htobuf32(buf,tsA);
buf += 4;
size_t signatureLen = keys.GetPublic ().GetSignatureLen ();
size_t signatureLen = keys.GetPublic ()->GetSignatureLen ();
size_t len = (buf - m_ReceiveBuffer) + signatureLen;
size_t paddingSize = len & 0x0F; // %16
if (paddingSize > 0)
{
paddingSize = 16 - paddingSize;
// TODO: fill padding with random data
// fill padding with random data
RAND_bytes(buf, paddingSize);
buf += paddingSize;
len += paddingSize;
}
@@ -294,9 +286,9 @@ namespace transport
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB
s.Sign (keys, buf);
m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer);
@@ -306,16 +298,17 @@ namespace transport
void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
{
(void) bytes_transferred;
if (ecode)
{
LogPrint (eLogError, "Couldn't send Phase 3 message: ", ecode.message ());
LogPrint (eLogInfo, "NTCP: Couldn't send Phase 3 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
// wait for phase4
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize);
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
@@ -328,7 +321,7 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogError, "Phase 3 read error: ", ecode.message ());
LogPrint (eLogInfo, "NTCP: Phase 3 read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -337,20 +330,20 @@ namespace transport
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
uint8_t * buf = m_ReceiveBuffer;
uint16_t size = bufbe16toh (buf);
m_RemoteIdentity.FromBuffer (buf + 2, size);
if (m_Server.FindNTCPSession (m_RemoteIdentity.GetIdentHash ()))
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (buf + 2, size));
if (m_Server.FindNTCPSession (m_RemoteIdentity->GetIdentHash ()))
{
LogPrint (eLogError, "NTCP session already exists");
LogPrint (eLogInfo, "NTCP: session already exists");
Terminate ();
}
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen ();
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen ();
size_t paddingLen = expectedSize & 0x0F;
if (paddingLen) paddingLen = (16 - paddingLen);
if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE)
{
// we need more bytes for Phase3
expectedSize += paddingLen;
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (),
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize - NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, tsB, paddingLen));
}
@@ -363,7 +356,7 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogError, "Phase 3 extra read error: ", ecode.message ());
LogPrint (eLogInfo, "NTCP: Phase 3 extra read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -376,11 +369,22 @@ namespace transport
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
{
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/;
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity->GetFullLen () + 2 /*size*/;
uint32_t tsA = buf32toh(buf);
buf += 4;
buf += paddingLen;
// check timestamp
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t tsA1 = be32toh (tsA);
if (tsA1 < ts - NTCP_CLOCK_SKEW || tsA1 > ts + NTCP_CLOCK_SKEW)
{
LogPrint (eLogError, "NTCP: Phase3 time difference ", ts - tsA1, " exceeds clock skew");
Terminate ();
return;
}
// check signature
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
@@ -389,11 +393,10 @@ namespace transport
s.Insert (tsB); // tsB
if (!s.Verify (m_RemoteIdentity, buf))
{
LogPrint (eLogError, "signature verification failed");
LogPrint (eLogError, "NTCP: signature verification failed");
Terminate ();
return;
}
m_RemoteIdentity.DropVerifier ();
SendPhase4 (tsA, tsB);
}
@@ -403,11 +406,11 @@ namespace transport
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (tsB); // tsB
auto keys = i2p::context.GetPrivateKeys ();
auto signatureLen = keys.GetPublic ().GetSignatureLen ();
auto signatureLen = keys.GetPublic ()->GetSignatureLen ();
s.Sign (keys, m_ReceiveBuffer);
size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize);
@@ -419,15 +422,16 @@ namespace transport
void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
(void) bytes_transferred;
if (ecode)
{
LogPrint (eLogWarning, "Couldn't send Phase 4 message: ", ecode.message ());
LogPrint (eLogWarning, "NTCP: Couldn't send Phase 4 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
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 ();
@@ -441,11 +445,11 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogError, "Phase 4 read error: ", ecode.message (), ". Check your clock");
LogPrint (eLogError, "NTCP: Phase 4 read error: ", ecode.message (), ". Check your clock");
if (ecode != boost::asio::error::operation_aborted)
{
// this router doesn't like us
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
Terminate ();
}
}
@@ -453,22 +457,31 @@ namespace transport
{
m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
// check timestamp
uint32_t tsB = bufbe32toh (m_Establisher->phase2.encrypted.timestamp);
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (tsB < ts - NTCP_CLOCK_SKEW || tsB > ts + NTCP_CLOCK_SKEW)
{
LogPrint (eLogError, "NTCP: Phase4 time difference ", ts - tsB, " exceeds clock skew");
Terminate ();
return;
}
// verify signature
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident
s.Insert (i2p::context.GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB
if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer))
{
LogPrint (eLogError, "signature verification failed");
LogPrint (eLogError, "NTCP: Phase 4 process error: signature verification failed");
Terminate ();
return;
}
m_RemoteIdentity.DropVerifier ();
LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected");
LogPrint (eLogDebug, "NTCP: session to ", m_Socket.remote_endpoint (), " connected");
Connected ();
m_ReceiveBufferOffset = 0;
@@ -486,12 +499,13 @@ namespace transport
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "Read error: ", ecode.message ());
if (!m_NumReceivedBytes) m_Server.Ban (m_ConnectedFrom);
if (ecode) {
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogDebug, "NTCP: Read error: ", ecode.message ());
if (!m_NumReceivedBytes)
m_Server.Ban (m_ConnectedFrom);
//if (ecode != boost::asio::error::operation_aborted)
Terminate ();
Terminate ();
}
else
{
@@ -530,7 +544,7 @@ namespace transport
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes));
if (ec)
{
LogPrint (eLogError, "Read more bytes error: ", ec.message ());
LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ());
Terminate ();
return;
}
@@ -544,7 +558,7 @@ namespace transport
m_Handler.Flush ();
}
ScheduleTermination (); // reset termination timer
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
Receive ();
}
}
@@ -553,19 +567,20 @@ namespace transport
{
if (!m_NextMessage) // new message, header expected
{
// descrypt header and extract length
// decrypt header and extract length
uint8_t buf[16];
m_Decryption.Decrypt (encrypted, buf);
uint16_t dataSize = bufbe16toh (buf);
if (dataSize)
{
// new message
if (dataSize > NTCP_MAX_MESSAGE_SIZE)
if (dataSize + 16U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
{
LogPrint (eLogError, "NTCP data size ", dataSize, " exceeds max size");
LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size");
return false;
}
m_NextMessage = dataSize <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
auto msg = (dataSize + 16U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
m_NextMessage = msg;
memcpy (m_NextMessage->buf, buf, 16);
m_NextMessageOffset = 16;
m_NextMessage->offset = 2; // size field
@@ -574,7 +589,7 @@ namespace transport
else
{
// timestamp
LogPrint ("Timestamp");
LogPrint (eLogDebug, "NTCP: Timestamp");
return true;
}
}
@@ -587,20 +602,35 @@ namespace transport
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
{
// we have a complete I2NP message
m_Handler.PutNextMessage (m_NextMessage);
uint8_t checksum[4];
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->buf, m_NextMessageOffset - 4));
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4))
{
if (!m_NextMessage->IsExpired ())
{
#ifdef WITH_EVENTS
EmitEvent({{"type", "transport.recvmsg"} , {"ident", GetIdentHashBase64()}, {"number", "1"}});
#endif
m_Handler.PutNextMessage (m_NextMessage);
}
else
LogPrint (eLogInfo, "NTCP: message expired");
}
else
LogPrint (eLogWarning, "NTCP: Incorrect adler checksum of message, dropped");
m_NextMessage = nullptr;
}
return true;
}
void NTCPSession::Send (i2p::I2NPMessage * msg)
void NTCPSession::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{
m_IsSending = true;
boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<I2NPMessage *>{ msg }));
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<std::shared_ptr<I2NPMessage> >{ msg }));
}
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (I2NPMessage * msg)
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg)
{
uint8_t * sendBuffer;
int len;
@@ -609,10 +639,7 @@ namespace transport
{
// regular I2NP
if (msg->offset < 2)
{
LogPrint (eLogError, "Malformed I2NP message");
i2p::DeleteI2NPMessage (msg);
}
LogPrint (eLogError, "NTCP: Malformed I2NP message"); // TODO:
sendBuffer = msg->GetBuffer () - 2;
len = msg->GetLength ();
htobe16buf (sendBuffer, len);
@@ -627,9 +654,12 @@ namespace transport
}
int rem = (len + 6) & 0x0F; // %16
int padding = 0;
if (rem > 0) padding = 16 - rem;
// TODO: fill padding
m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
if (rem > 0) {
padding = 16 - rem;
// fill with random padding
RAND_bytes(sendBuffer + len + 2, padding);
}
htobe32buf (sendBuffer + len + 2 + padding, adler32 (adler32 (0, Z_NULL, 0), sendBuffer, len + 2+ padding));
int l = len + padding + 6;
m_Encryption.Encrypt(sendBuffer, l, sendBuffer);
@@ -637,30 +667,30 @@ namespace transport
}
void NTCPSession::Send (const std::vector<I2NPMessage *>& msgs)
void NTCPSession::Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
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));
}
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<I2NPMessage *> msgs)
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs)
{
(void) msgs;
m_IsSending = false;
for (auto it: msgs)
if (it) i2p::DeleteI2NPMessage (it);
if (ecode)
{
LogPrint (eLogWarning, "Couldn't send msgs: ", ecode.message ());
LogPrint (eLogWarning, "NTCP: Couldn't send msgs: ", ecode.message ());
// we shouldn't call Terminate () here, because HandleReceive takes care
// TODO: 'delete this' statement in Terminate () must be eliminated later
// Terminate ();
}
else
{
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumSentBytes += bytes_transferred;
i2p::transport::transports.UpdateSentBytes (bytes_transferred);
if (!m_SendQueue.empty())
@@ -668,8 +698,6 @@ namespace transport
Send (m_SendQueue);
m_SendQueue.clear ();
}
else
ScheduleTermination (); // reset termination timer
}
}
@@ -679,71 +707,36 @@ namespace transport
Send (nullptr);
}
void NTCPSession::SendI2NPMessage (I2NPMessage * msg)
{
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessage, shared_from_this (), msg));
}
void NTCPSession::PostI2NPMessage (I2NPMessage * msg)
{
if (msg)
{
if (m_IsTerminated)
{
DeleteI2NPMessage (msg);
return;
}
if (m_IsSending)
m_SendQueue.push_back (msg);
else
Send (msg);
}
}
void NTCPSession::SendI2NPMessages (const std::vector<I2NPMessage *>& msgs)
void NTCPSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs));
}
void NTCPSession::PostI2NPMessages (std::vector<I2NPMessage *> msgs)
void NTCPSession::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
{
if (m_IsTerminated)
{
for (auto it: msgs)
DeleteI2NPMessage (it);
return;
}
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 ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds");
//Terminate ();
m_Socket.close ();// invoke Terminate () from HandleReceive
}
}
//-----------------------------------------
NTCPServer::NTCPServer (int port):
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)
{
}
@@ -759,34 +752,52 @@ namespace transport
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
// create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses)
auto& addresses = context.GetRouterInfo ().GetAddresses ();
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, "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, "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 ();
}
}
@@ -797,11 +808,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)
{
@@ -823,32 +840,34 @@ namespace transport
}
catch (std::exception& ex)
{
LogPrint ("NTCP server: ", ex.what ());
LogPrint (eLogError, "NTCP: runtime exception: ", ex.what ());
}
}
}
void NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
bool NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (session)
if (!session || !session->GetRemoteIdentity ()) return false;
auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_NTCPSessions.find (ident);
if (it != m_NTCPSessions.end ())
{
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
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));
return true;
}
void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (session)
{
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
}
if (session && session->GetRemoteIdentity ())
m_NTCPSessions.erase (session->GetRemoteIdentity ()->GetIdentHash ());
}
std::shared_ptr<NTCPSession> NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident)
{
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
auto it = m_NTCPSessions.find (ident);
if (it != m_NTCPSessions.end ())
return it->second;
@@ -863,14 +882,14 @@ namespace transport
auto ep = conn->GetSocket ().remote_endpoint(ec);
if (!ec)
{
LogPrint (eLogInfo, "Connected from ", ep);
LogPrint (eLogDebug, "NTCP: Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
@@ -880,7 +899,7 @@ namespace transport
conn->ServerLogin ();
}
else
LogPrint (eLogError, "Connected from error ", ec.message ());
LogPrint (eLogError, "NTCP: Connected from error ", ec.message ());
}
@@ -900,14 +919,14 @@ namespace transport
auto ep = conn->GetSocket ().remote_endpoint(ec);
if (!ec)
{
LogPrint (eLogInfo, "Connected from ", ep);
LogPrint (eLogDebug, "NTCP: Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
@@ -917,7 +936,7 @@ namespace transport
conn->ServerLogin ();
}
else
LogPrint (eLogError, "Connected from error ", ec.message ());
LogPrint (eLogError, "NTCP: Connected from error ", ec.message ());
}
if (error != boost::asio::error::operation_aborted)
@@ -930,27 +949,27 @@ namespace transport
void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn)
{
LogPrint (eLogInfo, "Connecting to ", address ,":", port);
m_Service.post([conn, this]()
{
this->AddNTCPSession (conn);
});
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port);
m_Service.post([=]()
{
if (this->AddNTCPSession (conn))
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
});
}
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
{
if (ecode)
{
LogPrint (eLogError, "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);
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
else
{
LogPrint (eLogInfo, "Connected to ", conn->GetSocket ().remote_endpoint ());
LogPrint (eLogDebug, "NTCP: Connected to ", conn->GetSocket ().remote_endpoint ());
if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ());
conn->ClientLogin ();
@@ -961,7 +980,33 @@ namespace transport
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
LogPrint (eLogInfo, addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
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

@@ -7,10 +7,7 @@
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include <cryptopp/adler32.h>
#include "aes.h"
#include "Crypto.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
@@ -20,8 +17,6 @@ namespace i2p
{
namespace transport
{
#pragma pack(1)
struct NTCPPhase1
{
uint8_t pubKey[256];
@@ -34,18 +29,19 @@ namespace transport
struct
{
uint8_t hxy[32];
uint32_t timestamp;
uint8_t timestamp[4];
uint8_t filler[12];
} encrypted;
};
#pragma pack()
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>
@@ -58,17 +54,15 @@ 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 SendI2NPMessage (I2NPMessage * msg);
void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs);
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
private:
void PostI2NPMessage (I2NPMessage * msg);
void PostI2NPMessages (std::vector<I2NPMessage *> msgs);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void Connected ();
void SendTimeSyncMessage ();
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
@@ -97,26 +91,19 @@ namespace transport
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
bool DecryptNextBlock (const uint8_t * encrypted);
void Send (i2p::I2NPMessage * msg);
boost::asio::const_buffers_1 CreateMsgBuffer (I2NPMessage * msg);
void Send (const std::vector<I2NPMessage *>& msgs);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<I2NPMessage *> msgs);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
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;
i2p::crypto::CBCEncryption m_Encryption;
CryptoPP::Adler32 m_Adler;
struct Establisher
{
@@ -128,12 +115,12 @@ namespace transport
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
int m_ReceiveBufferOffset;
i2p::I2NPMessage * m_NextMessage;
std::shared_ptr<I2NPMessage> m_NextMessage;
size_t m_NextMessageOffset;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsSending;
std::vector<I2NPMessage *> m_SendQueue;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
boost::asio::ip::address m_ConnectedFrom; // for ban
};
@@ -143,17 +130,20 @@ namespace transport
{
public:
NTCPServer (int port);
NTCPServer ();
~NTCPServer ();
void Start ();
void Stop ();
void AddNTCPSession (std::shared_ptr<NTCPSession> session);
bool AddNTCPSession (std::shared_ptr<NTCPSession> session);
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
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);
@@ -164,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:
@@ -171,9 +165,9 @@ namespace transport
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_TerminationTimer;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
std::mutex m_NTCPSessionsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
public:

1040
NetDb.cpp

File diff suppressed because it is too large Load Diff

119
NetDb.h
View File

@@ -8,7 +8,10 @@
#include <string>
#include <thread>
#include <mutex>
#include <boost/filesystem.hpp>
#include "Base.h"
#include "Gzip.h"
#include "FS.h"
#include "Queue.h"
#include "I2NPProtocol.h"
#include "RouterInfo.h"
@@ -16,45 +19,29 @@
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Reseed.h"
#include "NetDbRequests.h"
#include "Family.h"
namespace i2p
{
namespace data
{
class RequestedDestination
{
public:
const int NETDB_MIN_ROUTERS = 90;
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60*60; // 1 hour, in seconds
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60;
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours
const int NETDB_PUBLISH_INTERVAL = 60*40;
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
/** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
RequestedDestination (const IdentHash& destination, bool isExploratory = false):
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
/** function for visiting a router info we have locally */
typedef std::function<void(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoVisitor;
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers ();
bool IsExploratory () const { return m_IsExploratory; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; };
I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
I2NPMessage * CreateRequestMessage (const IdentHash& floodfill);
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r);
void Fail ();
private:
IdentHash m_Destination;
bool m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime;
RequestComplete m_RequestComplete;
};
/** 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:
@@ -64,68 +51,94 @@ namespace data
void Start ();
void Stop ();
void AddRouterInfo (const uint8_t * buf, int len);
void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
bool AddRouterInfo (const uint8_t * buf, int len);
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (I2NPMessage * msg);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
void HandleDatabaseLookupMsg (I2NPMessage * msg);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter () const;
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(const std::string & fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (I2NPMessage * msg);
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; };
// for web interface and stats
// for web interface
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:
bool CreateNetDb(boost::filesystem::path directory);
void Load (const char * directory);
void SaveUpdated (const char * directory);
void Load ();
bool LoadRouterInfo (const std::string & path);
void SaveUpdated ();
void Run (); // exploratory thread
void Explore (int numDestinations);
void Publish ();
void ManageLeaseSets ();
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;
mutable std::mutex m_FloodfillsMutex;
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, std::unique_ptr<RequestedDestination> > m_RequestedDestinations;
bool m_IsRunning;
uint64_t m_LastLoad;
std::thread * m_Thread;
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
GzipInflator m_Inflator;
Reseeder * m_Reseeder;
Families m_Families;
i2p::fs::HashedStorage m_Storage;
static const char m_NetDbPath[];
friend class NetDbRequests;
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;

156
NetDbRequests.cpp Normal file
View File

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

69
NetDbRequests.h Normal file
View File

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

View File

@@ -1,24 +1,32 @@
#include <boost/filesystem.hpp>
#include <sys/stat.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "base64.h"
#include "util.h"
#include "Base.h"
#include "FS.h"
#include "Log.h"
#include "Profiling.h"
namespace i2p
{
namespace data
{
i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
RouterProfile::RouterProfile (const IdentHash& identHash):
m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0),
m_NumTunnelsNonReplied (0)
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0)
{
}
boost::posix_time::ptime RouterProfile::GetTime () const
{
return boost::posix_time::second_clock::local_time();
}
void RouterProfile::UpdateTime ()
{
m_LastUpdateTime = boost::posix_time::second_clock::local_time();
m_LastUpdateTime = GetTime ();
}
void RouterProfile::Save ()
@@ -28,88 +36,83 @@ namespace data
participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
boost::property_tree::ptree usage;
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
// fill property tree
boost::property_tree::ptree pt;
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
// save to file
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
if (!boost::filesystem::exists (path))
{
// Create directory is necessary
if (!boost::filesystem::create_directory (path))
{
LogPrint (eLogError, "Failed to create directory ", path);
return;
}
const char * chars = GetBase64SubstitutionTable (); // 64 bytes
for (int i = 0; i < 64; i++)
{
auto path1 = path / (std::string ("p") + chars[i]);
if (!boost::filesystem::create_directory (path1))
{
LogPrint (eLogError, "Failed to create directory ", path1);
return;
}
}
std::string ident = m_IdentHash.ToBase64 ();
std::string path = m_ProfilesStorage.Path(ident);
try {
boost::property_tree::write_ini (path, pt);
} catch (std::exception& ex) {
/* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ());
}
std::string base64 = m_IdentHash.ToBase64 ();
path = path / (std::string ("p") + base64[0]);
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
try
{
boost::property_tree::write_ini (filename.string (), pt);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ());
}
}
}
void RouterProfile::Load ()
{
std::string base64 = m_IdentHash.ToBase64 ();
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
path /= std::string ("p") + base64[0];
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
if (boost::filesystem::exists (filename))
{
boost::property_tree::ptree pt;
try
{
boost::property_tree::read_ini (filename.string (), pt);
std::string ident = m_IdentHash.ToBase64 ();
std::string path = m_ProfilesStorage.Path(ident);
boost::property_tree::ptree pt;
if (!i2p::fs::Exists(path)) {
LogPrint(eLogWarning, "Profiling: no profile yet for ", ident);
return;
}
try {
boost::property_tree::read_ini (path, pt);
} catch (std::exception& ex) {
/* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ());
return;
}
try {
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (t.length () > 0)
m_LastUpdateTime = boost::posix_time::time_from_string (t);
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) {
try {
// read participations
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION);
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0);
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0);
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
} catch (boost::property_tree::ptree_bad_path& ex) {
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident);
}
try {
// read usage
auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE);
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
} catch (boost::property_tree::ptree_bad_path& ex) {
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident);
}
} else {
*this = RouterProfile (m_IdentHash);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ());
return;
}
try
{
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (t.length () > 0)
m_LastUpdateTime = boost::posix_time::time_from_string (t);
// read participations
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION);
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0);
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0);
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read profile ", base64, " :", ex.what ());
}
}
}
} catch (std::exception& ex) {
LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ());
}
}
void RouterProfile::TunnelBuildResponse (uint8_t ret)
{
UpdateTime ();
if (ret > 0)
m_NumTunnelsDeclined++;
else
m_NumTunnelsAgreed++;
UpdateTime ();
}
void RouterProfile::TunnelNonReplied ()
@@ -117,6 +120,32 @@ namespace data
m_NumTunnelsNonReplied++;
UpdateTime ();
}
bool RouterProfile::IsLowPartcipationRate () const
{
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
}
bool RouterProfile::IsLowReplyRate () const
{
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
return m_NumTunnelsNonReplied > 10*(total + 1);
}
bool RouterProfile::IsBad ()
{
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{
// reset profile
m_NumTunnelsAgreed = 0;
m_NumTunnelsDeclined = 0;
m_NumTunnelsNonReplied = 0;
isBad = false;
}
if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++;
return isBad;
}
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
{
@@ -124,5 +153,30 @@ namespace data
profile->Load (); // if possible
return profile;
}
void InitProfilesStorage ()
{
m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
}
void DeleteObsoleteProfiles ()
{
struct stat st;
std::time_t now = std::time(nullptr);
std::vector<std::string> files;
m_ProfilesStorage.Traverse(files);
for (const auto& path: files) {
if (stat(path.c_str(), &st) != 0) {
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue;
}
if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) {
LogPrint(eLogDebug, "Profiling: removing expired peer profile: ", path);
i2p::fs::Remove(path);
}
}
}
}
}
}

View File

@@ -9,33 +9,42 @@ namespace i2p
{
namespace data
{
const char PEER_PROFILES_DIRECTORY[] = "peerProfiles";
const char PEER_PROFILE_PREFIX[] = "profile-";
// sections
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
const char PEER_PROFILE_SECTION_USAGE[] = "usage";
// params
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime";
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied";
const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
class RouterProfile
{
public:
RouterProfile (const IdentHash& identHash);
RouterProfile& operator= (const RouterProfile& ) = default;
void Save ();
void Load ();
bool IsBad () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsBad ();
void TunnelBuildResponse (uint8_t ret);
void TunnelNonReplied ();
private:
boost::posix_time::ptime GetTime () const;
void UpdateTime ();
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate () const;
bool IsLowReplyRate () const;
private:
@@ -45,9 +54,14 @@ namespace data
uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined;
uint32_t m_NumTunnelsNonReplied;
// usage
uint32_t m_NumTimesTaken;
uint32_t m_NumTimesRejected;
};
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
void InitProfilesStorage ();
void DeleteObsoleteProfiles ();
}
}

73
Queue.h
View File

@@ -7,6 +7,7 @@
#include <thread>
#include <condition_variable>
#include <functional>
#include <utility>
namespace i2p
{
@@ -17,28 +18,28 @@ namespace util
{
public:
void Put (Element * e)
void Put (Element e)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_Queue.push (e);
m_Queue.push (std::move(e));
m_NonEmpty.notify_one ();
}
void Put (const std::vector<Element *>& vec)
void Put (const std::vector<Element>& vec)
{
if (!vec.empty ())
{
std::unique_lock<std::mutex> l(m_QueueMutex);
for (auto it: vec)
for (const auto& it: vec)
m_Queue.push (it);
m_NonEmpty.notify_one ();
}
}
Element * GetNext ()
Element GetNext ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
Element * el = GetNonThreadSafe ();
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait (l);
@@ -47,10 +48,10 @@ namespace util
return el;
}
Element * GetNextWithTimeout (int usec)
Element GetNextWithTimeout (int usec)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
Element * el = GetNonThreadSafe ();
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec));
@@ -85,13 +86,13 @@ namespace util
void WakeUp () { m_NonEmpty.notify_all (); };
Element * Get ()
Element Get ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe ();
}
Element * Peek ()
Element Peek ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe (true);
@@ -99,11 +100,11 @@ namespace util
private:
Element * GetNonThreadSafe (bool peek = false)
Element GetNonThreadSafe (bool peek = false)
{
if (!m_Queue.empty ())
{
Element * el = m_Queue.front ();
auto el = m_Queue.front ();
if (!peek)
m_Queue.pop ();
return el;
@@ -113,56 +114,10 @@ namespace util
private:
std::queue<Element *> m_Queue;
std::queue<Element> m_Queue;
std::mutex m_QueueMutex;
std::condition_variable m_NonEmpty;
};
template<class Msg>
class MsgQueue: public Queue<Msg>
{
public:
typedef std::function<void()> OnEmpty;
MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) {};
~MsgQueue () { Stop (); };
void Stop()
{
if (m_IsRunning)
{
m_IsRunning = false;
Queue<Msg>::WakeUp ();
m_Thread.join();
}
}
void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; };
private:
void Run ()
{
while (m_IsRunning)
{
while (Msg * msg = Queue<Msg>::Get ())
{
msg->Process ();
delete msg;
}
if (m_OnEmpty != nullptr)
m_OnEmpty ();
if (m_IsRunning)
Queue<Msg>::Wait ();
}
}
private:
volatile bool m_IsRunning;
OnEmpty m_OnEmpty;
std::thread m_Thread;
};
}
}

169
README.md
View File

@@ -1,124 +1,63 @@
i2pd
====
I2P router written in C++
[Русская версия](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.
Requirements for Linux/FreeBSD/OSX
----------------------------------
GCC 4.6 or newer, Boost 1.46 or newer, crypto++. Clang can be used instead of
GCC.
Requirements for Windows
------------------------
VS2013 (known to work with 12.0.21005.1 or newer), Boost 1.46 or newer,
crypto++ 5.62. See Win32/README-Build.txt for instructions on how to build i2pd
and its dependencies.
Downloads
------------
Official binary releases could be found at:
http://download.i2p.io/purplei2p/i2pd/releases/
Build Statuses
---------------
- Linux x64 - [![Build Status](https://jenkins.greyhat.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/)
- Linux ARM - To be added
- Mac OS X - Got it working, but not well tested. (Only works with clang, not GCC.)
- Microsoft VC13 - To be added
Testing
-------
First, build it.
On Ubuntu/Debian based
* sudo apt-get install libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-regex-dev libcrypto++-dev libboost-date-time-dev
* $ cd i2pd
* $ make
Then, run it:
$ ./i2p
The client should now reseed by itself.
To visit an I2P page, you need to find the b32 address of your destination.
After that, go to the webconsole and add it behind the url. (Remove http:// from the address)
This should resulting in for example:
http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa.b32.i2p
Cmdline options
---------------
* --host= - The external IP (deprecated).
* --port= - The port to listen on
* --httpport= - The http port to listen on
* --log= - Enable or disable logging to file. 1 for yes, 0 for no.
* --daemon= - Enable or disable daemon mode. 1 for yes, 0 for no.
* --service= - 1 if uses system folders (/var/run/i2pd.pid, /var/log/i2pd.log, /var/lib/i2pd).
* --v6= - 1 if supports communication through ipv6, off by default
* --floodfill= - 1 if router is floodfill, off by default
* --bandwidth= - L if bandwidth is limited to 32Kbs/sec, O if not. Always O if floodfill, otherwise L by default.
* --httpproxyport= - The port to listen on (HTTP Proxy)
* --socksproxyport= - The port to listen on (SOCKS Proxy)
* --proxykeys= - optional keys file for proxy's local destination
* --ircport= - The local port of IRC tunnel to listen on. 6668 by default
* --ircdest= - I2P destination address of IRC server. For example irc.postman.i2p
* --irckeys= - optional keys file for tunnel's local destination
* --eepkeys= - File name containing destination keys, for example privKeys.dat.
The file will be created if it does not already exist (issue #110).
* --eephost= - Address incoming trafic forward to. 127.0.0.1 by default
* --eepport= - Port incoming trafic forward to. 80 by default
* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified
* --bobport= - Port of BOB command channel. Usually 2827. BOB is off if not specified
* --i2pcontrolport= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
* --conf= - Config file (default: ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)
This parameter will be silently ignored if the specified config file does not exist.
Options specified on the command line take precedence over those in the config file.
Config files
------------
INI-like, syntax is the following : <key> = <value>.
All command-line parameters are allowed as keys, for example:
i2p.conf:
log = 1
v6 = 0
ircdest = irc.postman.i2p
tunnels.cfg (filename of this config is subject of change):
; outgoing tunnel, to remote service
[tunnel1]
type = client ; mandatory
port = <integer> ; mandatory, bind our side of tunnel to this local port
keys = <filename> ; optional
destination = <ident> ; mandatory
destinationport = <integer> ; optional, port of remote i2p service
; incoming tunnel, for local service(s)
[tunnel2]
type = server ; mandatory
host = <ident> ; mandatory, hostname of our i2p service
keys = <filename> ; mandatory, hostname keys
port = <integer> ; mandatory, forward incoming connections from i2p to this port
inport = <integer> ; optional, i2p service port
accesslist = <ident>[,<ident>] ; optional, comma-separated list of i2p idents, allowed to connect to service
Note: '<ident>' type is a string like <hostname.i2p> or <abracadabra.b32.i2p>

View File

@@ -1,52 +1,29 @@
#include <string.h>
#include <fstream>
#include <sstream>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <cryptopp/asn.h>
#include <cryptopp/base64.h>
#include <cryptopp/crc.h>
#include <cryptopp/hmac.h>
#include <cryptopp/zinflate.h>
#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>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Reseed.h"
#include "FS.h"
#include "Log.h"
#include "Identity.h"
#include "CryptoConst.h"
#include "NetDb.h"
#include "HTTP.h"
#include "util.h"
#include "Config.h"
namespace i2p
{
namespace data
{
static std::vector<std::string> httpReseedHostList = {
// "http://193.150.121.66/netDb/", // unstable
// "http://us.reseed.i2p2.no/", // misconfigured, not serving reseed data
// "http://jp.reseed.i2p2.no/", // Really outdated RIs
"http://netdb.i2p2.no/", // only SU3 (v2) support
"http://i2p.mooo.com/netDb/",
"http://uk.reseed.i2p2.no/",
"http://i2p-netdb.innovatio.no/"
};
//TODO: Remember to add custom port support. Not all serves on 443
static std::vector<std::string> httpsReseedHostList = {
// "https://193.150.121.66/netDb/", // unstable
// "https://i2p-netdb.innovatio.no/",// Vuln to POODLE
"https://netdb.i2p2.no/", // Only SU3 (v2) support
"https://reseed.i2p-projekt.de/", // Only HTTPS
"https://cowpuncher.drollette.com/netdb/", // Only HTTPS and SU3 (v2) support -- will move to a new location
// following hosts are fine but don't support AES256
/*"https://i2p.mooo.com/netDb/",
"https://link.mx24.eu/", // Only HTTPS and SU3 (v2) support
"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v2) support
"https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v2) support*/
};
Reseeder::Reseeder()
{
}
@@ -55,82 +32,29 @@ namespace data
{
}
bool Reseeder::reseedNow()
{
// This method is deprecated
try
{
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
LogPrint("Reseeding from ", reseedHost);
std::string content = i2p::util::http::httpRequest(reseedHost);
if (content == "")
{
LogPrint("Reseed failed");
return false;
}
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase);
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1);
boost::sregex_token_iterator j;
//TODO: Ugly code, try to clean up.
//TODO: Try to reduce N number of variables
std::string name;
std::string routerInfo;
std::string tmpUrl;
std::string filename;
std::string ignoreFileSuffix = ".su3";
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
while (i != j)
{
name = *i++;
if (name.find(ignoreFileSuffix)!=std::string::npos)
continue;
LogPrint("Downloading ", name);
tmpUrl = reseedHost;
tmpUrl.append(name);
routerInfo = i2p::util::http::httpRequest(tmpUrl);
if (routerInfo.size()==0)
continue;
filename = root.string();
#ifndef _WIN32
filename += "/netDb/r";
#else
filename += "\\netDb\\r";
#endif
filename += name.at(11); // first char in id
#ifndef _WIN32
filename.append("/");
#else
filename.append("\\");
#endif
filename.append(name.c_str());
std::ofstream outfile (filename, std::ios::binary);
outfile << routerInfo;
outfile.close();
}
return true;
}
catch (std::exception& ex)
{
//TODO: error reporting
return false;
}
return false;
}
int Reseeder::ReseedNowSU3 ()
{
CryptoPP::AutoSeededRandomPool rnd;
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1 + httpsReseedHostList.size () - 1);
std::string reseedHost = (ind < httpReseedHostList.size()) ? httpReseedHostList[ind] :
httpsReseedHostList[ind - httpReseedHostList.size()];
return ReseedFromSU3 (reseedHost, ind >= httpReseedHostList.size());
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);
}
int Reseeder::ReseedFromSU3 (const std::string& host, bool https)
int Reseeder::ReseedFromSU3 (const std::string& host)
{
std::string url = host + "i2pseeds.su3";
LogPrint (eLogInfo, "Dowloading SU3 from ", host);
std::string su3 = https ? HttpsRequest (url) : i2p::util::http::httpRequest (url);
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
std::string su3 = HttpsRequest (url);
if (su3.length () > 0)
{
std::stringstream s(su3);
@@ -138,7 +62,7 @@ namespace data
}
else
{
LogPrint (eLogWarning, "SU3 download failed");
LogPrint (eLogWarning, "Reseed: SU3 download failed");
return 0;
}
}
@@ -150,7 +74,7 @@ namespace data
return ProcessSU3Stream (s);
else
{
LogPrint (eLogError, "Can't open file ", filename);
LogPrint (eLogError, "Reseed: Can't open file ", filename);
return 0;
}
}
@@ -165,7 +89,7 @@ namespace data
s.read (magicNumber, 7); // magic number and zero byte 6
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
{
LogPrint (eLogError, "Unexpected SU3 magic number");
LogPrint (eLogError, "Reseed: Unexpected SU3 magic number");
return 0;
}
s.seekg (1, std::ios::cur); // su3 file format version
@@ -189,7 +113,7 @@ namespace data
s.read ((char *)&fileType, 1); // file type
if (fileType != 0x00) // zip file
{
LogPrint (eLogError, "Can't handle file type ", (int)fileType);
LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType);
return 0;
}
s.seekg (1, std::ios::cur); // unused
@@ -197,7 +121,7 @@ namespace data
s.read ((char *)&contentType, 1); // content type
if (contentType != 0x03) // reseed data
{
LogPrint (eLogError, "Unexpected content type ", (int)contentType);
LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType);
return 0;
}
s.seekg (12, std::ios::cur); // unused
@@ -207,35 +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
i2p::crypto::RSASHA5124096RawVerifier verifier(it->second);
verifier.Update (tbs, tbsLen);
if (!verifier.Verify (signature))
LogPrint (eLogWarning, "SU3 signature verification failed");
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
{
size_t pos = s.tellg ();
size_t tbsLen = pos + contentLength;
uint8_t * tbs = new uint8_t[tbsLen];
s.seekg (0, std::ios::beg);
s.read ((char *)tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength);
// RSA-raw
{
// calculate digest
uint8_t digest[64];
SHA512 (tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
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);
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
}
else
LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported");
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
}
else
LogPrint (eLogWarning, "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 ();
@@ -256,8 +209,9 @@ namespace data
compressionMethod = le16toh (compressionMethod);
s.seekg (4, std::ios::cur); // skip fields we don't care about
uint32_t compressedSize, uncompressedSize;
uint8_t crc32[4];
s.read ((char *)crc32, 4);
uint32_t crc_32;
s.read ((char *)&crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize);
s.read ((char *)&uncompressedSize, 4);
@@ -265,6 +219,11 @@ namespace data
uint16_t fileNameLength, extraFieldLength;
s.read ((char *)&fileNameLength, 2);
fileNameLength = le16toh (fileNameLength);
if ( fileNameLength > 255 ) {
// too big
LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength);
return numFiles;
}
s.read ((char *)&extraFieldLength, 2);
extraFieldLength = le16toh (extraFieldLength);
char localFileName[255];
@@ -277,11 +236,11 @@ namespace data
size_t pos = s.tellg ();
if (!FindZipDataDescriptor (s))
{
LogPrint (eLogError, "SU3 archive data descriptor not found");
LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found");
return numFiles;
}
s.read ((char *)crc32, 4);
}
s.read ((char *)&crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
s.read ((char *)&uncompressedSize, 4);
@@ -291,10 +250,10 @@ namespace data
s.seekg (pos, std::ios::beg); // back to compressed data
}
LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes");
LogPrint (eLogDebug, "Reseed: Proccessing file ", localFileName, " ", compressedSize, " bytes");
if (!compressedSize)
{
LogPrint (eLogWarning, "Unexpected size 0. Skipped");
LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped");
continue;
}
@@ -302,25 +261,31 @@ namespace data
s.read ((char *)compressed, compressedSize);
if (compressionMethod) // we assume Deflate
{
CryptoPP::Inflator decompressor;
decompressor.Put (compressed, compressedSize);
decompressor.MessageEnd();
if (decompressor.MaxRetrievable () <= uncompressedSize)
{
uint8_t * uncompressed = new uint8_t[uncompressedSize];
decompressor.Get (uncompressed, uncompressedSize);
if (CryptoPP::CRC32().VerifyDigest (crc32, uncompressed, uncompressedSize))
z_stream inflator;
memset (&inflator, 0, sizeof (inflator));
inflateInit2 (&inflator, -MAX_WBITS); // no zlib header
uint8_t * uncompressed = new uint8_t[uncompressedSize];
inflator.next_in = compressed;
inflator.avail_in = compressedSize;
inflator.next_out = uncompressed;
inflator.avail_out = uncompressedSize;
int err;
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0)
{
uncompressedSize -= inflator.avail_out;
if (crc32 (0, uncompressed, uncompressedSize) == crc_32)
{
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
numFiles++;
}
}
else
LogPrint (eLogError, "CRC32 verification failed");
delete[] uncompressed;
}
LogPrint (eLogError, "Reseed: CRC32 verification failed");
}
else
LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header");
}
LogPrint (eLogError, "Reseed: SU3 decompression error ", err);
delete[] uncompressed;
inflateEnd (&inflator);
}
else // no compression
{
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
@@ -333,7 +298,7 @@ namespace data
else
{
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
LogPrint (eLogWarning, "Missing zip central directory header");
LogPrint (eLogWarning, "Reseed: Missing zip central directory header");
break; // no more files
}
size_t end = s.tellg ();
@@ -363,462 +328,139 @@ namespace data
return false;
}
const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----";
const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----";
void Reseeder::LoadCertificate (const std::string& filename)
{
std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0, std::ios::end);
size_t len = s.tellg ();
s.seekg (0, std::ios::beg);
char buf[2048];
s.read (buf, len);
std::string cert (buf, len);
// assume file in pem format
auto pos1 = cert.find (CERTIFICATE_HEADER);
auto pos2 = cert.find (CERTIFICATE_FOOTER);
if (pos1 == std::string::npos || pos2 == std::string::npos)
{
LogPrint (eLogError, "Malformed certificate file");
return;
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
SSL * ssl = SSL_new (ctx);
X509 * cert = SSL_get_certificate (ssl);
// verify
if (cert)
{
// extract issuer name
char name[100];
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
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);
if (cn)
m_SigningKeys[cn] = value;
else
LogPrint (eLogError, "Reseed: Can't find CN field in ", filename);
}
pos1 += strlen (CERTIFICATE_HEADER);
pos2 -= pos1;
std::string base64 = cert.substr (pos1, pos2);
CryptoPP::ByteQueue queue;
CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P
decoder.Attach (new CryptoPP::Redirector (queue));
decoder.Put ((const uint8_t *)base64.data(), base64.length());
decoder.MessageEnd ();
LoadCertificate (queue);
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Can't open certificate file ", filename);
LogPrint (eLogError, "Reseed: Can't open certificate file ", filename);
SSL_CTX_free (ctx);
}
std::string Reseeder::LoadCertificate (CryptoPP::ByteQueue& queue)
{
// extract X.509
CryptoPP::BERSequenceDecoder x509Cert (queue);
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
// version
uint32_t ver;
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
// serial
CryptoPP::Integer serial;
serial.BERDecode(tbsCert);
// signature
CryptoPP::BERSequenceDecoder signature (tbsCert);
signature.SkipAll();
// issuer
std::string name;
CryptoPP::BERSequenceDecoder issuer (tbsCert);
{
CryptoPP::BERSetDecoder c (issuer); c.SkipAll();
CryptoPP::BERSetDecoder st (issuer); st.SkipAll();
CryptoPP::BERSetDecoder l (issuer); l.SkipAll();
CryptoPP::BERSetDecoder o (issuer); o.SkipAll();
CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll();
CryptoPP::BERSetDecoder cn (issuer);
{
CryptoPP::BERSequenceDecoder attributes (cn);
{
CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER);
ident.SkipAll ();
CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING);
}
}
}
issuer.SkipAll();
// validity
CryptoPP::BERSequenceDecoder validity (tbsCert);
validity.SkipAll();
// subject
CryptoPP::BERSequenceDecoder subject (tbsCert);
subject.SkipAll();
// public key
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
{
CryptoPP::BERSequenceDecoder ident (publicKey);
ident.SkipAll ();
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
key.Skip (1); // FIXME: probably bug in crypto++
CryptoPP::BERSequenceDecoder keyPair (key);
CryptoPP::Integer n;
n.BERDecode (keyPair);
if (name.length () > 0)
{
PublicKey value;
n.Encode (value, 512);
m_SigningKeys[name] = value;
}
else
LogPrint (eLogWarning, "Unknown issuer. Skipped");
}
publicKey.SkipAll();
tbsCert.SkipAll();
x509Cert.SkipAll();
return name;
}
void Reseeder::LoadCertificates ()
{
boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed";
if (!boost::filesystem::exists (reseedDir))
{
LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist");
std::string certDir = i2p::fs::DataDirPath("certificates", "reseed");
std::vector<std::string> files;
int numCertificates = 0;
if (!i2p::fs::ReadDir(certDir, files)) {
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
return;
}
int numCertificates = 0;
boost::filesystem::directory_iterator end; // empty
for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it)
{
if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt")
{
LoadCertificate (it->path ().string ());
numCertificates++;
}
for (const std::string & file : files) {
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
LogPrint(eLogWarning, "Reseed: ignoring file ", file);
continue;
}
LoadCertificate (file);
numCertificates++;
}
LogPrint (eLogInfo, numCertificates, " certificates loaded");
LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded");
}
std::string Reseeder::HttpsRequest (const std::string& address)
{
i2p::util::http::url u(address);
TlsSession session (u.host_, 443);
// 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";
session.Send ((uint8_t *)ss.str ().c_str (), ss.str ().length ());
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;
// read response
std::stringstream rs;
while (session.Receive (rs))
;
return i2p::util::http::GetHttpContent (rs);
}
TlsSession::TlsSession (const std::string& host, int port):
m_Seqn (0)
{
m_Site.connect(host, boost::lexical_cast<std::string>(port));
if (m_Site.good ())
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 (url.host, std::to_string(url.port)), ecode);
if (!ecode)
{
Handshake ();
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> s(service, ctx);
s.lowest_layer().connect (*it, ecode);
if (!ecode)
{
s.handshake (boost::asio::ssl::stream_base::client, ecode);
if (!ecode)
{
LogPrint (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 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
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 ", url.host, ": ", ecode.message ());
}
else
LogPrint (eLogError, "Can't connect to ", host, ":", port);
LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
return "";
}
void TlsSession::Handshake ()
{
static uint8_t clientHello[] =
{
0x16, // handshake
0x03, 0x03, // version (TLS 1.2)
0x00, 0x2F, // length of handshake
// handshake
0x01, // handshake type (client hello)
0x00, 0x00, 0x2B, // length of handshake payload
// client hello
0x03, 0x03, // highest version supported (TLS 1.2)
0x45, 0xFA, 0x01, 0x19, 0x74, 0x55, 0x18, 0x36,
0x42, 0x05, 0xC1, 0xDD, 0x4A, 0x21, 0x80, 0x80,
0xEC, 0x37, 0x11, 0x93, 0x16, 0xF4, 0x66, 0x00,
0x12, 0x67, 0xAB, 0xBA, 0xFF, 0x29, 0x13, 0x9E, // 32 random bytes
0x00, // session id length
0x00, 0x02, // chiper suites length
0x00, 0x3D, // RSA_WITH_AES_256_CBC_SHA256
0x01, // compression methods length
0x00, // no compression
0x00, 0x00 // extensions length
};
static uint8_t changeCipherSpecs[] =
{
0x14, // change cipher specs
0x03, 0x03, // version (TLS 1.2)
0x00, 0x01, // length
0x01 // type
};
static uint8_t finished[] =
{
0x16, // handshake
0x03, 0x03, // version (TLS 1.2)
0x00, 0x50, // length of handshake (80 bytes)
// handshake (encrypted)
// unencrypted context
// 0x14 handshake type (finished)
// 0x00, 0x00, 0x0C length of handshake payload
// 12 bytes of verified data
};
// send ClientHello
m_Site.write ((char *)clientHello, sizeof (clientHello));
m_FinishedHash.Update (clientHello + 5, sizeof (clientHello) - 5);
// read ServerHello
uint8_t type;
m_Site.read ((char *)&type, 1);
uint16_t version;
m_Site.read ((char *)&version, 2);
uint16_t length;
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * serverHello = new char[length];
m_Site.read (serverHello, length);
m_FinishedHash.Update ((uint8_t *)serverHello, length);
uint8_t serverRandom[32];
if (serverHello[0] == 0x02) // handshake type server hello
memcpy (serverRandom, serverHello + 6, 32);
else
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHello[0]);
delete[] serverHello;
// read Certificate
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * certificate = new char[length];
m_Site.read (certificate, length);
m_FinishedHash.Update ((uint8_t *)certificate, length);
CryptoPP::RSA::PublicKey publicKey;
// 0 - handshake type
// 1 - 3 - handshake payload length
// 4 - 6 - length of array of certificates
// 7 - 9 - length of certificate
if (certificate[0] == 0x0B) // handshake type certificate
publicKey = ExtractPublicKey ((uint8_t *)certificate + 10, length - 10);
else
LogPrint (eLogError, "Unexpected handshake type ", (int)certificate[0]);
delete[] certificate;
// read ServerHelloDone
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * serverHelloDone = new char[length];
m_Site.read (serverHelloDone, length);
m_FinishedHash.Update ((uint8_t *)serverHelloDone, length);
if (serverHelloDone[0] != 0x0E) // handshake type hello done
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHelloDone[0]);
delete[] serverHelloDone;
// our turn now
// generate secret key
uint8_t secret[48];
secret[0] = 3; secret[1] = 3; // version
m_Rnd.GenerateBlock (secret + 2, 46); // 46 random bytes
// encrypt RSA
CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(publicKey);
size_t encryptedLen = encryptor.CiphertextLength (48); // number of bytes for encrypted 48 bytes, usually 256 (2048 bits key)
uint8_t * encrypted = new uint8_t[encryptedLen + 2]; // + 2 bytes for length
htobe16buf (encrypted, encryptedLen); // first two bytes means length
encryptor.Encrypt (m_Rnd, secret, 48, encrypted + 2);
// send ClientKeyExchange
// 0x10 - handshake type "client key exchange"
SendHandshakeMsg (0x10, encrypted, encryptedLen + 2);
delete[] encrypted;
// send ChangeCipherSpecs
m_Site.write ((char *)changeCipherSpecs, sizeof (changeCipherSpecs));
// calculate master secret
uint8_t masterSecret[48], random[64];
memcpy (random, clientHello + 11, 32);
memcpy (random + 32, serverRandom, 32);
PRF (secret, "master secret", random, 64, 48, masterSecret);
// expand master secret
uint8_t keys[128]; // clientMACKey(32), serverMACKey(32), clientKey(32), serverKey(32)
memcpy (random, serverRandom, 32);
memcpy (random + 32, clientHello + 11, 32);
PRF (masterSecret, "key expansion", random, 64, 128, keys);
memcpy (m_MacKey, keys, 32);
m_Encryption.SetKey (keys + 64);
m_Decryption.SetKey (keys + 96);
// send finished
uint8_t finishedHashDigest[32], finishedPayload[40], encryptedPayload[80];
finishedPayload[0] = 0x14; // handshake type (finished)
finishedPayload[1] = 0; finishedPayload[2] = 0; finishedPayload[3] = 0x0C; // 12 bytes
m_FinishedHash.Final (finishedHashDigest);
PRF (masterSecret, "client finished", finishedHashDigest, 32, 12, finishedPayload + 4);
uint8_t mac[32];
CalculateMAC (0x16, finishedPayload, 16, mac);
Encrypt (finishedPayload, 16, mac, encryptedPayload);
m_Site.write ((char *)finished, sizeof (finished));
m_Site.write ((char *)encryptedPayload, 80);
// read ChangeCipherSpecs
uint8_t changeCipherSpecs1[6];
m_Site.read ((char *)changeCipherSpecs1, 6);
// read finished
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * finished1 = new char[length];
m_Site.read (finished1, length);
delete[] finished1;
}
void TlsSession::SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len)
{
uint8_t handshakeHeader[9];
handshakeHeader[0] = 0x16; // handshake
handshakeHeader[1] = 0x03; handshakeHeader[2] = 0x03; // version is always TLS 1.2 (3,3)
htobe16buf (handshakeHeader + 3, len + 4); // length of payload
//payload starts
handshakeHeader[5] = handshakeType; // handshake type
handshakeHeader[6] = 0; // highest byte of payload length is always zero
htobe16buf (handshakeHeader + 7, len); // length of data
m_Site.write ((char *)handshakeHeader, 9);
m_FinishedHash.Update (handshakeHeader + 5, 4); // only payload counts
m_Site.write ((char *)data, len);
m_FinishedHash.Update (data, len);
}
void TlsSession::PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
size_t len, uint8_t * buf)
{
// secret is assumed 48 bytes
// random is not more than 64 bytes
CryptoPP::HMAC<CryptoPP::SHA256> hmac (secret, 48);
uint8_t seed[96]; size_t seedLen;
seedLen = strlen (label);
memcpy (seed, label, seedLen);
memcpy (seed + seedLen, random, randomLen);
seedLen += randomLen;
size_t offset = 0;
uint8_t a[128];
hmac.CalculateDigest (a, seed, seedLen);
while (offset < len)
{
memcpy (a + 32, seed, seedLen);
hmac.CalculateDigest (buf + offset, a, seedLen + 32);
offset += 32;
hmac.CalculateDigest (a, a, 32);
}
}
size_t TlsSession::Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out)
{
size_t size = 0;
m_Rnd.GenerateBlock (out, 16); // iv
size += 16;
m_Encryption.SetIV (out);
memcpy (out + size, in, len);
size += len;
memcpy (out + size, mac, 32);
size += 32;
uint8_t paddingSize = len + 1;
paddingSize &= 0x0F; // %16
if (paddingSize > 0) paddingSize = 16 - paddingSize;
memset (out + size, paddingSize, paddingSize + 1); // paddind and last byte are equal to padding size
size += paddingSize + 1;
m_Encryption.Encrypt (out + 16, size - 16, out + 16);
return size;
}
size_t TlsSession::Decrypt (uint8_t * buf, size_t len)
{
m_Decryption.SetIV (buf);
m_Decryption.Decrypt (buf + 16, len - 16, buf + 16);
return len - 48 - buf[len -1] - 1; // IV(16), mac(32) and padding
}
void TlsSession::CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac)
{
uint8_t header[13]; // seqn (8) + type (1) + version (2) + length (2)
htobe64buf (header, m_Seqn);
header[8] = type; header[9] = 3; header[10] = 3; // 3,3 means TLS 1.2
htobe16buf (header + 11, len);
CryptoPP::HMAC<CryptoPP::SHA256> hmac (m_MacKey, 32);
hmac.Update (header, 13);
hmac.Update (buf, len);
hmac.Final (mac);
m_Seqn++;
}
CryptoPP::RSA::PublicKey TlsSession::ExtractPublicKey (const uint8_t * certificate, size_t len)
{
CryptoPP::ByteQueue queue;
queue.Put (certificate, len);
queue.MessageEnd ();
// extract X.509
CryptoPP::BERSequenceDecoder x509Cert (queue);
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
// version
uint32_t ver;
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
// serial
CryptoPP::Integer serial;
serial.BERDecode(tbsCert);
// signature
CryptoPP::BERSequenceDecoder signature (tbsCert);
signature.SkipAll();
// issuer
CryptoPP::BERSequenceDecoder issuer (tbsCert);
issuer.SkipAll();
// validity
CryptoPP::BERSequenceDecoder validity (tbsCert);
validity.SkipAll();
// subject
CryptoPP::BERSequenceDecoder subject (tbsCert);
subject.SkipAll();
// public key
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
CryptoPP::BERSequenceDecoder ident (publicKey);
ident.SkipAll ();
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
key.Skip (1); // FIXME: probably bug in crypto++
CryptoPP::BERSequenceDecoder keyPair (key);
CryptoPP::Integer n, e;
n.BERDecode (keyPair);
e.BERDecode (keyPair);
CryptoPP::RSA::PublicKey ret;
ret.Initialize (n, e);
return ret;
}
void TlsSession::Send (const uint8_t * buf, size_t len)
{
uint8_t * out = new uint8_t[len + 64 + 5]; // 64 = 32 mac + 16 iv + upto 16 padding, 5 = header
out[0] = 0x17; // application data
out[1] = 0x03; out[2] = 0x03; // version
uint8_t mac[32];
CalculateMAC (0x17, buf, len, mac);
size_t encryptedLen = Encrypt (buf, len, mac, out + 5);
htobe16buf (out + 3, encryptedLen);
m_Site.write ((char *)out, encryptedLen + 5);
delete[] out;
}
bool TlsSession::Receive (std::ostream& rs)
{
if (m_Site.eof ()) return false;
uint8_t type; uint16_t version, length;
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
uint8_t * buf = new uint8_t[length];
m_Site.read ((char *)buf, length);
size_t decryptedLen = Decrypt (buf, length);
rs.write ((char *)buf + 16, decryptedLen);
delete[] buf;
return true;
}
}
}

View File

@@ -5,11 +5,8 @@
#include <string>
#include <vector>
#include <map>
#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>
#include <boost/asio.hpp>
#include "Identity.h"
#include "aes.h"
#include "Crypto.h"
namespace i2p
{
@@ -24,7 +21,6 @@ namespace data
Reseeder();
~Reseeder();
bool reseedNow(); // depreacted
int ReseedNowSU3 ();
void LoadCertificates ();
@@ -32,9 +28,8 @@ namespace data
private:
void LoadCertificate (const std::string& filename);
std::string LoadCertificate (CryptoPP::ByteQueue& queue); // returns issuer's name
int ReseedFromSU3 (const std::string& host, bool https = false);
int ReseedFromSU3 (const std::string& host);
int ProcessSU3File (const char * filename);
int ProcessSU3Stream (std::istream& s);
@@ -46,36 +41,6 @@ namespace data
std::map<std::string, PublicKey> m_SigningKeys;
};
class TlsSession
{
public:
TlsSession (const std::string& host, int port);
void Send (const uint8_t * buf, size_t len);
bool Receive (std::ostream& rs);
private:
void Handshake ();
void SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len);
CryptoPP::RSA::PublicKey ExtractPublicKey (const uint8_t * certificate, size_t len);
void PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
size_t len, uint8_t * buf);
void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac);
size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out);
size_t Decrypt (uint8_t * buf, size_t len); // pyaload is buf + 16
private:
uint64_t m_Seqn;
boost::asio::ip::tcp::iostream m_Site;
CryptoPP::SHA256 m_FinishedHash;
CryptoPP::AutoSeededRandomPool m_Rnd;
i2p::crypto::CBCEncryption m_Encryption;
i2p::crypto::CBCDecryption m_Decryption;
uint8_t m_MacKey[32]; // client
};
}
}

View File

@@ -1,14 +1,15 @@
#include <fstream>
#include <cryptopp/dh.h>
#include <cryptopp/dsa.h>
#include <boost/lexical_cast.hpp>
#include "CryptoConst.h"
#include "RouterContext.h"
#include "Config.h"
#include "Crypto.h"
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "NetDb.h"
#include "FS.h"
#include "util.h"
#include "version.h"
#include "Log.h"
#include "Family.h"
#include "RouterContext.h"
namespace i2p
{
@@ -16,12 +17,14 @@ 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)
{
}
void RouterContext::Init ()
{
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
if (!Load ())
CreateNewRouter ();
@@ -30,7 +33,11 @@ namespace i2p
void RouterContext::CreateNewRouter ()
{
m_Keys = i2p::data::CreateRandomKeys ();
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
#else
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
#endif
SaveKeys ();
NewRouterInfo ();
}
@@ -39,36 +46,78 @@ namespace i2p
{
i2p::data::RouterInfo routerInfo;
routerInfo.SetRouterIdentity (GetIdentity ());
int port = i2p::util::config::GetArg("-port", 0);
uint16_t port; i2p::config::GetOption("port", port);
if (!port)
port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range
routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port);
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
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 ("coreVersion", I2P_VERSION);
routerInfo.SetProperty ("netId", "2");
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.SetProperty ("stat_uptime", "90m");
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
}
void RouterContext::UpdateRouterInfo ()
{
m_RouterInfo.CreateBuffer (m_Keys);
m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO));
m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO));
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
}
void RouterContext::SetStatus (RouterStatus status)
{
if (status != m_Status)
{
m_Status = status;
m_Error = eRouterErrorNone;
switch (m_Status)
{
case eRouterStatusOK:
SetReachable ();
break;
case eRouterStatusFirewalled:
SetUnreachable ();
break;
default:
;
}
}
}
void RouterContext::UpdatePort (int port)
{
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address.port != port)
if (address->port != port)
{
address.port = port;
address->port = port;
updated = true;
}
}
@@ -81,9 +130,9 @@ namespace i2p
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address.host != host && address.IsCompatible (host))
if (address->host != host && address->IsCompatible (host))
{
address.host = host;
address->host = host;
updated = true;
}
}
@@ -92,16 +141,11 @@ namespace i2p
UpdateRouterInfo ();
}
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag)
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
{
bool ret = false;
auto address = routerInfo.GetSSUAddress ();
if (address)
{
ret = m_RouterInfo.AddIntroducer (address, tag);
if (ret)
UpdateRouterInfo ();
}
bool ret = m_RouterInfo.AddIntroducer (introducer);
if (ret)
UpdateRouterInfo ();
return ret;
}
@@ -120,28 +164,74 @@ namespace i2p
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
// we don't publish number of routers and leaseset for non-floodfill
m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_LEASESETS);
m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_ROUTERS);
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS);
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS);
}
UpdateRouterInfo ();
}
void RouterContext::SetHighBandwidth ()
std::string RouterContext::GetFamily () const
{
if (!m_RouterInfo.IsHighBandwidth ())
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth);
UpdateRouterInfo ();
}
return m_RouterInfo.GetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
}
void RouterContext::SetLowBandwidth ()
void RouterContext::SetFamily (const std::string& family)
{
if (m_RouterInfo.IsHighBandwidth ())
std::string signature;
if (family.length () > 0)
signature = i2p::data::CreateFamilySignature (family, GetIdentHash ());
if (signature.length () > 0)
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth);
UpdateRouterInfo ();
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY, family);
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG, signature);
}
else
{
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG);
}
}
void RouterContext::SetBandwidth (char L) {
uint16_t limit = 0;
enum { low, high, extra } type = high;
/* detect parameters */
switch (L)
{
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break;
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = 48; type = low; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 64; type = high; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = extra; break;
default:
limit = 48; type = low;
}
/* update caps & flags in RI */
auto caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eHighBandwidth;
caps &= ~i2p::data::RouterInfo::eExtraBandwidth;
switch (type)
{
case low : /* not set */; break;
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break;
}
m_RouterInfo.SetCaps (caps);
UpdateRouterInfo ();
m_BandwidthLimit = limit;
}
void RouterContext::SetBandwidth (int limit)
{
if (limit > 2000) { SetBandwidth('X'); }
else if (limit > 256) { SetBandwidth('P'); }
else if (limit > 128) { SetBandwidth('O'); }
else if (limit > 64) { SetBandwidth('N'); }
else if (limit > 48) { SetBandwidth('M'); }
else if (limit > 12) { SetBandwidth('L'); }
else { SetBandwidth('K'); }
}
bool RouterContext::IsUnreachable () const
@@ -155,17 +245,18 @@ 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)
addr.introducers.clear ();
addr->introducers.clear ();
// update
UpdateRouterInfo ();
@@ -184,18 +275,19 @@ 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 form SSU
m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port);
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
// delete previous introducers
for (auto& addr : addresses)
addr.introducers.clear ();
addr->introducers.clear ();
// update
UpdateRouterInfo ();
@@ -208,26 +300,36 @@ namespace i2p
else
m_RouterInfo.DisableV6 ();
UpdateRouterInfo ();
}
}
void RouterContext::SetSupportsV4 (bool supportsV4)
{
if (supportsV4)
m_RouterInfo.EnableV4 ();
else
m_RouterInfo.DisableV4 ();
UpdateRouterInfo ();
}
void RouterContext::UpdateNTCPV6Address (const boost::asio::ip::address& host)
{
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)
if (addr->host.is_v6 () && addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
if (addr.host != host)
if (addr->host != host)
{
addr.host = host;
addr->host = host;
updated = true;
}
found = true;
}
else
port = addr.port;
port = addr->port;
}
if (!found)
{
@@ -236,8 +338,11 @@ namespace i2p
auto mtu = i2p::util::net::GetMTU (host);
if (mtu)
{
LogPrint ("Our v6 MTU=", mtu);
if (mtu > 1472) mtu = 1472;
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
if (mtu > 1472) { // TODO: magic constant
mtu = 1472;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
}
}
m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO
updated = true;
@@ -251,25 +356,51 @@ namespace i2p
if (m_IsFloodfill)
{
// update routers and leasesets
m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumLeaseSets ()));
m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ()));
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, std::to_string(i2p::data::netdb.GetNumLeaseSets ()));
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, std::to_string(i2p::data::netdb.GetNumRouters ()));
UpdateRouterInfo ();
}
}
bool RouterContext::Load ()
{
std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in);
std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary);
if (!fk.is_open ()) return false;
i2p::data::Keys keys;
fk.read ((char *)&keys, sizeof (keys));
m_Keys = keys;
fk.seekg (0, std::ios::end);
size_t len = fk.tellg();
fk.seekg (0, std::ios::beg);
i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
if (len == sizeof (i2p::data::Keys)) // old keys file format
{
i2p::data::Keys keys;
fk.read ((char *)&keys, sizeof (keys));
m_Keys = keys;
}
else // new keys file format
{
uint8_t * buf = new uint8_t[len];
fk.read ((char *)buf, len);
m_Keys.FromBuffer (buf, len);
delete[] buf;
}
m_RouterInfo.SetRouterIdentity (GetIdentity ());
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO));
if (!routerInfo.IsUnreachable ()) // router.info looks good
{
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
// Migration to 0.9.24. TODO: remove later
m_RouterInfo.DeleteProperty ("coreVersion");
m_RouterInfo.DeleteProperty ("stat_uptime");
}
else
{
LogPrint (eLogError, ROUTER_INFO, " is malformed. Creating new");
NewRouterInfo ();
}
if (IsUnreachable ())
SetReachable (); // we assume reachable until we discover firewall through peer tests
@@ -279,21 +410,43 @@ namespace i2p
void RouterContext::SaveKeys ()
{
std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out);
i2p::data::Keys keys;
memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey));
memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey));
auto& ident = GetIdentity ().GetStandardIdentity ();
memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey));
memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey));
fk.write ((char *)&keys, sizeof (keys));
// save in the same format as .dat files
std::ofstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ofstream::binary | std::ofstream::out);
size_t len = m_Keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
m_Keys.ToBuffer (buf, len);
fk.write ((char *)buf, len);
delete[] buf;
}
std::shared_ptr<i2p::tunnel::TunnelPool> RouterContext::GetTunnelPool () const
{
return i2p::tunnel::tunnels.GetExploratoryPool ();
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
}
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
void RouterContext::CleanupDestination ()
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::CleanupExpiredTags ();
}
uint32_t RouterContext::GetUptime () const
{
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;

View File

@@ -4,9 +4,8 @@
#include <inttypes.h>
#include <string>
#include <memory>
#include <mutex>
#include <boost/asio.hpp>
#include <cryptopp/dsa.h>
#include <cryptopp/osrng.h>
#include "Identity.h"
#include "RouterInfo.h"
#include "Garlic.h"
@@ -16,17 +15,21 @@ namespace i2p
const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys";
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
enum RouterStatus
{
eRouterStatusOK = 0,
eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2
eRouterStatusFirewalled = 2,
eRouterStatusError = 3
};
enum RouterError
{
eRouterErrorNone = 0,
eRouterErrorClockSkew = 1
};
class RouterContext: public i2p::garlic::GarlicDestination
{
public:
@@ -34,46 +37,69 @@ 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
{
return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo,
[](const i2p::data::RouterInfo *) {});
}
CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; };
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
{
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
[](i2p::garlic::GarlicDestination *) {});
}
uint32_t GetUptime () const;
uint32_t GetStartupTime () const { return m_StartupTime; };
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status) { m_Status = 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
bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag);
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const;
void SetUnreachable ();
void SetReachable ();
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
void SetHighBandwidth ();
void SetLowBandwidth ();
void SetFamily (const std::string& family);
std::string GetFamily () const;
void SetBandwidth (int limit); /* in kilobytes */
void SetBandwidth (char L); /* by letter */
bool AcceptsTunnels () const { return m_AcceptsTunnels; };
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
bool SupportsV4 () const { return m_RouterInfo.IsV4 (); };
void SetSupportsV6 (bool supportsV6);
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; };
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
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);
// override GarlicDestination
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
private:
@@ -87,11 +113,14 @@ namespace i2p
i2p::data::RouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys;
CryptoPP::AutoSeededRandomPool m_Rnd;
uint64_t m_LastUpdateTime;
bool m_AcceptsTunnels, m_IsFloodfill;
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;
};
extern RouterContext context;

View File

@@ -3,24 +3,33 @@
#include "I2PEndian.h"
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <cryptopp/sha.h>
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "base64.h"
#include <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 "RouterInfo.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 ();
}
@@ -28,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;
@@ -36,26 +46,42 @@ namespace data
RouterInfo::~RouterInfo ()
{
delete m_Buffer;
delete[] m_Buffer;
}
void RouterInfo::Update (const uint8_t * buf, int len)
{
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
m_IsUpdated = true;
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
m_Properties.clear ();
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer (true);
// don't delete buffer until save to file
// verify signature since we have indentity already
int l = len - m_RouterIdentity->GetSignatureLen ();
if (m_RouterIdentity->Verify (buf, l, buf + l))
{
// clean up
m_IsUpdated = true;
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
// don't clean up m_Addresses, it will be replaced in ReadFromStream
m_Properties.clear ();
// copy buffer
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
// skip identity
size_t identityLen = m_RouterIdentity->GetFullLen ();
// read new RI
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
// don't delete buffer until saved to the file
}
else
{
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
}
}
void RouterInfo::SetRouterIdentity (const IdentityEx& identity)
void RouterInfo::SetRouterIdentity (std::shared_ptr<const IdentityEx> identity)
{
m_RouterIdentity = identity;
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
@@ -63,14 +89,14 @@ namespace data
bool RouterInfo::LoadFile ()
{
std::ifstream s(m_FullPath.c_str (), std::ifstream::binary);
std::ifstream s(m_FullPath, std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0,std::ios::end);
m_BufferLen = s.tellg ();
if (m_BufferLen < 40)
if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE)
{
LogPrint(eLogError, "File", m_FullPath, " is malformed");
LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed");
return false;
}
s.seekg(0, std::ios::beg);
@@ -80,7 +106,7 @@ namespace data
}
else
{
LogPrint (eLogError, "Can't open file ", m_FullPath);
LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath);
return false;
}
return true;
@@ -90,24 +116,42 @@ namespace data
{
if (LoadFile ())
ReadFromBuffer (false);
else
m_IsUnreachable = true;
}
void RouterInfo::ReadFromBuffer (bool verifySignature)
{
size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen);
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
m_RouterIdentity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t identityLen = m_RouterIdentity->GetFullLen ();
if (identityLen >= m_BufferLen)
{
LogPrint (eLogError, "RouterInfo: identity length ", identityLen, " exceeds buffer size ", m_BufferLen);
m_IsUnreachable = true;
return;
}
if (verifySignature)
{
// verify signature
int l = m_BufferLen - m_RouterIdentity.GetSignatureLen ();
if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
{
LogPrint (eLogError, "signature verification failed");
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
return;
}
m_RouterIdentity.DropVerifier ();
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)
@@ -115,17 +159,19 @@ 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));
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
for (int i = 0; i < numAddresses; i++)
{
uint8_t supportedTransports = 0;
bool isValidAddress = true;
Address address;
s.read ((char *)&address.cost, sizeof (address.cost));
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"))
@@ -135,15 +181,16 @@ namespace data
address.port = 0;
address.mtu = 0;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size));
s.read ((char *)&size, sizeof (size)); if (!s) return;
size = be16toh (size);
while (r < size)
{
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;
@@ -152,23 +199,22 @@ namespace data
{
if (address.transportStyle == eTransportNTCP)
{
m_SupportedTransports |= eNTCPV4; // TODO:
supportedTransports |= eNTCPV4; // TODO:
address.addressString = value;
}
else
{
// TODO: resolve address for SSU
LogPrint (eLogWarning, "Unexpected SSU address ", value);
isValidAddress = false;
supportedTransports |= eSSUV4; // TODO:
address.addressString = value;
}
}
else
{
// add supported protocol
if (address.host.is_v4 ())
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
}
}
else if (!strcmp (key, "port"))
@@ -186,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);
@@ -201,41 +252,71 @@ namespace data
else if (!strcmp (key, "ikey"))
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
}
if (!s) return;
}
if (isValidAddress)
m_Addresses.push_back(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));
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
// read properties
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size));
s.read ((char *)&size, sizeof (size)); if (!s) return;
size = be16toh (size);
while (r < size)
{
#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) != i2p::context.GetNetID ())
{
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
m_IsUnreachable = true;
}
// family
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
{
m_Family = value;
boost::to_lower (m_Family);
}
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
{
if (!netdb.GetFamilies ().VerifyFamily (m_Family, GetIdentHash (), value))
{
LogPrint (eLogWarning, "RouterInfo: family signature verification failed");
m_Family.clear ();
}
}
if (!s) return;
}
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)
{
@@ -252,6 +333,10 @@ namespace data
case CAPS_FLAG_HIGH_BANDWIDTH3:
m_Caps |= Caps::eHighBandwidth;
break;
case CAPS_FLAG_EXTRA_BANDWIDTH1:
case CAPS_FLAG_EXTRA_BANDWIDTH2:
m_Caps |= Caps::eExtraBandwidth;
break;
case CAPS_FLAG_HIDDEN:
m_Caps |= Caps::eHidden;
break;
@@ -278,30 +363,35 @@ namespace data
std::string caps;
if (m_Caps & eFloodfill)
{
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // highest bandwidth
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
caps += CAPS_FLAG_FLOODFILL; // floodfill
}
else
{
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
}
else
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
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& address : m_Addresses)
for (const auto& addr_ptr : *m_Addresses)
{
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);
@@ -330,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 << '=';
@@ -339,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 << '=';
@@ -351,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 << '=';
@@ -360,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 << '=';
@@ -402,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 << '=';
@@ -414,12 +504,20 @@ namespace data
s.write (properties.str ().c_str (), properties.str ().size ());
}
bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const
{
if (!m_RouterIdentity) return false;
size_t size = m_RouterIdentity->GetFullLen ();
if (size + 8 > len) return false;
return bufbe64toh (buf + size) > m_Timestamp;
}
const uint8_t * RouterInfo::LoadBuffer ()
{
if (!m_Buffer)
{
if (LoadFile ())
LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file");
LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
}
return m_Buffer;
}
@@ -429,7 +527,7 @@ namespace data
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
std::stringstream s;
uint8_t ident[1024];
auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024);
auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024);
s.write ((char *)ident, identLen);
WriteToStream (s);
m_BufferLen = s.str ().size ();
@@ -438,31 +536,45 @@ namespace data
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
// signature
privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
m_BufferLen += privateKeys.GetPublic ().GetSignatureLen ();
m_BufferLen += privateKeys.GetPublic ()->GetSignatureLen ();
}
void RouterInfo::SaveToFile (const std::string& fullPath)
bool RouterInfo::SaveToFile (const std::string& fullPath)
{
m_FullPath = fullPath;
if (m_Buffer)
{
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
f.write ((char *)m_Buffer, m_BufferLen);
}
else
LogPrint (eLogError, "Can't save to file");
if (!m_Buffer) {
LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL");
return false;
}
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
if (!f.is_open ()) {
LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath);
return false;
}
f.write ((char *)m_Buffer, m_BufferLen);
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);
@@ -471,47 +583,47 @@ namespace data
void RouterInfo::AddNTCPAddress (const char * host, int port)
{
Address addr;
addr.host = boost::asio::ip::address::from_string (host);
addr.port = port;
addr.transportStyle = eTransportNTCP;
addr.cost = 2;
addr.date = 0;
addr.mtu = 0;
m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4;
auto addr = std::make_shared<Address>();
addr->host = boost::asio::ip::address::from_string (host);
addr->port = port;
addr->transportStyle = eTransportNTCP;
addr->cost = 2;
addr->date = 0;
addr->mtu = 0;
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4;
m_Addresses->push_back(std::move(addr));
}
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
{
Address addr;
addr.host = boost::asio::ip::address::from_string (host);
addr.port = port;
addr.transportStyle = eTransportSSU;
addr.cost = 10; // NTCP should have priority over SSU
addr.date = 0;
addr.mtu = mtu;
memcpy (addr.key, key, 32);
m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4;
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
auto addr = std::make_shared<Address>();
addr->host = boost::asio::ip::address::from_string (host);
addr->port = port;
addr->transportStyle = eTransportSSU;
addr->cost = 10; // NTCP should have priority over SSU
addr->date = 0;
addr->mtu = mtu;
memcpy (addr->key, key, 32);
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Addresses->push_back(std::move(addr));
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
}
bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag)
bool RouterInfo::AddIntroducer (const Introducer& introducer)
{
for (auto& addr : m_Addresses)
for (auto& addr : *m_Addresses)
{
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{
for (auto intro: addr.introducers)
if (intro.iTag == tag) return false; // already presented
Introducer x;
x.iHost = address->host;
x.iPort = address->port;
x.iTag = tag;
memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32>
addr.introducers.push_back (x);
for (auto& intro: addr->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
addr->introducers.push_back (introducer);
return true;
}
}
@@ -520,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 ())
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);
addr->introducers.erase (it);
return true;
}
}
}
}
return false;
@@ -558,11 +670,14 @@ namespace data
m_Properties.erase (key);
}
bool RouterInfo::IsFloodfill () const
std::string RouterInfo::GetProperty (const std::string& key) const
{
return m_Caps & Caps::eFloodfill;
auto it = m_Properties.find (key);
if (it != m_Properties.end ())
return it->second;
return "";
}
bool RouterInfo::IsNTCP (bool v4only) const
{
if (v4only)
@@ -584,70 +699,90 @@ namespace data
return m_SupportedTransports & (eNTCPV6 | eSSUV6);
}
bool RouterInfo::IsV4 () const
{
return m_SupportedTransports & (eNTCPV4 | eSSUV4);
}
void RouterInfo::EnableV6 ()
{
if (!IsV6 ())
m_SupportedTransports |= eNTCPV6 | eSSUV6;
}
void RouterInfo::EnableV4 ()
{
if (!IsV4 ())
m_SupportedTransports |= eNTCPV4 | eSSUV4;
}
void RouterInfo::DisableV6 ()
{
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;
}
}
}
void RouterInfo::DisableV4 ()
{
if (IsV4 ())
{
m_SupportedTransports &= ~(eNTCPV4 | eSSUV4);
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{
auto addr = *it;
if (addr->host.is_v4 ())
it = m_Addresses->erase (it);
else
++it;
}
}
}
bool RouterInfo::UsesIntroducer () const
{
return m_Caps & Caps::eUnreachable; // non-reachable
}
const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCPAddress (bool v4only) const
{
return GetAddress (eTransportNTCP, v4only);
}
const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
{
return GetAddress (eTransportSSU, v4only);
}
const RouterInfo::Address * RouterInfo::GetSSUV6Address () const
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUV6Address () const
{
return GetAddress (eTransportSSU, false, true);
}
const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
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)
if (address->transportStyle == s)
{
if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ()))
return &address;
if ((!v4only || address->host.is_v4 ()) && (!v6only || address->host.is_v6 ()))
return address;
}
}
return nullptr;

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"
@@ -14,16 +16,25 @@ namespace i2p
{
namespace data
{
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
const char ROUTER_INFO_PROPERTY_NETID[] = "netId";
const char ROUTER_INFO_PROPERTY_FAMILY[] = "family";
const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig";
const char CAPS_FLAG_FLOODFILL = 'f';
const char CAPS_FLAG_HIDDEN = 'H';
const char CAPS_FLAG_REACHABLE = 'R';
const char CAPS_FLAG_UNREACHABLE = 'U';
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K';
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L';
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M';
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N';
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O';
/* bandwidth flags */
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; /* 48-64 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; /* 64-128 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; /* 128-256 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
const char CAPS_FLAG_SSU_TESTING = 'B';
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
@@ -44,11 +55,12 @@ namespace data
{
eFloodfill = 0x01,
eHighBandwidth = 0x02,
eReachable = 0x04,
eSSUTesting = 0x08,
eSSUIntroducer = 0x10,
eHidden = 0x20,
eUnreachable = 0x40
eExtraBandwidth = 0x04,
eReachable = 0x08,
eSSUTesting = 0x10,
eSSUIntroducer = 0x20,
eHidden = 0x40,
eUnreachable = 0x80
};
enum TransportStyle
@@ -58,11 +70,12 @@ namespace data
eTransportSSU
};
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer
{
boost::asio::ip::address iHost;
int iPort;
Tag<32> iKey;
IntroKey iKey;
uint32_t iTag;
};
@@ -75,7 +88,7 @@ namespace data
uint64_t date;
uint8_t cost;
// SSU only
Tag<32> key; // intro key for SSU
IntroKey key; // intro key for SSU
std::vector<Introducer> introducers;
bool IsCompatible (const boost::asio::ip::address& other) const
@@ -83,45 +96,61 @@ namespace data
return (host.is_v4 () && other.is_v4 ()) ||
(host.is_v6 () && other.is_v6 ());
}
bool operator==(const Address& other) const
{
return transportStyle == other.transportStyle && host == other.host && port == other.port;
}
bool operator!=(const Address& other) const
{
return !(*this == other);
}
};
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);
~RouterInfo ();
const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; };
void SetRouterIdentity (const IdentityEx& identity);
std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; };
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); };
uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<Address>& GetAddresses () { return m_Addresses; };
const Address * GetNTCPAddress (bool v4only = true) const;
const Address * GetSSUAddress (bool v4only = true) const;
const Address * GetSSUV6Address () const;
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;
void AddNTCPAddress (const char * host, int port);
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
bool AddIntroducer (const Address * address, uint32_t tag);
bool AddIntroducer (const Introducer& introducer);
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
void DeleteProperty (const std::string& key); // called from RouterContext only
std::string GetProperty (const std::string& key) const; // called from RouterContext only
void ClearProperties () { m_Properties.clear (); };
bool IsFloodfill () const;
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsNTCP (bool v4only = true) const;
bool IsSSU (bool v4only = true) const;
bool IsV6 () const;
bool IsV4 () const;
void EnableV6 ();
void DisableV6 ();
void EnableV4 ();
void DisableV4 ();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool UsesIntroducer () const;
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps);
void SetCaps (const char * caps);
@@ -136,41 +165,44 @@ namespace data
bool IsUpdated () const { return m_IsUpdated; };
void SetUpdated (bool updated) { m_IsUpdated = updated; };
void SaveToFile (const std::string& fullPath);
bool SaveToFile (const std::string& fullPath);
std::shared_ptr<RouterProfile> GetProfile () const;
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
void Update (const uint8_t * buf, int len);
void DeleteBuffer () { delete m_Buffer; m_Buffer = nullptr; };
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; };
const IdentHash& GetIdentHash () const { return m_RouterIdentity->GetIdentHash (); };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity->GetStandardIdentity ().publicKey; };
bool IsDestination () const { return false; };
private:
bool LoadFile ();
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);
const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
std::shared_ptr<const Address> GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
void UpdateCapsProperty ();
private:
std::string m_FullPath;
IdentityEx m_RouterIdentity;
std::string m_FullPath, m_Family;
std::shared_ptr<const IdentityEx> m_RouterIdentity;
uint8_t * m_Buffer;
int m_BufferLen;
size_t m_BufferLen;
uint64_t m_Timestamp;
std::vector<Address> m_Addresses;
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;

269
SAM.cpp
View File

@@ -3,12 +3,12 @@
#ifdef _MSC_VER
#include <stdlib.h>
#endif
#include <boost/lexical_cast.hpp>
#include "base64.h"
#include "Base.h"
#include "Identity.h"
#include "Log.h"
#include "Destination.h"
#include "ClientContext.h"
#include "util.h"
#include "SAM.h"
namespace i2p
@@ -47,16 +47,17 @@ namespace client
break;
case eSAMSocketTypeStream:
{
if (m_Session)
m_Session->sockets.remove (shared_from_this ());
if (m_Session)
m_Session->DelSocket (shared_from_this ());
break;
}
case eSAMSocketTypeAcceptor:
{
if (m_Session)
{
m_Session->sockets.remove (shared_from_this ());
m_Session->localDestination->StopAcceptingStreams ();
{
m_Session->DelSocket (shared_from_this ());
if (m_Session->localDestination)
m_Session->localDestination->StopAcceptingStreams ();
}
break;
}
@@ -64,7 +65,8 @@ namespace client
;
}
m_SocketType = eSAMSocketTypeTerminated;
m_Socket.close ();
if (m_Socket.is_open()) m_Socket.close ();
m_Session = nullptr;
}
void SAMSocket::ReceiveHandshake ()
@@ -78,14 +80,17 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM handshake read error: ", ecode.message ());
LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_Buffer[bytes_transferred] = 0;
LogPrint ("SAM handshake ", m_Buffer);
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
*eol = 0;
LogPrint (eLogDebug, "SAM: handshake ", m_Buffer);
char * separator = strchr (m_Buffer, ' ');
if (separator)
{
@@ -124,7 +129,7 @@ namespace client
}
else
{
LogPrint ("SAM handshake mismatch");
LogPrint (eLogError, "SAM: handshake mismatch");
Terminate ();
}
}
@@ -134,7 +139,7 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM handshake reply send error: ", ecode.message ());
LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -165,7 +170,7 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM reply send error: ", ecode.message ());
LogPrint (eLogError, "SAM: reply send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -182,10 +187,12 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM read error: ", ecode.message ());
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else if (m_SocketType == eSAMSocketTypeStream)
HandleReceived (ecode, bytes_transferred);
else
{
bytes_transferred += m_BufferOffset;
@@ -235,19 +242,19 @@ namespace client
}
else
{
LogPrint (eLogError, "SAM unexpected message ", m_Buffer);
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
Terminate ();
}
}
else
{
LogPrint (eLogError, "SAM malformed message ", m_Buffer);
LogPrint (eLogError, "SAM: malformed message ", m_Buffer);
Terminate ();
}
}
else
{
LogPrint (eLogWarning, "SAM incomplete message ", bytes_transferred);
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
m_BufferOffset = bytes_transferred;
// try to receive remaining message
Receive ();
@@ -257,7 +264,7 @@ namespace client
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
{
LogPrint ("SAM session create: ", buf);
LogPrint (eLogDebug, "SAM: session create: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& style = params[SAM_PARAM_STYLE];
@@ -328,7 +335,7 @@ namespace client
void SAMSocket::ProcessStreamConnect (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM stream connect: ", buf);
LogPrint (eLogDebug, "SAM: stream connect: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
@@ -339,50 +346,52 @@ namespace client
m_Session = m_Owner.FindSession (id);
if (m_Session)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
context.GetAddressBook ().InsertAddress (dest);
auto leaseSet = m_Session->localDestination->FindLeaseSet (dest.GetIdentHash ());
if (leaseSet)
Connect (leaseSet);
else
auto dest = std::make_shared<i2p::data::IdentityEx> ();
size_t len = dest->FromBase64(destination);
if (len > 0)
{
m_Session->localDestination->RequestDestination (dest.GetIdentHash (),
std::bind (&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, dest.GetIdentHash ()));
context.GetAddressBook().InsertAddress(dest);
auto leaseSet = m_Session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet)
Connect(leaseSet);
else
{
m_Session->localDestination->RequestDestination(dest->GetIdentHash(),
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1));
}
}
else
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
}
else
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
{
m_SocketType = eSAMSocketTypeStream;
m_Session->sockets.push_back (shared_from_this ());
m_Session->AddSocket (shared_from_this ());
m_Stream = m_Session->localDestination->CreateStream (remote);
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
I2PReceive ();
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
void SAMSocket::HandleConnectLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
std::shared_ptr<const i2p::data::LeaseSet> leaseSet;
if (success)
leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
Connect (leaseSet);
else
{
LogPrint ("SAM destination to connect not found");
LogPrint (eLogError, "SAM: destination to connect not found");
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
}
}
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM stream accept: ", buf);
LogPrint (eLogDebug, "SAM: stream accept: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
@@ -395,7 +404,7 @@ namespace client
if (!m_Session->localDestination->IsAcceptingStreams ())
{
m_SocketType = eSAMSocketTypeAcceptor;
m_Session->sockets.push_back (shared_from_this ());
m_Session->AddSocket (shared_from_this ());
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
@@ -408,10 +417,10 @@ namespace client
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
{
LogPrint (eLogDebug, "SAM datagram send: ", buf, " ", len);
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
size_t size = boost::lexical_cast<int>(params[SAM_PARAM_SIZE]), offset = data - buf;
size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf;
if (offset + size <= len)
{
if (m_Session)
@@ -424,14 +433,14 @@ namespace client
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
}
else
LogPrint (eLogError, "SAM missing datagram destination");
LogPrint (eLogError, "SAM: missing datagram destination");
}
else
LogPrint (eLogError, "SAM session is not created from DATAGRAM SEND");
LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND");
}
else
{
LogPrint (eLogWarning, "SAM sent datagram size ", size, " exceeds buffer ", len - offset);
LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset);
return 0; // try to receive more
}
return offset + size;
@@ -439,29 +448,29 @@ namespace client
void SAMSocket::ProcessDestGenerate ()
{
LogPrint (eLogDebug, "SAM dest generate");
LogPrint (eLogDebug, "SAM: dest generate");
auto keys = i2p::data::PrivateKeys::CreateRandomKeys ();
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#endif
SendMessageReply (m_Buffer, len, false);
}
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM naming lookup: ", buf);
LogPrint (eLogDebug, "SAM: naming lookup: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& name = params[SAM_PARAM_NAME];
i2p::data::IdentityEx identity;
std::shared_ptr<const i2p::data::IdentityEx> identity;
i2p::data::IdentHash ident;
if (name == "ME")
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
else if (context.GetAddressBook ().GetAddress (name, identity))
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
SendNamingLookupReply (identity);
else if (m_Session && m_Session->localDestination &&
context.GetAddressBook ().GetIdentHash (name, ident))
@@ -476,7 +485,7 @@ namespace client
}
else
{
LogPrint ("SAM naming failed. Unknown address ", name);
LogPrint (eLogError, "SAM: naming failed, unknown address ", name);
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#else
@@ -486,11 +495,8 @@ namespace client
}
}
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
{
std::shared_ptr<const i2p::data::LeaseSet> leaseSet;
if (success)
leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
{
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
@@ -498,7 +504,7 @@ namespace client
}
else
{
LogPrint (eLogInfo, "SAM naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
@@ -510,9 +516,9 @@ namespace client
}
}
void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity)
void SAMSocket::SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity)
{
auto base64 = identity.ToBase64 ();
auto base64 = identity->ToBase64 ();
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#else
@@ -544,7 +550,7 @@ namespace client
{
if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE)
{
LogPrint (eLogError, "Buffer is full. Terminate");
LogPrint (eLogError, "SAM: Buffer is full, terminate");
Terminate ();
return;
}
@@ -557,33 +563,68 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM read error: ", ecode.message ());
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (m_Stream)
m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred);
Receive ();
{
auto s = shared_from_this ();
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
[s](const boost::system::error_code& ecode)
{
if (!ecode)
s->Receive ();
else
s->Terminate ();
});
}
}
}
void SAMSocket::I2PReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
SAM_SOCKET_CONNECTION_MAX_IDLE);
{
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
{
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
SAM_SOCKET_CONNECTION_MAX_IDLE);
}
else // closed by peer
{
// get remaning data
auto len = m_Stream->ReadSome (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
if (len > 0) // still some data
{
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
}
else // no more data
Terminate ();
}
}
}
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint ("SAM stream read error: ", ecode.message ());
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
{
if (bytes_transferred > 0)
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); // postpone termination
else
Terminate ();
}
else
Terminate ();
}
else
@@ -597,7 +638,7 @@ namespace client
{
if (ecode)
{
LogPrint ("SAM socket write error: ", ecode.message ());
LogPrint (eLogError, "SAM: socket write error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -609,7 +650,7 @@ namespace client
{
if (stream)
{
LogPrint ("SAM incoming I2P connection for session ", m_ID);
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
m_Stream = stream;
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
auto session = m_Owner.FindSession (m_ID);
@@ -618,10 +659,15 @@ namespace client
m_SocketType = eSAMSocketTypeStream;
if (!m_IsSilent)
{
// send remote peer address
uint8_t ident[1024];
size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
// get remote peer address
auto ident_ptr = stream->GetRemoteIdentity();
const size_t ident_len = ident_ptr->GetFullLen();
uint8_t* ident = new uint8_t[ident_len];
// send remote peer address as base64
const size_t l = ident_ptr->ToBuffer (ident, ident_len);
const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
delete[] ident;
m_StreamBuffer[l1] = '\n';
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
}
@@ -629,12 +675,12 @@ namespace client
I2PReceive ();
}
else
LogPrint (eLogInfo, "SAM I2P acceptor has been reset");
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
}
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SAM datagram received ", len);
LogPrint (eLogDebug, "SAM: datagram received ", len);
auto base64 = from.ToBase64 ();
#ifdef _MSC_VER
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
@@ -648,7 +694,7 @@ namespace client
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
}
else
LogPrint (eLogWarning, "SAM received datagram size ", len," exceeds buffer");
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
}
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
@@ -658,25 +704,26 @@ namespace client
SAMSession::~SAMSession ()
{
for (auto it: sockets)
it->SetSocketType (eSAMSocketTypeTerminated);
CloseStreams();
i2p::client::context.DeleteLocalDestination (localDestination);
}
void SAMSession::CloseStreams ()
{
for (auto it: sockets)
{
it->CloseStream ();
it->SetSocketType (eSAMSocketTypeTerminated);
}
sockets.clear ();
{
std::lock_guard<std::mutex> lock(m_SocketsMutex);
for (auto& sock : m_Sockets) {
sock->CloseStream();
}
}
// XXX: should this be done inside locked parts?
m_Sockets.clear();
}
SAMBridge::SAMBridge (int port):
SAMBridge::SAMBridge (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
{
}
@@ -698,8 +745,8 @@ namespace client
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto it: m_Sessions)
delete it.second;
for (auto& it: m_Sessions)
it.second->CloseStreams ();
m_Sessions.clear ();
m_Service.stop ();
if (m_Thread)
@@ -720,7 +767,7 @@ namespace client
}
catch (std::exception& ex)
{
LogPrint ("SAM: ", ex.what ());
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
}
}
@@ -740,20 +787,20 @@ namespace client
auto ep = socket->GetSocket ().remote_endpoint (ec);
if (!ec)
{
LogPrint ("New SAM connection from ", ep);
LogPrint (eLogDebug, "SAM: new connection from ", ep);
socket->ReceiveHandshake ();
}
else
LogPrint (eLogError, "SAM connection from error ", ec.message ());
LogPrint (eLogError, "SAM: incoming connection error ", ec.message ());
}
else
LogPrint ("SAM accept error: ", ecode.message ());
LogPrint (eLogError, "SAM: accept error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Accept ();
}
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination,
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, const std::string& destination,
const std::map<std::string, std::string> * params)
{
std::shared_ptr<ClientDestination> localDestination = nullptr;
@@ -772,16 +819,17 @@ namespace client
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params->end ())
// TODO: extract string values
signatureType = boost::lexical_cast<int> (it->second);
signatureType = std::stoi(it->second);
}
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
}
if (localDestination)
{
auto session = std::make_shared<SAMSession>(localDestination);
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession *>(id, new SAMSession (localDestination)));
auto ret = m_Sessions.insert (std::make_pair(id, session));
if (!ret.second)
LogPrint ("Session ", id, " already exists");
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
return ret.first->second;
}
return nullptr;
@@ -789,19 +837,24 @@ namespace client
void SAMBridge::CloseSession (const std::string& id)
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
std::shared_ptr<SAMSession> session;
{
auto session = it->second;
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
{
session = it->second;
m_Sessions.erase (it);
}
}
if (session)
{
session->localDestination->StopAcceptingStreams ();
session->CloseStreams ();
m_Sessions.erase (it);
delete session;
}
}
SAMSession * SAMBridge::FindSession (const std::string& id) const
std::shared_ptr<SAMSession> SAMBridge::FindSession (const std::string& id) const
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
@@ -826,7 +879,7 @@ namespace client
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
*eol = 0; eol++;
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
if (sessionID)
{
@@ -844,17 +897,17 @@ namespace client
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
}
else
LogPrint ("Session ", sessionID, " not found");
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
}
else
LogPrint ("Missing destination key");
LogPrint (eLogError, "SAM: Missing destination key");
}
else
LogPrint ("Missing sessionID");
LogPrint (eLogError, "SAM: Missing sessionID");
ReceiveDatagram ();
}
else
LogPrint ("SAM datagram receive error: ", ecode.message ());
LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ());
}
}
}

44
SAM.h
View File

@@ -28,6 +28,7 @@ namespace client
const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n";
const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n";
const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n";
const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n";
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
@@ -40,7 +41,7 @@ namespace client
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n";
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n";
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n";
const char SAM_PARAM_MIN[] = "MIN";
@@ -109,9 +110,9 @@ namespace client
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleConnectLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
void SendNamingLookupReply (const i2p::data::IdentityEx& identity);
void HandleNamingLookupLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk ();
@@ -127,13 +128,36 @@ namespace client
std::string m_ID; // nickname
bool m_IsSilent;
std::shared_ptr<i2p::stream::Stream> m_Stream;
SAMSession * m_Session;
std::shared_ptr<SAMSession> m_Session;
};
struct SAMSession
{
std::shared_ptr<ClientDestination> localDestination;
std::list<std::shared_ptr<SAMSocket> > sockets;
std::list<std::shared_ptr<SAMSocket> > m_Sockets;
std::mutex m_SocketsMutex;
/** safely add a socket to this session */
void AddSocket(std::shared_ptr<SAMSocket> sock) {
std::lock_guard<std::mutex> lock(m_SocketsMutex);
m_Sockets.push_back(sock);
}
/** safely remove a socket from this session */
void DelSocket(std::shared_ptr<SAMSocket> sock) {
std::lock_guard<std::mutex> lock(m_SocketsMutex);
m_Sockets.remove(sock);
}
/** get a list holding a copy of all sam sockets from this session */
std::list<std::shared_ptr<SAMSocket> > ListSockets() {
std::list<std::shared_ptr<SAMSocket> > l;
{
std::lock_guard<std::mutex> lock(m_SocketsMutex);
for(const auto& sock : m_Sockets ) l.push_back(sock);
}
return l;
}
SAMSession (std::shared_ptr<ClientDestination> dest);
~SAMSession ();
@@ -145,17 +169,17 @@ namespace client
{
public:
SAMBridge (int port);
SAMBridge (const std::string& address, int port);
~SAMBridge ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient
std::shared_ptr<SAMSession> CreateSession (const std::string& id, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params);
void CloseSession (const std::string& id);
SAMSession * FindSession (const std::string& id) const;
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
private:
@@ -176,7 +200,7 @@ namespace client
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_DatagramSocket;
mutable std::mutex m_SessionsMutex;
std::map<std::string, SAMSession *> m_Sessions;
std::map<std::string, std::shared_ptr<SAMSession> > m_Sessions;
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
public:

357
SOCKS.cpp
View File

@@ -9,6 +9,8 @@
#include "ClientContext.h"
#include "I2PEndian.h"
#include "I2PTunnel.h"
#include "I2PService.h"
#include "util.h"
namespace i2p
{
@@ -17,11 +19,15 @@ namespace proxy
static const size_t socks_buffer_size = 8192;
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192;
static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8;
struct SOCKSDnsAddress
{
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;
@@ -51,7 +57,10 @@ namespace proxy
GET5_IPV6,
GET5_HOST_SIZE,
GET5_HOST,
DONE
READY,
UPSTREAM_RESOLVE,
UPSTREAM_CONNECT,
UPSTREAM_HANDSHAKE
};
enum authMethods
{
@@ -109,6 +118,7 @@ namespace proxy
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
boost::asio::const_buffers_1 GenerateUpstreamRequest();
bool Socks5ChooseAuth();
void SocksRequestFailed(errTypes error);
void SocksRequestSuccess();
@@ -116,12 +126,29 @@ namespace proxy
void SentSocksDone(const boost::system::error_code & ecode);
void SentSocksResponse(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
void ForwardSOCKS();
uint8_t m_sock_buff[socks_buffer_size];
boost::asio::ip::tcp::socket * m_sock;
void SocksUpstreamSuccess();
void AsyncUpstreamSockRead();
void SendUpstreamRequest();
void HandleUpstreamData(uint8_t * buff, std::size_t len);
void HandleUpstreamSockSend(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleUpstreamConnected(const boost::system::error_code & ecode,
boost::asio::ip::tcp::resolver::iterator itr);
void HandleUpstreamResolved(const boost::system::error_code & ecode,
boost::asio::ip::tcp::resolver::iterator itr);
boost::asio::ip::tcp::resolver m_proxy_resolver;
uint8_t m_sock_buff[socks_buffer_size];
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock, m_upstreamSock;
std::shared_ptr<i2p::stream::Stream> m_stream;
uint8_t *m_remaining_data; //Data left to be sent
uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded
uint8_t m_response[7+max_socks_hostname_size];
uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE];
uint8_t m_upstream_request[14+max_socks_hostname_size];
std::size_t m_upstream_response_len;
address m_address; //Address
std::size_t m_remaining_data_len; //Size of the data left to be sent
uint32_t m_4aip; //Used in 4a requests
@@ -133,25 +160,35 @@ namespace proxy
socksVersions m_socksv; //Socks version
cmdTypes m_cmd; // Command requested
state m_state;
const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses?
const std::string m_UpstreamProxyAddress;
const uint16_t m_UpstreamProxyPort;
public:
SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4)
SOCKSHandler(SOCKSServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) :
I2PServiceHandler(parent),
m_proxy_resolver(parent->GetService()),
m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4),
m_UseUpstreamProxy(useUpstream),
m_UpstreamProxyAddress(upstreamAddr),
m_UpstreamProxyPort(upstreamPort)
{ m_address.ip = 0; EnterState(GET_SOCKSV); }
~SOCKSHandler() { Terminate(); }
void Handle() { AsyncSockRead(); }
};
void SOCKSHandler::AsyncSockRead()
{
LogPrint(eLogDebug,"--- SOCKS async sock read");
if(m_sock)
LogPrint(eLogDebug, "SOCKS: async sock read");
if (m_sock) {
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
else
LogPrint(eLogError,"--- SOCKS no socket for read");
} else {
LogPrint(eLogError,"SOCKS: no socket for read");
}
}
void SOCKSHandler::Terminate()
@@ -159,14 +196,19 @@ namespace proxy
if (Kill()) return;
if (m_sock)
{
LogPrint(eLogDebug,"--- SOCKS close sock");
LogPrint(eLogDebug, "SOCKS: closing socket");
m_sock->close();
delete m_sock;
m_sock = nullptr;
}
if (m_upstreamSock)
{
LogPrint(eLogDebug, "SOCKS: closing upstream socket");
m_upstreamSock->close();
m_upstreamSock = nullptr;
}
if (m_stream)
{
LogPrint(eLogDebug,"--- SOCKS close stream");
LogPrint(eLogDebug, "SOCKS: closing stream");
m_stream.reset ();
}
Done(shared_from_this());
@@ -210,6 +252,37 @@ namespace proxy
return boost::asio::const_buffers_1(m_response,size);
}
boost::asio::const_buffers_1 SOCKSHandler::GenerateUpstreamRequest()
{
size_t upstreamRequestSize = 0;
// TODO: negotiate with upstream
// SOCKS 4a
m_upstream_request[0] = '\x04'; //version
m_upstream_request[1] = m_cmd;
htobe16buf(m_upstream_request+2, m_port);
m_upstream_request[4] = 0;
m_upstream_request[5] = 0;
m_upstream_request[6] = 0;
m_upstream_request[7] = 1;
// user id
m_upstream_request[8] = 'i';
m_upstream_request[9] = '2';
m_upstream_request[10] = 'p';
m_upstream_request[11] = 'd';
m_upstream_request[12] = 0;
upstreamRequestSize += 13;
if (m_address.dns.size <= max_socks_hostname_size - ( upstreamRequestSize + 1) ) {
// bounds check okay
memcpy(m_upstream_request + upstreamRequestSize, m_address.dns.value, m_address.dns.size);
upstreamRequestSize += m_address.dns.size;
// null terminate
m_upstream_request[++upstreamRequestSize] = 0;
} else {
LogPrint(eLogError, "SOCKS: BUG!!! m_addr.dns.sizs > max_socks_hostname - ( upstreamRequestSize + 1 ) )");
}
return boost::asio::const_buffers_1(m_upstream_request, upstreamRequestSize);
}
bool SOCKSHandler::Socks5ChooseAuth()
{
m_response[0] = '\x05'; //Version
@@ -217,16 +290,16 @@ namespace proxy
boost::asio::const_buffers_1 response(m_response,2);
if (m_authchosen == AUTH_UNACCEPTABLE)
{
LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed");
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
shared_from_this(), std::placeholders::_1));
shared_from_this(), std::placeholders::_1));
return false;
}
else
{
LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen);
LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen);
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse,
shared_from_this(), std::placeholders::_1));
shared_from_this(), std::placeholders::_1));
return true;
}
}
@@ -239,17 +312,17 @@ namespace proxy
switch (m_socksv)
{
case SOCKS4:
LogPrint(eLogWarning,"--- SOCKS4 failed: ", error);
LogPrint(eLogWarning, "SOCKS: v4 request failed: ", error);
if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors
response = GenerateSOCKS4Response(error, m_4aip, m_port);
break;
case SOCKS5:
LogPrint(eLogWarning,"--- SOCKS5 failed: ", error);
LogPrint(eLogWarning, "SOCKS: v5 request failed: ", error);
response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port);
break;
}
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
shared_from_this(), std::placeholders::_1));
shared_from_this(), std::placeholders::_1));
}
void SOCKSHandler::SocksRequestSuccess()
@@ -259,11 +332,11 @@ namespace proxy
switch (m_socksv)
{
case SOCKS4:
LogPrint(eLogInfo,"--- SOCKS4 connection success");
LogPrint(eLogInfo, "SOCKS: v4 connection success");
response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
break;
case SOCKS5:
LogPrint(eLogInfo,"--- SOCKS5 connection success");
LogPrint(eLogInfo, "SOCKS: v5 connection success");
auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash());
address ad; ad.dns.FromString(s);
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
@@ -271,7 +344,7 @@ namespace proxy
break;
}
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone,
shared_from_this(), std::placeholders::_1));
shared_from_this(), std::placeholders::_1));
}
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
@@ -294,7 +367,7 @@ namespace proxy
if ( m_cmd != CMD_CONNECT )
{
//TODO: we need to support binds and other shit!
LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd);
LogPrint(eLogError, "SOCKS: unsupported command: ", m_cmd);
SocksRequestFailed(SOCKS5_CMD_UNSUP);
return false;
}
@@ -304,22 +377,15 @@ namespace proxy
switch (m_socksv)
{
case SOCKS5:
LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype);
LogPrint(eLogError, "SOCKS: v5 unsupported address type: ", m_addrtype);
break;
case SOCKS4:
LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4");
LogPrint(eLogError, "SOCKS: request with v4a rejected because it's actually SOCKS4");
break;
}
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
return false;
}
//TODO: we may want to support other domains
if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos)
{
LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString());
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
return false;
}
return true;
}
@@ -341,7 +407,7 @@ namespace proxy
EnterState(GET5_AUTHNUM); //Initialize the parser at the right position
break;
default:
LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff));
LogPrint(eLogError, "SOCKS: rejected invalid version: ", ((int)*sock_buff));
Terminate();
return false;
}
@@ -368,7 +434,7 @@ namespace proxy
case CMD_UDP:
if (m_socksv == SOCKS5) break;
default:
LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff));
LogPrint(eLogError, "SOCKS: invalid command: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
@@ -386,7 +452,7 @@ namespace proxy
{
switch (m_socksv)
{
case SOCKS5: EnterState(DONE); break;
case SOCKS5: EnterState(READY); break;
case SOCKS4: EnterState(GET_IPV4); break;
}
}
@@ -407,7 +473,7 @@ namespace proxy
if (!*sock_buff)
{
if( m_4aip == 0 || m_4aip > 255 )
EnterState(DONE);
EnterState(READY);
else
EnterState(GET4A_HOST);
}
@@ -415,12 +481,12 @@ namespace proxy
case GET4A_HOST:
if (!*sock_buff)
{
EnterState(DONE);
EnterState(READY);
break;
}
if (m_address.dns.size >= max_socks_hostname_size)
{
LogPrint(eLogError,"--- SOCKS4a destination is too large");
LogPrint(eLogError, "SOCKS: v4a req failed: destination is too large");
SocksRequestFailed(SOCKS4_FAIL);
return false;
}
@@ -429,7 +495,7 @@ namespace proxy
case GET5_REQUESTV:
if (*sock_buff != SOCKS5)
{
LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff));
LogPrint(eLogError,"SOCKS: v5 rejected unknown request version: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
@@ -438,7 +504,7 @@ namespace proxy
case GET5_GETRSV:
if ( *sock_buff != 0 )
{
LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff));
LogPrint(eLogError, "SOCKS: v5 unknown reserved field: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
@@ -451,7 +517,7 @@ namespace proxy
case ADDR_IPV6: EnterState(GET5_IPV6); break;
case ADDR_DNS : EnterState(GET5_HOST_SIZE); break;
default:
LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff));
LogPrint(eLogError, "SOCKS: v5 unknown address type: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
@@ -470,13 +536,13 @@ namespace proxy
if (m_parseleft == 0) EnterState(GET_PORT);
break;
default:
LogPrint(eLogError,"--- SOCKS parse state?? ", m_state);
LogPrint(eLogError, "SOCKS: parse state?? ", m_state);
Terminate();
return false;
}
sock_buff++;
len--;
if (m_state == DONE)
if (m_state == READY)
{
m_remaining_data_len = len;
m_remaining_data = sock_buff;
@@ -488,21 +554,33 @@ namespace proxy
void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
LogPrint(eLogDebug,"--- SOCKS sock recv: ", len);
LogPrint(eLogDebug, "SOCKS: recieved ", len, " bytes");
if(ecode)
{
LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode);
Terminate();
LogPrint(eLogWarning, "SOCKS: recv got error: ", ecode);
Terminate();
return;
}
if (HandleData(m_sock_buff, len))
{
if (m_state == DONE)
if (m_state == READY)
{
LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
const std::string addr = m_address.dns.ToString();
LogPrint(eLogInfo, "SOCKS: requested ", addr, ":" , m_port);
const size_t addrlen = addr.size();
// does it end with .i2p?
if ( addr.rfind(".i2p") == addrlen - 4) {
// yes it does, make an i2p session
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
} else if (m_UseUpstreamProxy) {
// forward it to upstream proxy
ForwardSOCKS();
} else {
// no upstream proxy
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
}
}
else
AsyncSockRead();
@@ -511,13 +589,9 @@ namespace proxy
void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
{
if (!ecode)
Terminate();
else
{
LogPrint (eLogError,"--- SOCKS Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
if (ecode)
LogPrint (eLogError, "SOCKS: closing socket after sending failure because: ", ecode.message ());
Terminate();
}
void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode)
@@ -525,7 +599,7 @@ namespace proxy
if (!ecode)
{
if (Kill()) return;
LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
LogPrint (eLogInfo, "SOCKS: new I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, m_stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (m_remaining_data,m_remaining_data_len);
@@ -533,7 +607,7 @@ namespace proxy
}
else
{
LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ());
LogPrint (eLogError, "SOCKS: closing socket after completion reply because: ", ecode.message ());
Terminate();
}
}
@@ -542,7 +616,7 @@ namespace proxy
{
if (ecode)
{
LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ());
LogPrint (eLogError, "SOCKS: closing socket after sending reply because: ", ecode.message ());
Terminate();
}
}
@@ -556,20 +630,163 @@ namespace proxy
}
else
{
LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info.");
LogPrint (eLogError, "SOCKS: error when creating the stream, check the previous warnings for more info");
SocksRequestFailed(SOCKS5_HOST_UNREACH);
}
}
SOCKSServer::SOCKSServer(int port, std::shared_ptr<i2p::client::ClientDestination> localDestination) :
TCPIPAcceptor (port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
void SOCKSHandler::ForwardSOCKS()
{
LogPrint(eLogInfo, "SOCKS: forwarding to upstream");
EnterState(UPSTREAM_RESOLVE);
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort));
m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
void SOCKSHandler::AsyncUpstreamSockRead()
{
LogPrint(eLogDebug, "SOCKS: async upstream sock read");
if (m_upstreamSock) {
m_upstreamSock->async_read_some(boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE),
std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "SOCKS: no upstream socket for read");
SocksRequestFailed(SOCKS5_GEN_FAIL);
}
}
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(boost::asio::ip::tcp::socket * socket)
void SOCKSHandler::HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
return std::make_shared<SOCKSHandler> (this, socket);
if (ecode) {
if (m_state == UPSTREAM_HANDSHAKE ) {
// we are trying to handshake but it failed
SocksRequestFailed(SOCKS5_NET_UNREACH);
} else {
LogPrint(eLogError, "SOCKS: bad state when reading from upstream: ", (int) m_state);
}
return;
}
HandleUpstreamData(m_upstream_response, bytes_transfered);
}
void SOCKSHandler::SocksUpstreamSuccess()
{
LogPrint(eLogInfo, "SOCKS: upstream success");
boost::asio::const_buffers_1 response(nullptr, 0);
switch (m_socksv)
{
case SOCKS4:
LogPrint(eLogInfo, "SOCKS: v4 connection success");
response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
break;
case SOCKS5:
LogPrint(eLogInfo, "SOCKS: v5 connection success");
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, m_address, m_port);
break;
}
m_sock->send(response);
auto forwarder = std::make_shared<i2p::client::TCPIPPipe>(GetOwner(), m_sock, m_upstreamSock);
m_upstreamSock = nullptr;
m_sock = nullptr;
GetOwner()->AddHandler(forwarder);
forwarder->Start();
Terminate();
}
void SOCKSHandler::HandleUpstreamData(uint8_t * dataptr, std::size_t len)
{
if (m_state == UPSTREAM_HANDSHAKE) {
m_upstream_response_len += len;
// handle handshake data
if (m_upstream_response_len < SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) {
// too small, continue reading
AsyncUpstreamSockRead();
} else if (len == SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) {
// just right
uint8_t resp = m_upstream_response[1];
if (resp == SOCKS4_OK) {
// we have connected !
SocksUpstreamSuccess();
} else {
// upstream failure
LogPrint(eLogError, "SOCKS: upstream proxy failure: ", (int) resp);
// TODO: runtime error?
SocksRequestFailed(SOCKS5_GEN_FAIL);
}
} else {
// too big
SocksRequestFailed(SOCKS5_GEN_FAIL);
}
} else {
// invalid state
LogPrint(eLogError, "SOCKS: invalid state reading from upstream: ", (int) m_state);
}
}
void SOCKSHandler::SendUpstreamRequest()
{
LogPrint(eLogInfo, "SOCKS: negotiating with upstream proxy");
EnterState(UPSTREAM_HANDSHAKE);
if (m_upstreamSock) {
boost::asio::write(*m_upstreamSock,
GenerateUpstreamRequest());
AsyncUpstreamSockRead();
} else {
LogPrint(eLogError, "SOCKS: no upstream socket to send handshake to");
}
}
void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
{
if (ecode) {
LogPrint(eLogWarning, "SOCKS: could not connect to upstream proxy: ", ecode.message());
SocksRequestFailed(SOCKS5_NET_UNREACH);
return;
}
LogPrint(eLogInfo, "SOCKS: connected to upstream proxy");
SendUpstreamRequest();
}
void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
{
if (ecode) {
// error resolving
LogPrint(eLogWarning, "SOCKS: upstream proxy", m_UpstreamProxyAddress, " not resolved: ", ecode.message());
SocksRequestFailed(SOCKS5_NET_UNREACH);
return;
}
LogPrint(eLogInfo, "SOCKS: upstream proxy resolved");
EnterState(UPSTREAM_CONNECT);
auto & service = GetOwner()->GetService();
m_upstreamSock = std::make_shared<boost::asio::ip::tcp::socket>(service);
boost::asio::async_connect(*m_upstreamSock, itr,
std::bind(&SOCKSHandler::HandleUpstreamConnected,
shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}
SOCKSServer::SOCKSServer(const std::string& address, int port, const std::string& outAddress, uint16_t outPort,
std::shared_ptr<i2p::client::ClientDestination> localDestination) :
TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
{
m_UseUpstreamProxy = false;
if (outAddress.length() > 0)
SetUpstreamProxy(outAddress, outPort);
}
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
return std::make_shared<SOCKSHandler> (this, socket, m_UpstreamProxyAddress, m_UpstreamProxyPort, m_UseUpstreamProxy);
}
void SOCKSServer::SetUpstreamProxy(const std::string & addr, const uint16_t port)
{
m_UpstreamProxyAddress = addr;
m_UpstreamProxyPort = port;
m_UseUpstreamProxy = true;
}
}
}

12
SOCKS.h
View File

@@ -15,13 +15,21 @@ namespace proxy
{
public:
SOCKSServer(int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
SOCKSServer(const std::string& address, int port, const std::string& outAddress, uint16_t outPort,
std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
~SOCKSServer() {};
void SetUpstreamProxy(const std::string & addr, const uint16_t port);
protected:
// Implements TCPIPAcceptor
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
const char* GetName() { return "SOCKS"; }
private:
std::string m_UpstreamProxyAddress;
uint16_t m_UpstreamProxyPort;
bool m_UseUpstreamProxy;
};
typedef SOCKSServer SOCKSProxy;

412
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 ();
@@ -86,7 +114,7 @@ namespace transport
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU server: ", ex.what ());
LogPrint (eLogError, "SSU: server runtime exception: ", ex.what ());
}
}
}
@@ -101,7 +129,7 @@ namespace transport
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU V6 server: ", ex.what ());
LogPrint (eLogError, "SSU: v6 server runtime exception: ", ex.what ());
}
}
}
@@ -116,7 +144,7 @@ namespace transport
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU receivers: ", ex.what ());
LogPrint (eLogError, "SSU: receivers runtime exception: ", ex.what ());
}
}
}
@@ -174,12 +202,12 @@ namespace transport
moreBytes = m_Socket.available();
}
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
Receive ();
}
else
{
LogPrint ("SSU receive error: ", ecode.message ());
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
delete packet;
}
}
@@ -201,40 +229,46 @@ namespace transport
moreBytes = m_SocketV6.available();
}
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
ReceiveV6 ();
}
else
{
LogPrint ("SSU V6 receive error: ", ecode.message ());
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
delete packet;
}
}
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets)
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets,
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;
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
{
if (session) session->FlushData ();
auto it = m_Sessions.find (packet->from);
if (it != m_Sessions.end ())
session = it->second;
if (!session)
try
{
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
{
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
if (session) session->FlushData ();
auto it = sessions->find (packet->from);
if (it != sessions->end ())
session = it->second;
if (!session)
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[packet->from] = session;
}
LogPrint ("New SSU session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
(*sessions)[packet->from] = session;
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
}
}
}
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ());
if (session) session->FlushData ();
session = nullptr;
}
delete packet;
}
if (session) session->FlushData ();
@@ -256,96 +290,135 @@ namespace transport
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
{
auto it = m_Sessions.find (e);
if (it != m_Sessions.end ())
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
auto it = sessions.find (e);
if (it != sessions.end ())
return it->second;
else
return nullptr;
}
std::shared_ptr<SSUSession> SSUServer::GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
{
auto address = router->GetSSUAddress (!context.SupportsV6 ());
if (address)
CreateSession (router, address->host, address->port, peerTest);
else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
}
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest)
{
std::shared_ptr<SSUSession> session;
if (router)
{
auto address = router->GetSSUAddress (!context.SupportsV6 ());
if (router->UsesIntroducer ())
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, peerTest)); // always V4 thread
else
{
boost::asio::ip::udp::endpoint remoteEndpoint (addr, port);
auto& s = addr.is_v6 () ? m_ServiceV6 : m_Service;
s.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
}
}
}
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
{
auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
auto it = sessions.find (remoteEndpoint);
if (it != sessions.end ())
{
auto session = it->second;
if (peerTest && session->GetState () == eSessionStateEstablished)
session->SendPeerTest ();
}
else
{
// otherwise create new session
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
sessions[remoteEndpoint] = session;
// connect
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect ();
}
}
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
{
if (router && router->UsesIntroducer ())
{
auto address = router->GetSSUAddress (true); // v4 only for now
if (address)
{
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto it = m_Sessions.find (remoteEndpoint);
// check if session if presented alredy
if (it != m_Sessions.end ())
session = it->second;
else
{
auto session = it->second;
if (peerTest && session->GetState () == eSessionStateEstablished)
session->SendPeerTest ();
return;
}
// create new session
int numIntroducers = address->introducers.size ();
if (numIntroducers > 0)
{
// otherwise create new session
session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
std::shared_ptr<SSUSession> introducerSession;
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
// we might have a session to introducer already
for (int i = 0; i < numIntroducers; i++)
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[remoteEndpoint] = session;
}
if (!router->UsesIntroducer ())
{
// connect directly
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect ();
}
else
{
// connect through introducer
int numIntroducers = address->introducers.size ();
if (numIntroducers > 0)
{
std::shared_ptr<SSUSession> introducerSession;
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
// we might have a session to introducer already
for (int i = 0; i < numIntroducers; i++)
{
introducer = &(address->introducers[i]);
it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort));
if (it != m_Sessions.end ())
{
introducerSession = it->second;
break;
}
}
if (introducerSession) // session found
LogPrint ("Session to introducer already exists");
else // create new
{
LogPrint ("Creating new session to introducer");
introducer = &(address->introducers[0]); // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[introducerEndpoint] = introducerSession;
}
// introduce
LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (),
"] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction ();
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
{
uint8_t buf[1];
Send (buf, 0, remoteEndpoint); // send HolePunch
}
introducerSession->Introduce (introducer->iTag, introducer->iKey);
}
else
auto intr = &(address->introducers[i]);
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
if (ep.address ().is_v4 ()) // ipv4 only
{
LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented");
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions.erase (remoteEndpoint);
session.reset ();
}
if (!introducer) introducer = intr; // we pick first one for now
it = m_Sessions.find (ep);
if (it != m_Sessions.end ())
{
introducerSession = it->second;
break;
}
}
}
if (!introducer)
{
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 introducers present");
return;
}
if (introducerSession) // session found
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
else // create new
{
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;
}
// create session
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session;
// introduce
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
"] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction ();
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
{
uint8_t buf[1];
Send (buf, 0, remoteEndpoint); // send HolePunch
}
introducerSession->Introduce (*introducer, router);
}
else
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
}
else
LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address");
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
}
return session;
}
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
@@ -353,40 +426,69 @@ namespace transport
if (session)
{
session->Close ();
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions.erase (session->GetRemoteEndpoint ());
auto& ep = session->GetRemoteEndpoint ();
if (ep.address ().is_v6 ())
m_SessionsV6.erase (ep);
else
m_Sessions.erase (ep);
}
}
void SSUServer::DeleteAllSessions ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it: m_Sessions)
for (auto& it: m_Sessions)
it.second->Close ();
m_Sessions.clear ();
for (auto& it: m_SessionsV6)
it.second->Close ();
m_SessionsV6.clear ();
}
template<typename Filter>
std::shared_ptr<SSUSession> SSUServer::GetRandomSession (Filter filter)
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)
{
auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1);
auto ind = rand () % filteredSessions.size ();
return filteredSessions[ind];
}
return nullptr;
}
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded)
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded) // v4 only
{
return GetRandomSession (
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;
}
);
}
@@ -397,7 +499,7 @@ namespace transport
std::set<SSUSession *> ret;
for (int i = 0; i < maxNumIntroducers; i++)
{
auto session = GetRandomSession (
auto session = GetRandomV4Session (
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
{
return session->GetRelayTag () && !ret.count (session.get ()) &&
@@ -438,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)
@@ -455,25 +557,27 @@ 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 router = it1->GetRemoteRouter ();
if (router && i2p::context.AddIntroducer (*router, it1->GetRelayTag ()))
{
newList.push_back (it1->GetRemoteEndpoint ());
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.empty ())
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
{
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
if (introducer)
GetSession (introducer);
CreateSession (introducer);
}
ScheduleIntroducersUpdateTimer ();
}
@@ -535,13 +639,65 @@ namespace transport
it = m_PeerTests.erase (it);
}
else
it++;
++it;
}
if (numDeleted > 0)
LogPrint (eLogInfo, numDeleted, " peer tests have been expired");
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 ();
}
}
}
}

40
SSU.h
View File

@@ -9,7 +9,7 @@
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include "aes.h"
#include "Crypto.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
@@ -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,13 +38,18 @@ namespace transport
public:
SSUServer (int port);
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
~SSUServer ();
void Start ();
void Stop ();
std::shared_ptr<SSUSession> GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest = false);
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded);
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 ();
@@ -59,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 ();
@@ -69,11 +75,15 @@ namespace transport
void ReceiveV6 ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedPackets (std::vector<SSUPacket *> packets);
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomSession (Filter 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);
@@ -81,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
@@ -88,24 +104,26 @@ 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
mutable std::mutex m_SessionsMutex;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
public:
// for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
};
}
}

View File

@@ -5,6 +5,9 @@
#include "NetDb.h"
#include "SSU.h"
#include "SSUData.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
@@ -14,26 +17,22 @@ namespace transport
{
if (msg->len + fragmentSize > msg->maxLen)
{
LogPrint (eLogInfo, "SSU I2NP message size ", msg->maxLen, " is not enough");
I2NPMessage * newMsg = NewI2NPMessage ();
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *msg;
DeleteI2NPMessage (msg);
msg = newMsg;
}
memcpy (msg->buf + msg->len, fragment, fragmentSize);
msg->len += fragmentSize;
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
nextFragmentNum++;
}
SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ())
m_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_LastMessageReceivedTime (0)
{
m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE;
m_PacketSize = m_MaxPacketSize;
auto remoteRouter = session.GetRemoteRouter ();
if (remoteRouter)
AdjustPacketSize (*remoteRouter);
}
SSUData::~SSUData ()
@@ -48,13 +47,13 @@ namespace transport
void SSUData::Stop ()
{
m_ResendTimer.cancel ();
m_DecayTimer.cancel ();
m_IncompleteMessagesCleanupTimer.cancel ();
}
void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter)
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
{
auto ssuAddress = remoteRouter.GetSSUAddress ();
if (remoteRouter) return;
auto ssuAddress = remoteRouter->GetSSUAddress ();
if (ssuAddress && ssuAddress->mtu)
{
if (m_Session.IsV6 ())
@@ -67,11 +66,11 @@ namespace transport
m_PacketSize >>= 4;
m_PacketSize <<= 4;
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
}
else
{
LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu);
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu);
m_PacketSize = m_MaxPacketSize;
}
}
@@ -81,7 +80,7 @@ namespace transport
{
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
if (routerInfo)
AdjustPacketSize (*routerInfo);
AdjustPacketSize (routerInfo);
}
void SSUData::ProcessSentMessageAck (uint32_t msgID)
@@ -160,12 +159,12 @@ namespace transport
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
{
LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size");
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
return;
}
@@ -202,23 +201,23 @@ namespace transport
break;
}
if (isLast)
LogPrint (eLogDebug, "Message ", msgID, " complete");
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
}
}
else
{
if (fragmentNum < incompleteMessage->nextFragmentNum)
// duplicate fragment
LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored");
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
else
{
// missing fragment
LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
else
LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
}
isLast = false;
}
@@ -236,30 +235,31 @@ 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_Handler.PutNextMessage (msg);
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
if (!msg->IsExpired ())
{
#ifdef WITH_EVENTS
EmitEvent({{"type", "transport.recvmsg"} , {"ident", m_Session.GetIdentHashBase64()}, {"number", "1"}});
#endif
m_Handler.PutNextMessage (msg);
}
else
LogPrint (eLogDebug, "SSU: message expired");
}
else
{
LogPrint (eLogWarning, "SSU message ", msgID, " already received");
i2p::DeleteI2NPMessage (msg);
}
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
}
else
{
// we expect DeliveryStatus
if (msg->GetTypeID () == eI2NPDeliveryStatus)
{
LogPrint ("SSU session established");
LogPrint (eLogDebug, "SSU: session established");
m_Session.Established ();
}
else
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ());
DeleteI2NPMessage (msg);
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
}
}
else
@@ -278,7 +278,7 @@ namespace transport
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint (eLogDebug, "Process SSU data flags=", (int)flag, " len=", len);
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
@@ -287,20 +287,19 @@ namespace transport
{
uint8_t extendedDataSize = *buf;
buf++; // size
LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented");
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
buf += extendedDataSize;
}
// process data
ProcessFragments (buf);
}
void SSUData::Send (i2p::I2NPMessage * msg)
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{
uint32_t msgID = msg->ToSSU ();
if (m_SentMessages.count (msgID) > 0)
{
LogPrint (eLogWarning, "SSU message ", msgID, " already sent");
DeleteI2NPMessage (msg);
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
return;
}
if (m_SentMessages.empty ()) // schedule resend at first message only
@@ -357,7 +356,7 @@ namespace transport
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogError, "Can't send SSU fragment ", ec.what ());
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
}
if (!isLast)
{
@@ -368,7 +367,6 @@ namespace transport
len = 0;
fragmentNum++;
}
DeleteI2NPMessage (msg);
}
void SSUData::SendMsgAck (uint32_t msgID)
@@ -379,7 +377,7 @@ namespace transport
payload++;
*payload = 1; // number of ACKs
payload++;
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
htobe32buf (payload, msgID); // msgID
payload += 4;
*payload = 0; // number of fragments
@@ -392,7 +390,7 @@ namespace transport
{
if (fragmentNum > 64)
{
LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64");
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18];
@@ -431,57 +429,50 @@ 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 (eLogError, "Can't resend SSU fragment ", ec.what ());
LogPrint (eLogWarning, "SSU: Can't resend data fragment ", ec.what ());
}
}
}
it->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
it++;
++it;
}
else
{
LogPrint (eLogError, "SSU message has not been ACKed after ", MAX_NUM_RESENDS, " attempts. Deleted");
LogPrint (eLogInfo, "SSU: message has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
it = m_SentMessages.erase (it);
}
}
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 ();
@@ -500,12 +491,17 @@ namespace transport
{
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
{
LogPrint (eLogError, "SSU message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds. Deleted");
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
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;
@@ -27,8 +31,9 @@ namespace transport
const int RESEND_INTERVAL = 3; // in seconds
const int MAX_NUM_RESENDS = 5;
const int DECAY_INTERVAL = 20; // in seconds
const int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
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;
@@ -59,13 +64,12 @@ namespace transport
struct IncompleteMessage
{
I2NPMessage * msg;
std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
~IncompleteMessage () { if (msg) DeleteI2NPMessage (msg); };
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
};
@@ -81,7 +85,7 @@ namespace transport
{
public:
SSUData (SSUSession& session);
SSUData (SSUSession& session);
~SSUData ();
void Start ();
@@ -89,8 +93,9 @@ namespace transport
void ProcessMessage (uint8_t * buf, size_t len);
void FlushReceivedMessage ();
void Send (i2p::I2NPMessage * msg);
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
private:
@@ -104,23 +109,20 @@ namespace transport
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void ScheduleDecay ();
void HandleDecayTimer (const boost::system::error_code& ecode);
void ScheduleIncompleteMessagesCleanup ();
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter);
private:
SSUSession& m_Session;
std::map<uint32_t, 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
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,7 @@
#include <inttypes.h>
#include <set>
#include <memory>
#include "aes.h"
#include "hmac.h"
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "TransportSession.h"
#include "SSUData.h"
@@ -14,20 +13,21 @@ namespace i2p
{
namespace transport
{
#pragma pack(1)
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
struct SSUHeader
{
uint8_t mac[16];
uint8_t iv[16];
uint8_t flag;
uint32_t time;
uint8_t time[4];
uint8_t GetPayloadType () const { return flag >> 4; };
bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
};
#pragma pack()
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;
@@ -40,6 +40,9 @@ namespace transport
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
// extended options
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
enum SessionState
{
eSessionStateUnknown,
@@ -70,14 +73,15 @@ namespace transport
void Connect ();
void WaitForConnect ();
void Introduce (uint32_t iTag, const uint8_t * iKey);
void Introduce (const i2p::data::RouterInfo::Introducer& introducer,
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
void WaitForIntroduction ();
void Close ();
void Done ();
void Failed ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessage (I2NPMessage * msg);
void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs);
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; };
@@ -86,6 +90,7 @@ namespace transport
void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void FlushData ();
@@ -94,51 +99,46 @@ namespace transport
boost::asio::io_service& GetService ();
void CreateAESandMacKey (const uint8_t * pubKey);
void PostI2NPMessage (I2NPMessage * msg);
void PostI2NPMessages (std::vector<I2NPMessage *> msgs);
size_t GetSSUHeaderSize (const uint8_t * buf) const;
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest ();
void SendRelayRequest (uint32_t iTag, const uint8_t * iKey);
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x);
void ProcessSessionConfirmed (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x, bool sendRelayTag = true);
void ProcessSessionConfirmed (const uint8_t * buf, size_t len);
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
void ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
void ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from);
void ProcessRelayResponse (uint8_t * buf, size_t len);
void ProcessRelayIntro (uint8_t * buf, size_t len);
void SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from);
void ProcessRelayResponse (const uint8_t * buf, size_t len);
void ProcessRelayIntro (const uint8_t * buf, size_t len);
void Established ();
void Failed ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
void 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
void Send (const uint8_t * buf, size_t size);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey,
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
void DecryptSessionKey (uint8_t * buf, size_t len);
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
const uint8_t * GetIntroKey () const;
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
private:
friend class SSUData; // TODO: change in later
SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_Timer;
bool m_PeerTest;
boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest;
SessionState m_State;
bool m_IsSessionKey;
uint32_t m_RelayTag;
@@ -146,9 +146,12 @@ namespace transport
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey;
i2p::data::RouterInfo::IntroKey m_IntroKey;
uint32_t m_CreationTime; // seconds since epoch
SSUData m_Data;
bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
std::map<uint32_t, std::shared_ptr<const i2p::data::RouterInfo> > m_RelayRequests; // nonce->Charlie
};

491
Signature.cpp Normal file
View File

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

View File

@@ -2,13 +2,13 @@
#define SIGNATURE_H__
#include <inttypes.h>
#include <cryptopp/dsa.h>
#include <cryptopp/rsa.h>
#include <cryptopp/asn.h>
#include <cryptopp/oids.h>
#include <cryptopp/osrng.h>
#include <cryptopp/eccrypto.h>
#include "CryptoConst.h"
#include <string.h>
#include <openssl/dsa.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include "Crypto.h"
namespace i2p
{
@@ -30,7 +30,7 @@ namespace crypto
public:
virtual ~Signer () {};
virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0;
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
};
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
@@ -42,13 +42,27 @@ namespace crypto
DSAVerifier (const uint8_t * signingKey)
{
m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH));
m_PublicKey = CreateDSA ();
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
}
~DSAVerifier ()
{
DSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
CryptoPP::DSA::Verifier verifier (m_PublicKey);
return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH);
// calculate SHA1 digest
uint8_t digest[20];
SHA1 (buf, len, digest);
// signature
DSA_SIG * sig = DSA_SIG_new();
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
// DSA verification
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
@@ -56,7 +70,7 @@ namespace crypto
private:
CryptoPP::DSA::PublicKey m_PublicKey;
DSA * m_PublicKey;
};
class DSASigner: public Signer
@@ -65,189 +79,212 @@ namespace crypto
DSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH));
m_PrivateKey = CreateDSA ();
DSA_set0_key (m_PrivateKey, NULL, BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
}
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
~DSASigner ()
{
CryptoPP::DSA::Signer signer (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature);
DSA_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[20];
SHA1 (buf, len, digest);
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
const BIGNUM * r, * s;
DSA_SIG_get0 (sig, &r, &s);
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
DSA_SIG_free(sig);
}
private:
CryptoPP::DSA::PrivateKey m_PrivateKey;
DSA * m_PrivateKey;
};
inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CryptoPP::DSA::PrivateKey privateKey;
CryptoPP::DSA::PublicKey publicKey;
privateKey.Initialize (rnd, dsap, dsaq, dsag);
privateKey.MakePublicKey (publicKey);
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA * dsa = CreateDSA ();
DSA_generate_key (dsa);
const BIGNUM * pub_key, * priv_key;
DSA_get0_key(dsa, &pub_key, &priv_key);
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa);
}
template<typename Hash, size_t keyLen>
struct SHA256Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA256 (buf, len, digest);
}
enum { hashLen = 32 };
};
struct SHA384Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA384 (buf, len, digest);
}
enum { hashLen = 48 };
};
struct SHA512Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA512 (buf, len, digest);
}
enum { hashLen = 64 };
};
template<typename Hash, int curve, size_t keyLen>
class ECDSAVerifier: public Verifier
{
public:
template<typename Curve>
ECDSAVerifier (Curve curve, const uint8_t * signingKey)
ECDSAVerifier (const uint8_t * signingKey)
{
m_PublicKey.Initialize (curve,
CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2),
CryptoPP::Integer (signingKey + keyLen/2, keyLen/2)));
m_PublicKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_set_public_key_affine_coordinates (m_PublicKey,
BN_bin2bn (signingKey, keyLen/2, NULL),
BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL));
}
~ECDSAVerifier ()
{
EC_KEY_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Verifier verifier (m_PublicKey);
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_SIG_new();
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return keyLen; };
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
private:
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey m_PublicKey;
EC_KEY * m_PublicKey;
};
template<typename Hash>
template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer
{
public:
template<typename Curve>
ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen)
ECDSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length
m_PrivateKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL));
}
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
~ECDSASigner ()
{
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Signer signer (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature);
EC_KEY_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
// signatureLen = keyLen
bn2buf (sig->r, signature, keyLen/2);
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
ECDSA_SIG_free(sig);
}
private:
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey m_PrivateKey;
EC_KEY * m_PrivateKey;
};
template<typename Hash, typename Curve>
inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve,
size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey privateKey;
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey publicKey;
privateKey.Initialize (rnd, curve);
privateKey.MakePublicKey (publicKey);
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2);
auto q = publicKey.GetPublicElement ();
q.x.Encode (signingPublicKey, keyLen/2);
q.y.Encode (signingPublicKey + keyLen/2, keyLen/2);
}
EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_generate_key (signingKey);
bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2);
BIGNUM * x = BN_new(), * y = BN_new();
EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey),
EC_KEY_get0_public_key (signingKey), x, y, NULL);
bn2buf (x, signingPublicKey, keyLen/2);
bn2buf (y, signingPublicKey + keyLen/2, keyLen/2);
BN_free (x); BN_free (y);
EC_KEY_free (signingKey);
}
// ECDSA_SHA256_P256
const size_t ECDSAP256_KEY_LENGTH = 64;
class ECDSAP256Verifier: public ECDSAVerifier<CryptoPP::SHA256, ECDSAP256_KEY_LENGTH>
typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
public:
ECDSAP256Verifier (const uint8_t * signingKey):
ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey)
{
}
};
class ECDSAP256Signer: public ECDSASigner<CryptoPP::SHA256>
{
public:
ECDSAP256Signer (const uint8_t * signingPrivateKey):
ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH)
{
}
};
inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys<CryptoPP::SHA256> (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA384_P384
const size_t ECDSAP384_KEY_LENGTH = 96;
class ECDSAP384Verifier: public ECDSAVerifier<CryptoPP::SHA384, ECDSAP384_KEY_LENGTH>
typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
public:
ECDSAP384Verifier (const uint8_t * signingKey):
ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey)
{
}
};
class ECDSAP384Signer: public ECDSASigner<CryptoPP::SHA384>
{
public:
ECDSAP384Signer (const uint8_t * signingPrivateKey):
ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH)
{
}
};
inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys<CryptoPP::SHA384> (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA512_P521
const size_t ECDSAP521_KEY_LENGTH = 132;
class ECDSAP521Verifier: public ECDSAVerifier<CryptoPP::SHA512, ECDSAP521_KEY_LENGTH>
typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
public:
ECDSAP521Verifier (const uint8_t * signingKey):
ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey)
{
}
};
class ECDSAP521Signer: public ECDSASigner<CryptoPP::SHA512>
{
public:
ECDSAP521Signer (const uint8_t * signingPrivateKey):
ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH)
{
}
};
inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys<CryptoPP::SHA512> (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// RSA
template<typename Hash, size_t keyLen>
template<typename Hash, int type, size_t keyLen>
class RSAVerifier: public Verifier
{
public:
RSAVerifier (const uint8_t * signingKey)
{
m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae));
m_PublicKey = RSA_new ();
memset (m_PublicKey, 0, sizeof (RSA));
RSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, keyLen, NULL) /* n */ , BN_dup (GetRSAE ()) /* d */, NULL);
}
~RSAVerifier ()
{
RSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Verifier verifier (m_PublicKey);
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
return RSA_verify (type, digest, Hash::hashLen, signature, GetSignatureLen (), m_PublicKey);
}
size_t GetPublicKeyLen () const { return keyLen; }
size_t GetSignatureLen () const { return keyLen; }
@@ -255,161 +292,151 @@ namespace crypto
private:
CryptoPP::RSA::PublicKey m_PublicKey;
RSA * m_PublicKey;
};
template<typename Hash>
template<typename Hash, int type, size_t keyLen>
class RSASigner: public Signer
{
public:
RSASigner (const uint8_t * signingPrivateKey, size_t keyLen)
RSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2),
rsae,
CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2));
m_PrivateKey = RSA_new ();
memset (m_PrivateKey, 0, sizeof (RSA));
RSA_set0_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen, NULL), /* n */
BN_dup (GetRSAE ()) /* e */, BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL) /* d */);
}
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
~RSASigner ()
{
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Signer signer (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature);
RSA_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
unsigned int signatureLen = keyLen;
RSA_sign (type, digest, Hash::hashLen, signature, &signatureLen, m_PrivateKey);
}
private:
CryptoPP::RSA::PrivateKey m_PrivateKey;
RSA * m_PrivateKey;
};
inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd,
size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
inline void CreateRSARandomKeys (size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CryptoPP::RSA::PrivateKey privateKey;
privateKey.Initialize (rnd, publicKeyLen*8, rsae);
privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen);
privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen);
privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen);
RSA * rsa = RSA_new ();
BIGNUM * e = BN_dup (GetRSAE ()); // make it non-const
RSA_generate_key_ex (rsa, publicKeyLen*8, e, NULL);
const BIGNUM * n, * d, * e1;
RSA_get0_key (rsa, &n, &e1, &d);
bn2buf (n, signingPrivateKey, publicKeyLen);
bn2buf (d, signingPrivateKey + publicKeyLen, publicKeyLen);
bn2buf (n, signingPublicKey, publicKeyLen);
BN_free (e); // this e is not assigned to rsa->e
RSA_free (rsa);
}
// RSA_SHA256_2048
const size_t RSASHA2562048_KEY_LENGTH = 256;
class RSASHA2562048Verifier: public RSAVerifier<CryptoPP::SHA256, RSASHA2562048_KEY_LENGTH>
{
public:
RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
{
}
};
class RSASHA2562048Signer: public RSASigner<CryptoPP::SHA256>
{
public:
RSASHA2562048Signer (const uint8_t * signingPrivateKey):
RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2)
{
}
};
typedef RSAVerifier<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Verifier;
typedef RSASigner<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Signer;
// RSA_SHA384_3072
const size_t RSASHA3843072_KEY_LENGTH = 384;
class RSASHA3843072Verifier: public RSAVerifier<CryptoPP::SHA384, RSASHA3843072_KEY_LENGTH>
{
public:
RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
{
}
};
class RSASHA3843072Signer: public RSASigner<CryptoPP::SHA384>
{
public:
RSASHA3843072Signer (const uint8_t * signingPrivateKey):
RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2)
{
}
};
typedef RSAVerifier<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Verifier;
typedef RSASigner<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Signer;
// RSA_SHA512_4096
const size_t RSASHA5124096_KEY_LENGTH = 512;
class RSASHA5124096Verifier: public RSAVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
typedef RSAVerifier<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Verifier;
typedef RSASigner<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Signer;
// EdDSA
struct EDDSAPoint
{
BIGNUM * x, * y;
BIGNUM * z, * t; // projective coordinates
EDDSAPoint (): x(nullptr), y(nullptr), z(nullptr), t(nullptr) {};
EDDSAPoint (const EDDSAPoint& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
{ *this = other; };
EDDSAPoint (EDDSAPoint&& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
{ *this = std::move (other); };
EDDSAPoint (BIGNUM * x1, BIGNUM * y1, BIGNUM * z1 = nullptr, BIGNUM * t1 = nullptr): x(x1), y(y1), z(z1), t(t1) {};
~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); };
EDDSAPoint& operator=(EDDSAPoint&& other)
{
if (x) BN_free (x); x = other.x; other.x = nullptr;
if (y) BN_free (y); y = other.y; other.y = nullptr;
if (z) BN_free (z); z = other.z; other.z = nullptr;
if (t) BN_free (t); t = other.t; other.t = nullptr;
return *this;
}
EDDSAPoint& operator=(const EDDSAPoint& other)
{
if (x) BN_free (x); x = other.x ? BN_dup (other.x) : nullptr;
if (y) BN_free (y); y = other.y ? BN_dup (other.y) : nullptr;
if (z) BN_free (z); z = other.z ? BN_dup (other.z) : nullptr;
if (t) BN_free (t); t = other.t ? BN_dup (other.t) : nullptr;
return *this;
}
EDDSAPoint operator-() const
{
BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL;
if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); };
if (y) y1 = BN_dup (y);
if (z) z1 = BN_dup (z);
if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); };
return EDDSAPoint {x1, y1, z1, t1};
}
};
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
class EDDSA25519Verifier: public Verifier
{
public:
RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
{
}
};
EDDSA25519Verifier (const uint8_t * signingKey);
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
class RSASHA5124096Signer: public RSASigner<CryptoPP::SHA512>
{
public:
RSASHA5124096Signer (const uint8_t * signingPrivateKey):
RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2)
{
}
};
// Raw verifiers
class RawVerifier
{
public:
virtual ~RawVerifier () {};
virtual void Update (const uint8_t * buf, size_t len) = 0;
virtual bool Verify (const uint8_t * signature) = 0;
};
template<typename Hash, size_t keyLen>
class RSARawVerifier: public RawVerifier
{
public:
RSARawVerifier (const uint8_t * signingKey):
n (signingKey, keyLen)
{
}
void Update (const uint8_t * buf, size_t len)
{
m_Hash.Update (buf, len);
}
bool Verify (const uint8_t * signature)
{
// RSA encryption first
CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen),
CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n
uint8_t enSigBuf[keyLen];
enSig.Encode (enSigBuf, keyLen);
uint8_t digest[Hash::DIGESTSIZE];
m_Hash.Final (digest);
if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key
// we assume digest is right aligned, at least for PKCS#1 v1.5 padding
return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE);
}
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
private:
CryptoPP::Integer n; // RSA modulus
Hash m_Hash;
};
EDDSAPoint m_PublicKey;
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
class RSASHA5124096RawVerifier: public RSARawVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
class EDDSA25519Signer: public Signer
{
public:
RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey)
{
}
EDDSA25519Signer (const uint8_t * signingPrivateKey);
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; };
private:
uint8_t m_ExpandedPrivateKey[64];
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
}
}
}

View File

@@ -1,4 +1,4 @@
#include <cryptopp/gzip.h>
#include "Crypto.h"
#include "Log.h"
#include "RouterInfo.h"
#include "RouterContext.h"
@@ -20,9 +20,8 @@ namespace stream
m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO),
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
m_RemoteIdentity = remote->GetIdentity ();
UpdateCurrentRemoteLease ();
}
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
@@ -32,12 +31,11 @@ namespace stream
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE),
m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
{
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
}
Stream::~Stream ()
{
Terminate ();
while (!m_ReceiveQueue.empty ())
{
auto packet = m_ReceiveQueue.front ();
@@ -53,7 +51,7 @@ namespace stream
delete it;
m_SavedPackets.clear ();
LogPrint (eLogDebug, "Stream deleted");
LogPrint (eLogDebug, "Streaming: Stream deleted");
}
void Stream::Terminate ()
@@ -61,6 +59,13 @@ namespace stream
m_AckSendTimer.cancel ();
m_ReceiveTimer.cancel ();
m_ResendTimer.cancel ();
if (m_SendHandler)
{
auto handler = m_SendHandler;
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)
@@ -77,13 +82,13 @@ namespace stream
if (!receivedSeqn && !isSyn)
{
// plain ack
LogPrint (eLogDebug, "Plain ACK received");
LogPrint (eLogDebug, "Streaming: Plain ACK received");
delete packet;
return;
}
LogPrint (eLogDebug, "Received seqn=", receivedSeqn);
if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID);
if (receivedSeqn == m_LastReceivedSequenceNumber + 1)
{
// we have received next in sequence message
ProcessPacket (packet);
@@ -108,7 +113,9 @@ namespace stream
if (!m_IsAckSendScheduled)
{
m_IsAckSendScheduled = true;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT));
auto ackTimeout = m_RTT/10;
if (ackTimeout > ACK_SEND_TIMEOUT) ackTimeout = ACK_SEND_TIMEOUT;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ackTimeout));
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1));
}
@@ -122,13 +129,13 @@ namespace stream
if (receivedSeqn <= m_LastReceivedSequenceNumber)
{
// we have received duplicate
LogPrint (eLogWarning, "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, "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)
@@ -155,7 +162,8 @@ namespace stream
void Stream::SavePacket (Packet * packet)
{
m_SavedPackets.insert (packet);
if (!m_SavedPackets.insert (packet).second)
delete packet;
}
void Stream::ProcessPacket (Packet * packet)
@@ -163,43 +171,38 @@ namespace stream
// process flags
uint32_t receivedSeqn = packet->GetSeqn ();
uint16_t flags = packet->GetFlags ();
LogPrint (eLogDebug, "Process seqn=", receivedSeqn, ", flags=", flags);
LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags);
const uint8_t * optionData = packet->GetOptionData ();
if (flags & PACKET_FLAG_SYNCHRONIZE)
LogPrint (eLogDebug, "Synchronize");
if (flags & PACKET_FLAG_DELAY_REQUESTED)
{
optionData += 2;
}
if (flags & PACKET_FLAG_FROM_INCLUDED)
{
optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ());
LogPrint (eLogInfo, "From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ());
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, packet->GetOptionSize ());
optionData += m_RemoteIdentity->GetFullLen ();
if (!m_RemoteLeaseSet)
LogPrint (eLogDebug, "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)
{
uint16_t maxPacketSize = bufbe16toh (optionData);
LogPrint (eLogDebug, "Max packet size ", maxPacketSize);
LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize);
optionData += 2;
}
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{
LogPrint (eLogDebug, "Signature");
uint8_t signature[256];
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
memcpy (signature, optionData, signatureLen);
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
if (!m_RemoteIdentity.Verify (packet->GetBuffer (), packet->GetLength (), signature))
if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
{
LogPrint (eLogError, "Signature verification failed");
Close ();
LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
Close ();
flags |= PACKET_FLAG_CLOSE;
}
memcpy (const_cast<uint8_t *>(optionData), signature, signatureLen);
@@ -217,12 +220,19 @@ namespace stream
m_LastReceivedSequenceNumber = receivedSeqn;
if (flags & PACKET_FLAG_CLOSE)
if (flags & PACKET_FLAG_RESET)
{
LogPrint (eLogInfo, "Closed");
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)
@@ -230,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)
{
@@ -247,16 +262,21 @@ namespace stream
}
if (nacked)
{
LogPrint (eLogDebug, "Packet ", seqn, " NACK");
it++;
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK");
++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;
@@ -272,6 +292,10 @@ namespace stream
m_LastWindowSizeIncreaseTime = ts;
}
}
if (!seqn && m_RoutingSession) // first message confirmed
m_RoutingSession->SetSharedRoutingPath (
std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, m_RTT, 0, 0}));
}
else
break;
@@ -283,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)
@@ -299,6 +325,15 @@ namespace stream
return len;
}
void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler)
{
if (m_SendHandler)
handler (boost::asio::error::make_error_code (boost::asio::error::in_progress));
else
m_SendHandler = handler;
Send (buf, len);
}
void Stream::SendBuffer ()
{
int numMsgs = m_WindowSize - m_SentPackets.size ();
@@ -321,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
@@ -338,11 +373,11 @@ namespace stream
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen ();
htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size
size += 2; // options size
m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen);
m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen);
size += identityLen; // from
htobe16buf (packet + size, STREAMING_MTU);
size += 2; // max packet size
@@ -351,7 +386,7 @@ namespace stream
size += signatureLen; // signature
m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size);
size += m_SendBuffer.gcount (); // payload
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
}
else
{
@@ -367,14 +402,22 @@ namespace stream
packets.push_back (p);
numMsgs--;
}
if (m_SendBuffer.eof () && m_SendHandler)
{
m_SendHandler (boost::system::error_code ());
m_SendHandler = nullptr;
}
}
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);
@@ -397,7 +440,7 @@ namespace stream
}
if (lastReceivedSeqn < 0)
{
LogPrint (eLogError, "No packets have been received yet");
LogPrint (eLogError, "Streaming: No packets have been received yet");
return;
}
@@ -423,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;
}
@@ -453,40 +496,37 @@ namespace stream
p.len = size;
SendPackets (std::vector<Packet *> { &p });
LogPrint ("Quick Ack sent. ", (int)numNacks, " NACKs");
LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs");
}
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, "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, "Unexpected stream status ", (int)m_Status);
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID);
};
}
@@ -501,24 +541,24 @@ 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
size++; // resend delay
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
size += 2; // flags
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen ();
htobe16buf (packet + size, signatureLen); // signature only
size += 2; // options size
uint8_t * signature = packet + size;
memset (packet + size, 0, signatureLen);
size += signatureLen; // signature
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
p->len = size;
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
LogPrint ("FIN sent");
LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID);
}
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
@@ -550,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
@@ -572,31 +607,49 @@ namespace stream
UpdateCurrentRemoteLease ();
if (!m_RemoteLeaseSet)
{
LogPrint (eLogError, "Can't send packets. Missing remote LeaseSet");
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID);
return;
}
}
if (!m_CurrentOutboundTunnel) // first message to send
{
// try to get shared path first
if (!m_RoutingSession)
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
if (m_RoutingSession)
{
auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
if (routingPath)
{
m_CurrentOutboundTunnel = routingPath->outboundTunnel;
m_CurrentRemoteLease = routingPath->remoteLease;
m_RTT = routingPath->rtt;
m_RTO = m_RTT*1.5; // TODO: implement it better
}
}
}
if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ())
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel ();
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel)
{
LogPrint (eLogError, "No outbound tunnels in the pool");
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID);
return;
}
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000)
UpdateCurrentRemoteLease ();
if (ts < m_CurrentRemoteLease.endDate)
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet
ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
UpdateCurrentRemoteLease (true);
if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
for (auto it: packets)
{
auto msg = m_RoutingSession->WrapSingleMessage (CreateDataMessage (it->GetBuffer (), it->GetLength ()));
auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (it->GetBuffer (), it->GetLength (), m_Port));
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID,
m_CurrentRemoteLease->tunnelGateway, m_CurrentRemoteLease->tunnelID,
msg
});
m_NumSentBytes += it->GetLength ();
@@ -604,13 +657,43 @@ namespace stream
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
}
else
LogPrint (eLogWarning, "All leases are expired");
{
LogPrint (eLogWarning, "Streaming: Remote lease is not available, sSID=", m_SendStreamID);
if (m_RoutingSession)
m_RoutingSession->SetSharedRoutingPath (nullptr); // invalidate routing path
}
}
void Stream::SendUpdatedLeaseSet ()
{
if (m_RoutingSession)
{
if (m_RoutingSession->IsLeaseSetNonConfirmed ())
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT)
{
// LeaseSet was not confirmed, should try other tunnels
LogPrint (eLogWarning, "Streaming: LeaseSet was not confrimed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit");
m_RoutingSession->SetSharedRoutingPath (nullptr);
m_CurrentOutboundTunnel = nullptr;
m_CurrentRemoteLease = nullptr;
SendQuickAck ();
}
}
else if (m_RoutingSession->IsLeaseSetUpdated ())
{
LogPrint (eLogDebug, "Streaming: sending updated LeaseSet");
SendQuickAck ();
}
}
}
void Stream::ScheduleResend ()
{
m_ResendTimer.cancel ();
// check for invalid value
if (m_RTO <= 0) m_RTO = INITIAL_RTO;
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));
@@ -623,7 +706,7 @@ namespace stream
// check for resend attempts
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
{
LogPrint (eLogWarning, "Stream 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;
@@ -645,6 +728,7 @@ namespace stream
if (packets.size () > 0)
{
m_NumResendAttempts++;
m_RTO *= 2;
switch (m_NumResendAttempts)
{
case 1: // congesion avoidance
@@ -652,15 +736,18 @@ namespace stream
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
break;
case 2:
case 4:
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time
// no break here
case 4:
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
UpdateCurrentRemoteLease (); // pick another lease
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change
LogPrint (eLogWarning, "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
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
LogPrint (eLogWarning, "Another outbound tunnel has been selected for stream");
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 with sSID=", m_SendStreamID);
break;
default: ;
}
@@ -676,89 +763,122 @@ namespace stream
{
if (m_LastReceivedSequenceNumber < 0)
{
LogPrint (eLogWarning, "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;
}
}
void Stream::UpdateCurrentRemoteLease ()
void Stream::UpdateCurrentRemoteLease (bool expired)
{
if (!m_RemoteLeaseSet)
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{
m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ());
if (!m_RemoteLeaseSet)
LogPrint ("LeaseSet ", m_RemoteIdentity.GetIdentHash ().ToBase64 (), " not found");
m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!m_RemoteLeaseSet)
{
LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found");
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
}
}
if (m_RemoteLeaseSet)
{
if (!m_RoutingSession)
m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (m_RemoteLeaseSet, 32);
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first
if (leases.empty ())
{
m_LocalDestination.GetOwner ().RequestDestination (m_RemoteIdentity.GetIdentHash ()); // time to re-request
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
expired = false;
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to request
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
}
if (!leases.empty ())
{
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID)
// make sure we don't select previous
i = (i + 1) % leases.size (); // if so, pick next
m_CurrentRemoteLease = leases[i];
bool updated = false;
if (expired && m_CurrentRemoteLease)
{
for (const auto& it: leases)
if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID))
{
m_CurrentRemoteLease = it;
updated = true;
break;
}
}
if (!updated)
{
uint32_t i = rand () % leases.size ();
if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID)
// make sure we don't select previous
i = (i + 1) % leases.size (); // if so, pick next
m_CurrentRemoteLease = leases[i];
}
}
else
{
LogPrint (eLogWarning, "Streaming: All remote leases are expired");
m_RemoteLeaseSet = nullptr;
m_CurrentRemoteLease.endDate = 0;
// re-request expired
m_CurrentRemoteLease = nullptr;
// we have requested expired before, no need to do it twice
}
}
else
m_CurrentRemoteLease.endDate = 0;
{
LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found");
m_CurrentRemoteLease = nullptr;
}
}
I2NPMessage * Stream::CreateDataMessage (const uint8_t * payload, size_t len)
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_LastIncomingReceiveStreamID (0),
m_PendingIncomingTimer (m_Owner->GetService ()),
m_ConnTrackTimer(m_Owner->GetService()),
m_ConnsPerMinute(DEFAULT_MAX_CONNS_PER_MIN),
m_LastBanClear(i2p::util::GetMillisecondsSinceEpoch())
{
I2NPMessage * msg = NewI2NPShortMessage ();
CryptoPP::Gzip compressor;
if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)
compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL);
else
compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL);
compressor.Put (payload, len);
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload ();
htobe32buf (buf, size); // length
buf += 4;
compressor.Get (buf, size);
htobe16buf (buf + 4, m_LocalDestination.GetLocalPort ()); // source port
htobe16buf (buf + 6, m_Port); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
msg->len += size + 4;
FillI2NPMessageHeader (msg, eI2NPData);
return msg;
}
}
StreamingDestination::~StreamingDestination ()
{
for (auto& it: m_SavedPackets)
{
for (auto it1: it.second) delete it1;
it.second.clear ();
}
m_SavedPackets.clear ();
}
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)
@@ -771,7 +891,7 @@ namespace stream
it->second->HandleNextPacket (packet);
else
{
LogPrint ("Unknown stream sendStreamID=", sendStreamID);
LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID);
delete packet;
}
}
@@ -779,36 +899,104 @@ namespace stream
{
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
/* if (receiveStreamID == m_LastIncomingReceiveStreamID)
{
// already pending
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
delete packet; // drop it, because previous should be connected
return;
} */
auto incomingStream = CreateNewIncomingStream ();
incomingStream->HandleNextPacket (packet);
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;
}
}
m_LastIncomingReceiveStreamID = receiveStreamID;
// 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 rSID=", receiveStreamID);
for (auto it1: it->second)
incomingStream->HandleNextPacket (it1);
m_SavedPackets.erase (it);
}
}
// accept
if (m_Acceptor != nullptr)
m_Acceptor (incomingStream);
else
{
LogPrint ("Acceptor for incoming stream is not set");
DeleteStream (incomingStream);
LogPrint (eLogWarning, "Streaming: Acceptor for incoming stream is not set");
if (m_PendingIncomingStreams.size () < MAX_PENDING_INCOMING_BACKLOG)
{
m_PendingIncomingStreams.push_back (incomingStream);
m_PendingIncomingTimer.cancel ();
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, rSID=", receiveStreamID);
}
else
{
LogPrint (eLogWarning, "Streaming: Pending incoming streams backlog exceeds ", MAX_PENDING_INCOMING_BACKLOG);
incomingStream->Close ();
}
}
}
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
it.second->HandleNextPacket (packet);
return;
}
// TODO: should queue it up
LogPrint ("Unknown stream receiveStreamID=", receiveStreamID);
delete packet;
// save follow on packet
auto it = m_SavedPackets.find (receiveStreamID);
if (it != m_SavedPackets.end ())
it->second.push_back (packet);
else
{
m_SavedPackets[receiveStreamID] = std::list<Packet *>{ packet };
auto timer = std::make_shared<boost::asio::deadline_timer> (m_Owner->GetService ());
timer->expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT));
auto s = shared_from_this ();
timer->async_wait ([s,timer,receiveStreamID](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto it = s->m_SavedPackets.find (receiveStreamID);
if (it != s->m_SavedPackets.end ())
{
for (auto it1: it->second) delete it1;
it->second.clear ();
s->m_SavedPackets.erase (it);
}
}
});
}
}
}
}
std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this, remote, port);
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
@@ -816,7 +1004,7 @@ namespace stream
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
{
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this);
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
@@ -833,25 +1021,129 @@ namespace stream
}
}
void StreamingDestination::SetAcceptor (const Acceptor& acceptor)
{
m_Owner->GetService ().post([acceptor, this](void)
{
m_Acceptor = acceptor;
for (auto& it: m_PendingIncomingStreams)
if (it->GetStatus () == eStreamStatusOpen) // still open?
m_Acceptor (it);
m_PendingIncomingStreams.clear ();
m_PendingIncomingTimer.cancel ();
});
}
void StreamingDestination::ResetAcceptor ()
{
if (m_Acceptor) m_Acceptor (nullptr);
m_Acceptor = nullptr;
}
void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired");
for (auto& it: m_PendingIncomingStreams)
it->Close ();
m_PendingIncomingStreams.clear ();
}
}
void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
{
// unzip it
CryptoPP::Gunzip decompressor;
decompressor.Put (buf, len);
decompressor.MessageEnd();
Packet * uncompressed = new Packet;
uncompressed->offset = 0;
uncompressed->len = decompressor.MaxRetrievable ();
if (uncompressed->len <= MAX_PACKET_SIZE)
{
decompressor.Get (uncompressed->buf, uncompressed->len);
uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE);
if (uncompressed->len)
HandleNextPacket (uncompressed);
}
else
{
LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped");
delete uncompressed;
}
std::shared_ptr<I2NPMessage> StreamingDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort)
{
auto msg = NewI2NPShortMessage ();
if (!m_Gzip || len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)
m_Deflator.SetCompressionLevel (Z_NO_COMPRESSION);
else
m_Deflator.SetCompressionLevel (Z_DEFAULT_COMPRESSION);
uint8_t * buf = msg->GetPayload ();
buf += 4; // reserve for lengthlength
msg->len += 4;
size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
if (size)
{
htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, m_LocalPort); // source port
htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
msg->len += size;
msg->FillI2NPMessageHeader (eI2NPData);
}
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

@@ -11,6 +11,7 @@
#include <memory>
#include <mutex>
#include <boost/asio.hpp>
#include "Base.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "LeaseSet.h"
@@ -48,6 +49,25 @@ namespace stream
const int MAX_WINDOW_SIZE = 128;
const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 30; // in seconds
/** i2cp option for limiting inbound stremaing connections */
const char I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN[] = "maxconns";
/** default maximum connections attempts per minute per destination */
const uint32_t DEFAULT_MAX_CONNS_PER_MIN = 600;
/**
* max banned destinations per local destination
* TODO: make configurable
*/
const uint16_t MAX_BANNED_CONNS = 9999;
/**
* length of a ban in ms
* TODO: make configurable
*/
const uint64_t DEFAULT_BAN_INTERVAL = 60 * 60 * 1000;
struct Packet
{
@@ -85,7 +105,7 @@ namespace stream
enum StreamStatus
{
eStreamStatusNew,
eStreamStatusNew = 0,
eStreamStatusOpen,
eStreamStatusReset,
eStreamStatusClosing,
@@ -97,6 +117,8 @@ namespace stream
{
public:
typedef std::function<void (const boost::system::error_code& ecode)> SendHandler;
Stream (boost::asio::io_service& service, StreamingDestination& local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
@@ -105,13 +127,15 @@ namespace stream
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void HandleNextPacket (Packet * packet);
size_t Send (const uint8_t * buf, size_t len);
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
@@ -127,32 +151,32 @@ 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 ();
void SendClose ();
bool SendPacket (Packet * packet);
void SendPackets (const std::vector<Packet *>& packets);
void SendUpdatedLeaseSet ();
void SavePacket (Packet * packet);
void ProcessPacket (Packet * packet);
void ProcessAck (Packet * packet);
size_t ConcatenatePackets (uint8_t * buf, size_t len);
void UpdateCurrentRemoteLease ();
void UpdateCurrentRemoteLease (bool expired = false);
template<typename Buffer, typename ReceiveHandler>
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void HandleAckSendTimer (const boost::system::error_code& ecode);
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
private:
@@ -162,10 +186,10 @@ namespace stream
StreamStatus m_Status;
bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
i2p::data::IdentityEx m_RemoteIdentity;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
i2p::data::Lease m_CurrentRemoteLease;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
@@ -179,46 +203,75 @@ namespace stream
int m_WindowSize, m_RTT, m_RTO;
uint64_t m_LastWindowSizeIncreaseTime;
int m_NumResendAttempts;
SendHandler m_SendHandler;
};
class StreamingDestination
class StreamingDestination: public std::enable_shared_from_this<StreamingDestination>
{
public:
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
StreamingDestination (i2p::client::ClientDestination& owner, uint16_t localPort = 0):
m_Owner (owner), m_LocalPort (localPort) {};
~StreamingDestination () {};
StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0, bool gzip = true);
~StreamingDestination ();
void Start ();
void Stop ();
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void DeleteStream (std::shared_ptr<Stream> stream);
void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; };
void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; };
void SetAcceptor (const Acceptor& acceptor);
void ResetAcceptor ();
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
i2p::client::ClientDestination& GetOwner () { return m_Owner; };
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
uint16_t GetLocalPort () const { return m_LocalPort; };
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort);
/** 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:
i2p::client::ClientDestination& m_Owner;
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
uint16_t m_LocalPort;
bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
Acceptor m_Acceptor;
uint32_t m_LastIncomingReceiveStreamID;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
std::mutex m_ConnsMutex;
/** how many connections per minute did each identity have */
std::map<i2p::data::IdentHash, uint32_t> m_Conns;
boost::asio::deadline_timer m_ConnTrackTimer;
uint32_t m_ConnsPerMinute;
/** banned identities */
std::vector<i2p::data::IdentHash> m_Banned;
uint64_t m_LastBanClear;
public:
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
// for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
};
@@ -228,24 +281,23 @@ namespace stream
template<typename Buffer, typename ReceiveHandler>
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
{
if (!m_ReceiveQueue.empty ())
auto s = shared_from_this();
m_Service.post ([=](void)
{
auto s = shared_from_this();
m_Service.post ([=](void) { s->HandleReceiveTimer (
boost::asio::error::make_error_code (boost::asio::error::operation_aborted),
buffer, handler); });
}
else
{
m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
auto s = shared_from_this();
m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
{ s->HandleReceiveTimer (ecode, buffer, handler); });
}
if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset)
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0);
else
{
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
{ s->HandleReceiveTimer (ecode, buffer, handler, timeout - t); });
}
});
}
template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
{
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
if (received > 0)
@@ -259,8 +311,17 @@ namespace stream
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
}
else
{
// timeout expired
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
if (remainingTimeout <= 0)
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
else
{
// itermediate iterrupt
SendUpdatedLeaseSet (); // send our leaseset if applicable
AsyncReceive (buffer, handler, remainingTimeout);
}
}
}
}
}

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__ */

60
Timestamp.cpp Normal file
View File

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

View File

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

View File

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

View File

@@ -6,24 +6,24 @@
#include <memory>
#include <vector>
#include "Identity.h"
#include "Crypto.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "Timestamp.h"
namespace i2p
{
namespace transport
{
struct DHKeysPair // transient keys for transport sessions
{
uint8_t publicKey[256];
uint8_t privateKey[256];
};
class SignedData
{
public:
SignedData () {};
SignedData () {}
SignedData (const SignedData& other)
{
m_Stream << other.m_Stream.rdbuf ();
}
void Insert (const uint8_t * buf, size_t len)
{
m_Stream.write ((char *)buf, len);
@@ -35,9 +35,9 @@ namespace transport
m_Stream.write ((char *)&t, sizeof (T));
}
bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
{
return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
}
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
@@ -54,32 +54,41 @@ namespace transport
{
public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr),
m_NumSentBytes (0), m_NumReceivedBytes (0)
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 (m_RemoteRouter)
m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity ();
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
}
virtual ~TransportSession () { delete m_DHKeysPair; };
virtual ~TransportSession () {};
virtual void Done () = 0;
std::shared_ptr<const i2p::data::RouterInfo> GetRemoteRouter () { return m_RemoteRouter; };
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; };
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
virtual void SendI2NPMessage (I2NPMessage * msg) = 0;
virtual void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs) = 0;
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:
std::shared_ptr<const i2p::data::RouterInfo> m_RemoteRouter;
i2p::data::IdentityEx m_RemoteIdentity;
DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp;
};
}
}

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