Compare commits

..

780 Commits
2.5.1 ... 2.9.0

Author SHA1 Message Date
orignal
deca217544 don't always set port 4567 2016-08-12 11:07:00 -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
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
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
orignal
1062776762 cleanup router's tags 2016-07-28 13:24:25 -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
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
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
orignal
6a1049bfb7 override address if v6 only 2016-07-22 10:34:56 -04:00
orignal
a4112ebed2 add both v4 and v6 addresses 2016-07-22 10:16:57 -04:00
orignal
3b9b827ebf getnick doean't return error if was set before 2016-07-21 20:42:40 -04: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
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
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
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
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
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
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
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
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
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
f843d34234 Merge pull request #407 from PurpleI2P/openssl
recent changes
2016-03-09 13:41:55 -05:00
orignal
23c7340afe Merge pull request #404 from PurpleI2P/openssl
2.5.0
2016-03-04 21:35:41 -05:00
orignal
bf3615fb32 Merge pull request #401 from PurpleI2P/openssl
recent changes
2016-03-03 10:31:04 -05:00
212 changed files with 12640 additions and 4143 deletions

5
.gitignore vendored
View File

@@ -5,6 +5,10 @@ router.keys
i2p
libi2pd.so
netDb
/i2pd
/libi2pd.a
/libi2pdclient.a
# Autotools
autom4te.cache
@@ -233,3 +237,4 @@ pip-log.txt
# Sphinx
docs/_build
/androidIdea/

View File

@@ -16,13 +16,17 @@ addons:
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-program-options-dev
- libboost-regex-dev
- libboost-system-dev
- libboost-thread-dev
- libminiupnpc-dev
- libssl-dev
compiler:
- gcc
- 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

View File

@@ -5,12 +5,13 @@
#include <fstream>
#include <chrono>
#include <condition_variable>
#include <boost/lexical_cast.hpp>
#include <openssl/rand.h>
#include "Base.h"
#include "util.h"
#include "Identity.h"
#include "FS.h"
#include "Log.h"
#include "HTTP.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
@@ -24,7 +25,7 @@ namespace client
{
private:
i2p::fs::HashedStorage storage;
std::string indexPath;
std::string etagsPath, indexPath, localPath;
public:
AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") {};
@@ -34,14 +35,34 @@ namespace client
bool Init ();
int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses);
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified);
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
private:
int LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses); // returns -1 if can't open file, otherwise number of records
};
bool AddressBookFilesystemStorage::Init()
{
{
storage.SetPlace(i2p::fs::GetDataDir());
indexPath = storage.GetRoot() + i2p::fs::dirSep + "addresses.csv";
return storage.Init(i2p::data::GetBase32SubstitutionTable(), 32);
// init storage
if (storage.Init(i2p::data::GetBase32SubstitutionTable(), 32))
{
// init ETags
etagsPath = i2p::fs::StorageRootPath (storage, "etags");
if (!i2p::fs::Exists (etagsPath))
i2p::fs::CreateDirectory (etagsPath);
// init address files
indexPath = i2p::fs::StorageRootPath (storage, "addresses.csv");
localPath = i2p::fs::StorageRootPath (storage, "local.csv");
return true;
}
return false;
}
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
@@ -56,7 +77,7 @@ namespace client
f.seekg (0,std::ios::end);
size_t len = f.tellg ();
if (len < i2p::data::DEFAULT_IDENTITY_SIZE) {
LogPrint (eLogError, "Addresbook: File ", filename, " is too short: ", len);
LogPrint (eLogError, "Addressbook: File ", filename, " is too short: ", len);
return nullptr;
}
f.seekg(0, std::ios::beg);
@@ -72,7 +93,7 @@ namespace client
std::string path = storage.Path( address->GetIdentHash().ToBase32() );
std::ofstream f (path, std::ofstream::binary | std::ofstream::out);
if (!f.is_open ()) {
LogPrint (eLogError, "Addresbook: can't open file ", path);
LogPrint (eLogError, "Addressbook: can't open file ", path);
return;
}
size_t len = address->GetFullLen ();
@@ -87,24 +108,18 @@ namespace client
storage.Remove( ident.ToBase32() );
}
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = 0;
std::string s;
std::ifstream f (indexPath, std::ifstream::in); // in text mode
if (f.is_open ()) {
LogPrint(eLogInfo, "Addressbook: using index file ", indexPath);
} else {
LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath);
return 0;
}
std::ifstream f (filename, std::ifstream::in); // in text mode
if (!f) return -1;
addresses.clear ();
while (!f.eof ()) {
while (!f.eof ())
{
std::string s;
getline(f, s);
if (!s.length())
continue; // skip empty line
if (!s.length()) continue; // skip empty line
std::size_t pos = s.find(',');
if (pos != std::string::npos)
@@ -118,14 +133,34 @@ namespace client
num++;
}
}
return num;
}
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = LoadFromFile (indexPath, addresses);
if (num < 0)
{
LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath);
return 0;
}
LogPrint(eLogInfo, "Addressbook: using index file ", indexPath);
LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage");
return num;
}
int AddressBookFilesystemStorage::LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = LoadFromFile (localPath, addresses);
if (num < 0) return 0;
LogPrint (eLogInfo, "Addressbook: ", num, " local addresses loaded");
return num;
}
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
{
if (addresses.size() == 0) {
if (addresses.empty()) {
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
return 0;
}
@@ -138,7 +173,7 @@ namespace client
return 0;
}
for (auto it: addresses) {
for (const auto& it: addresses) {
f << it.first << "," << it.second.ToBase32 () << std::endl;
num++;
}
@@ -146,8 +181,30 @@ namespace client
return num;
}
void AddressBookFilesystemStorage::SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified)
{
std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt";
std::ofstream f (fname, std::ofstream::out | std::ofstream::trunc);
if (f)
{
f << etag << std::endl;
f<< lastModified << std::endl;
}
}
bool AddressBookFilesystemStorage::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified)
{
std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt";
std::ifstream f (fname, std::ofstream::in);
if (!f || f.eof ()) return false;
std::getline (f, etag);
if (f.eof ()) return false;
std::getline (f, lastModified);
return true;
}
//---------------------------------------------------------------------
AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false),
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{
}
@@ -159,13 +216,22 @@ namespace client
void AddressBook::Start ()
{
if (!m_Storage)
m_Storage = new AddressBookFilesystemStorage;
m_Storage->Init();
LoadHosts (); /* try storage, then hosts.txt, then download */
StartSubscriptions ();
StartLookups ();
}
void AddressBook::StartResolvers ()
{
LoadLocal ();
}
void AddressBook::Stop ()
{
StopLookups ();
StopSubscriptions ();
if (m_SubscriptionsUpdateTimer)
{
@@ -174,19 +240,19 @@ namespace client
}
if (m_IsDownloading)
{
LogPrint (eLogInfo, "Addresbook: subscriptions is downloading, abort");
LogPrint (eLogInfo, "Addressbook: subscriptions is downloading, abort");
for (int i = 0; i < 30; i++)
{
if (!m_IsDownloading)
{
LogPrint (eLogInfo, "Addresbook: subscriptions download complete");
LogPrint (eLogInfo, "Addressbook: subscriptions download complete");
break;
}
std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds
}
LogPrint (eLogError, "Addresbook: subscription download timeout");
LogPrint (eLogError, "Addressbook: subscription download timeout");
m_IsDownloading = false;
}
}
if (m_Storage)
{
m_Storage->Save (m_Addresses);
@@ -194,8 +260,6 @@ namespace client
m_Storage = nullptr;
}
m_DefaultSubscription = nullptr;
for (auto it: m_Subscriptions)
delete it;
m_Subscriptions.clear ();
}
@@ -219,7 +283,10 @@ namespace client
return true;
}
else
{
LookupAddress (address); // TODO:
return false;
}
}
}
// if not .b32 we assume full base64 address
@@ -271,15 +338,16 @@ namespace client
std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode
if (f.is_open ())
{
LoadHostsFromStream (f);
LoadHostsFromStream (f, false);
m_IsLoaded = true;
}
}
void AddressBook::LoadHostsFromStream (std::istream& f)
bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update)
{
std::unique_lock<std::mutex> l(m_AddressBookMutex);
int numAddresses = 0;
bool incomplete = false;
std::string s;
while (!f.eof ())
{
@@ -296,22 +364,29 @@ namespace client
std::string addr = s.substr(pos);
auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (ident->FromBase64(addr))
{
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
numAddresses++;
}
else
LogPrint (eLogError, "Addresbook: malformed address ", addr, " for ", name);
}
if (!ident->FromBase64(addr)) {
LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name);
incomplete = f.eof ();
continue;
}
numAddresses++;
if (m_Addresses.count(name) > 0)
continue; /* already exists */
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
if (is_update)
LogPrint(eLogInfo, "Addressbook: added new host: ", name);
}
else
incomplete = f.eof ();
}
LogPrint (eLogInfo, "Addresbook: ", numAddresses, " addresses processed");
LogPrint (eLogInfo, "Addressbook: ", numAddresses, " addresses processed");
if (numAddresses > 0)
{
m_IsLoaded = true;
if (!incomplete) m_IsLoaded = true;
m_Storage->Save (m_Addresses);
}
return !incomplete;
}
void AddressBook::LoadSubscriptions ()
@@ -326,29 +401,75 @@ namespace client
{
getline(f, s);
if (!s.length()) continue; // skip empty line
m_Subscriptions.push_back (new AddressBookSubscription (*this, s));
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, s));
}
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
}
else
LogPrint (eLogWarning, "Addresbook: subscriptions.txt not found in datadir");
LogPrint (eLogWarning, "Addressbook: subscriptions.txt not found in datadir");
}
else
LogPrint (eLogError, "Addressbook: subscriptions already loaded");
}
void AddressBook::DownloadComplete (bool success)
void AddressBook::LoadLocal ()
{
std::map<std::string, i2p::data::IdentHash> localAddresses;
m_Storage->LoadLocal (localAddresses);
for (const auto& it: localAddresses)
{
auto dot = it.first.find ('.');
if (dot != std::string::npos)
{
auto domain = it.first.substr (dot + 1);
auto it1 = m_Addresses.find (domain); // find domain in our addressbook
if (it1 != m_Addresses.end ())
{
auto dest = context.FindLocalDestination (it1->second);
if (dest)
{
// address is ours
std::shared_ptr<AddressResolver> resolver;
auto it2 = m_Resolvers.find (it1->second);
if (it2 != m_Resolvers.end ())
resolver = it2->second; // resolver exists
else
{
// create new resolver
resolver = std::make_shared<AddressResolver>(dest);
m_Resolvers.insert (std::make_pair(it1->second, resolver));
}
resolver->AddAddress (it.first, it.second);
}
}
}
}
}
bool AddressBook::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified)
{
if (m_Storage)
return m_Storage->GetEtag (subscription, etag, lastModified);
else
return false;
}
void AddressBook::DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified)
{
m_IsDownloading = false;
if (success && m_DefaultSubscription)
int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT;
if (success)
{
m_DefaultSubscription.reset (nullptr);
m_IsLoaded = true;
if (m_DefaultSubscription) m_DefaultSubscription = nullptr;
if (m_IsLoaded)
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT;
else
m_IsLoaded = true;
if (m_Storage) m_Storage->SaveEtag (subscription, etag, lastModified);
}
if (m_SubscriptionsUpdateTimer)
{
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(
success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT));
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(nextUpdateTimeout));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
@@ -368,7 +489,7 @@ namespace client
this, std::placeholders::_1));
}
else
LogPrint (eLogError, "Addresbook: can't start subscriptions: missing shared local destination");
LogPrint (eLogError, "Addressbook: can't start subscriptions: missing shared local destination");
}
void AddressBook::StopSubscriptions ()
@@ -393,16 +514,18 @@ namespace client
// download it from http://i2p-projekt.i2p/hosts.txt
LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription.");
if (!m_DefaultSubscription)
m_DefaultSubscription.reset (new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS));
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, DEFAULT_SUBSCRIPTION_ADDRESS);
m_IsDownloading = true;
m_DefaultSubscription->CheckSubscription ();
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
load_hosts.detach(); // TODO: use join
}
else if (!m_Subscriptions.empty ())
{
// pick random subscription
auto ind = rand () % m_Subscriptions.size();
m_IsDownloading = true;
m_Subscriptions[ind]->CheckSubscription ();
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind]));
load_hosts.detach(); // TODO: use join
}
}
else
@@ -415,168 +538,304 @@ namespace client
}
}
void AddressBook::StartLookups ()
{
auto dest = i2p::client::context.GetSharedLocalDestination ();
if (dest)
{
auto datagram = dest->GetDatagramDestination ();
if (!datagram)
datagram = dest->CreateDatagramDestination ();
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
ADDRESS_RESPONSE_DATAGRAM_PORT);
}
}
void AddressBook::StopLookups ()
{
auto dest = i2p::client::context.GetSharedLocalDestination ();
if (dest)
{
auto datagram = dest->GetDatagramDestination ();
if (datagram)
datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
}
}
void AddressBook::LookupAddress (const std::string& address)
{
const i2p::data::IdentHash * ident = nullptr;
auto dot = address.find ('.');
if (dot != std::string::npos)
ident = FindAddress (address.substr (dot + 1));
if (!ident)
{
LogPrint (eLogError, "Addressbook: Can't find domain for ", address);
return;
}
auto dest = i2p::client::context.GetSharedLocalDestination ();
if (dest)
{
auto datagram = dest->GetDatagramDestination ();
if (datagram)
{
uint32_t nonce;
RAND_bytes ((uint8_t *)&nonce, 4);
{
std::unique_lock<std::mutex> l(m_LookupsMutex);
m_Lookups[nonce] = address;
}
LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
size_t len = address.length () + 9;
uint8_t * buf = new uint8_t[len];
memset (buf, 0, 4);
htobe32buf (buf + 4, nonce);
buf[8] = address.length ();
memcpy (buf + 9, address.c_str (), address.length ());
datagram->SendDatagramTo (buf, len, *ident, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT);
delete[] buf;
}
}
}
void AddressBook::HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (len < 44)
{
LogPrint (eLogError, "Addressbook: Lookup response is too short ", len);
return;
}
uint32_t nonce = bufbe32toh (buf + 4);
LogPrint (eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce);
std::string address;
{
std::unique_lock<std::mutex> l(m_LookupsMutex);
auto it = m_Lookups.find (nonce);
if (it != m_Lookups.end ())
{
address = it->second;
m_Lookups.erase (it);
}
}
if (address.length () > 0)
{
// TODO: verify from
m_Addresses[address] = buf + 8;
}
}
AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link):
m_Book (book), m_Link (link)
{
}
void AddressBookSubscription::CheckSubscription ()
void AddressBookSubscription::CheckUpdates ()
{
std::thread load_hosts(&AddressBookSubscription::Request, this);
load_hosts.detach(); // TODO: use join
bool result = MakeRequest ();
m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified);
}
void AddressBookSubscription::Request ()
bool AddressBookSubscription::MakeRequest ()
{
i2p::http::URL url;
// must be run in separate thread
LogPrint (eLogInfo, "Addresbook: Downloading hosts database from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
bool success = false;
i2p::util::http::url u (m_Link);
i2p::data::IdentHash ident;
if (m_Book.GetIdentHash (u.host_, ident))
{
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
if (!leaseSet)
{
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident,
[&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (ident);
}
}
if (leaseSet)
{
std::stringstream request, response;
// standard header
request << "GET " << u.path_ << " HTTP/1.1\r\n"
<< "Host: " << u.host_ << "\r\n"
<< "Accept: */*\r\n"
<< "User-Agent: Wget/1.11.4\r\n"
//<< "Accept-Encoding: gzip\r\n"
<< "X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n"
<< "Connection: close\r\n";
if (m_Etag.length () > 0) // etag
request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n";
if (m_LastModified.length () > 0) // if-modfief-since
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n";
request << "\r\n"; // end of header
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_);
stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ());
uint8_t buf[4096];
bool end = false;
while (!end)
{
stream->AsyncReceive (boost::asio::buffer (buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (bytes_transferred)
response.write ((char *)buf, bytes_transferred);
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
end = true;
newDataReceived.notify_all ();
},
30); // wait for 30 seconds
std::unique_lock<std::mutex> l(newDataReceivedMutex);
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
LogPrint (eLogError, "Addresbook: subscriptions request timeout expired");
}
// process remaining buffer
while (size_t len = stream->ReadSome (buf, 4096))
response.write ((char *)buf, len);
// parse response
std::string version;
response >> version; // HTTP version
int status = 0;
response >> status; // status
if (status == 200) // OK
{
bool isChunked = false, isGzip = false;
std::string header, statusMessage;
std::getline (response, statusMessage);
// read until new line meaning end of header
while (!response.eof () && header != "\r")
{
std::getline (response, header);
auto colon = header.find (':');
if (colon != std::string::npos)
{
std::string field = header.substr (0, colon);
boost::to_lower (field); // field are not case-sensitive
colon++;
header.resize (header.length () - 1); // delete \r
if (field == i2p::util::http::ETAG)
m_Etag = header.substr (colon + 1);
else if (field == i2p::util::http::LAST_MODIFIED)
m_LastModified = header.substr (colon + 1);
else if (field == i2p::util::http::TRANSFER_ENCODING)
isChunked = !header.compare (colon + 1, std::string::npos, "chunked");
else if (field == i2p::util::http::CONTENT_ENCODING)
isGzip = !header.compare (colon + 1, std::string::npos, "gzip") ||
!header.compare (colon + 1, std::string::npos, "x-i2p-gzip");
}
}
LogPrint (eLogInfo, "Addressbook: ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
if (!response.eof ())
{
success = true;
if (!isChunked)
success = ProcessResponse (response, isGzip);
else
{
// merge chunks
std::stringstream merged;
i2p::util::http::MergeChunkedResponse (response, merged);
success = ProcessResponse (merged, isGzip);
}
}
}
else if (status == 304)
{
success = true;
LogPrint (eLogInfo, "Addressbook: no updates from ", m_Link);
}
else
LogPrint (eLogWarning, "Adressbook: HTTP response ", status);
}
else
LogPrint (eLogError, "Addressbook: address ", u.host_, " not found");
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link);
if (!url.parse(m_Link)) {
LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link);
return false;
}
else
LogPrint (eLogError, "Addressbook: Can't resolve ", u.host_);
if (!success)
LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed");
m_Book.DownloadComplete (success);
}
bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip)
{
if (isGzip)
if (!m_Book.GetIdentHash (url.host, m_Ident)) {
LogPrint (eLogError, "Addressbook: Can't resolve ", url.host);
return false;
}
/* this code block still needs some love */
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident);
if (!leaseSet)
{
std::stringstream uncompressed;
i2p::data::GzipInflator inflator;
inflator.Inflate (s, uncompressed);
if (!uncompressed.fail ())
m_Book.LoadHostsFromStream (uncompressed);
else
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident,
[&newDataReceived, &leaseSet, &newDataReceivedMutex](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
std::unique_lock<std::mutex> l1(newDataReceivedMutex);
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident, false); // don't notify, because we know it already
return false;
}
else
m_Book.LoadHostsFromStream (s);
return true;
}
}
if (!leaseSet) {
/* still no leaseset found */
LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found");
return false;
}
if (m_Etag.empty() && m_LastModified.empty()) {
m_Book.GetEtag (m_Ident, m_Etag, m_LastModified);
LogPrint (eLogDebug, "Addressbook: loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
}
/* save url parts for later use */
std::string dest_host = url.host;
int dest_port = url.port ? url.port : 80;
/* create http request & send it */
i2p::http::HTTPReq req;
req.add_header("Host", dest_host);
req.add_header("User-Agent", "Wget/1.11.4");
req.add_header("Connection", "close");
if (!m_Etag.empty())
req.add_header("If-None-Match", m_Etag);
if (!m_LastModified.empty())
req.add_header("If-Modified-Since", m_LastModified);
/* convert url to relative */
url.schema = "";
url.host = "";
req.uri = url.to_string();
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
std::string request = req.to_string();
stream->Send ((const uint8_t *) request.data(), request.length());
/* read response */
std::string response;
uint8_t recv_buf[4096];
bool end = false;
while (!end) {
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (bytes_transferred)
response.append ((char *)recv_buf, bytes_transferred);
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
end = true;
newDataReceived.notify_all ();
},
30); // wait for 30 seconds
std::unique_lock<std::mutex> l(newDataReceivedMutex);
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired");
}
// process remaining buffer
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) {
response.append ((char *)recv_buf, len);
}
/* parse response */
i2p::http::HTTPRes res;
int res_head_len = res.parse(response);
if (res_head_len < 0) {
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host);
return false;
}
if (res_head_len == 0) {
LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout");
return false;
}
/* assert: res_head_len > 0 */
response.erase(0, res_head_len);
if (res.code == 304) {
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304");
return false;
}
if (res.code != 200) {
LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code);
return false;
}
int len = res.content_length();
if (response.empty()) {
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes");
return false;
}
if (len > 0 && len != (int) response.length()) {
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes");
return false;
}
/* assert: res.code == 200 */
auto it = res.headers.find("ETag");
if (it != res.headers.end()) {
m_Etag = it->second;
}
it = res.headers.find("If-Modified-Since");
if (it != res.headers.end()) {
m_LastModified = it->second;
}
if (res.is_chunked()) {
std::stringstream in(response), out;
i2p::http::MergeChunkedResponse (in, out);
response = out.str();
} else if (res.is_gzipped()) {
std::stringstream out;
i2p::data::GzipInflator inflator;
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out);
if (out.fail()) {
LogPrint(eLogError, "Addressbook: can't gunzip http response");
return false;
}
response = out.str();
}
std::stringstream ss(response);
LogPrint (eLogInfo, "Addressbook: got update from ", dest_host);
m_Book.LoadHostsFromStream (ss, true);
return true;
}
AddressResolver::AddressResolver (std::shared_ptr<ClientDestination> destination):
m_LocalDestination (destination)
{
if (m_LocalDestination)
{
auto datagram = m_LocalDestination->GetDatagramDestination ();
if (!datagram)
datagram = m_LocalDestination->CreateDatagramDestination ();
datagram->SetReceiver (std::bind (&AddressResolver::HandleRequest, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
ADDRESS_RESOLVER_DATAGRAM_PORT);
}
}
AddressResolver::~AddressResolver ()
{
if (m_LocalDestination)
{
auto datagram = m_LocalDestination->GetDatagramDestination ();
if (datagram)
datagram->ResetReceiver (ADDRESS_RESOLVER_DATAGRAM_PORT);
}
}
void AddressResolver::HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (len < 9 || len < buf[8] + 9U)
{
LogPrint (eLogError, "Addressbook: Address request is too short ", len);
return;
}
// read requested address
uint8_t l = buf[8];
char address[255];
memcpy (address, buf + 9, l);
address[l] = 0;
LogPrint (eLogDebug, "Addressbook: Address request ", address);
// send response
uint8_t response[44];
memset (response, 0, 4); // reserved
memcpy (response + 4, buf + 4, 4); // nonce
auto it = m_LocalAddresses.find (address); // address lookup
if (it != m_LocalAddresses.end ())
memcpy (response + 8, it->second, 32); // ident
else
memset (response + 8, 0, 32); // not found
memset (response + 40, 0, 4); // set expiration time to zero
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort);
}
void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident)
{
m_LocalAddresses[name] = ident;
}
}
}

View File

@@ -12,17 +12,25 @@
#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";
#ifdef MESHNET
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://i42ofzetmgicvui5sshinfckpijix2udewbam4sjo6x5fbukltia.b32.i2p/hosts.txt";
#else
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
#endif
const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes
const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours)
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"); }
@@ -37,10 +45,15 @@ namespace client
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:
@@ -48,18 +61,23 @@ namespace client
AddressBook ();
~AddressBook ();
void Start ();
void StartResolvers ();
void Stop ();
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address);
const i2p::data::IdentHash * FindAddress (const std::string& address);
void LookupAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success);
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(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 ();
@@ -67,17 +85,25 @@ namespace client
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;
std::unique_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
};
@@ -86,17 +112,36 @@ namespace client
public:
AddressBookSubscription (AddressBook& book, const std::string& link);
void CheckSubscription ();
void CheckUpdates ();
private:
void Request ();
bool ProcessResponse (std::stringstream& s, bool isGzip = false);
bool MakeRequest ();
private:
AddressBook& m_Book;
std::string m_Link, m_Etag, m_LastModified;
i2p::data::IdentHash m_Ident;
// m_Etag must be surrounded by ""
};
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;
};
}
}

108
BOB.cpp
View File

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

4
BOB.h
View File

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

108
Base.cpp
View File

@@ -1,5 +1,6 @@
#include <stdlib.h>
#include "Log.h"
#include <string.h>
#include "Base.h"
namespace i2p
@@ -8,8 +9,8 @@ namespace data
{
static const char T32[32] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'k', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 't', 't', 'u', 'v', 'w', 'x',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '2', '3', '4', '5', '6', '7',
};
@@ -284,107 +285,6 @@ namespace data
}
return ret;
}
GzipInflator::GzipInflator (): m_IsDirty (false)
{
memset (&m_Inflator, 0, sizeof (m_Inflator));
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
}
GzipInflator::~GzipInflator ()
{
inflateEnd (&m_Inflator);
}
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) inflateReset (&m_Inflator);
m_IsDirty = true;
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
m_Inflator.next_out = out;
m_Inflator.avail_out = outLen;
int err;
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
return outLen - m_Inflator.avail_out;
else
{
LogPrint (eLogError, "Decompression error ", err);
return 0;
}
}
bool GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& s)
{
m_IsDirty = true;
uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE];
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
int ret;
do
{
m_Inflator.next_out = out;
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
ret = inflate (&m_Inflator, Z_NO_FLUSH);
if (ret < 0)
{
LogPrint (eLogError, "Decompression error ", ret);
inflateEnd (&m_Inflator);
s.setstate(std::ios_base::failbit);
break;
}
else
s.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
}
while (!m_Inflator.avail_out); // more data to read
delete[] out;
return ret == Z_STREAM_END || ret < 0;
}
void GzipInflator::Inflate (std::istream& in, std::ostream& out)
{
uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE];
while (!in.eof ())
{
in.read ((char *)buf, GZIP_CHUNK_SIZE);
Inflate (buf, in.gcount (), out);
}
delete[] buf;
}
GzipDeflator::GzipDeflator (): m_IsDirty (false)
{
memset (&m_Deflator, 0, sizeof (m_Deflator));
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
}
GzipDeflator::~GzipDeflator ()
{
deflateEnd (&m_Deflator);
}
void GzipDeflator::SetCompressionLevel (int level)
{
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
}
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) deflateReset (&m_Deflator);
m_IsDirty = true;
m_Deflator.next_in = const_cast<uint8_t *>(in);
m_Deflator.avail_in = inLen;
m_Deflator.next_out = out;
m_Deflator.avail_out = outLen;
int err;
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
return outLen - m_Deflator.avail_out;
else
{
LogPrint (eLogError, "Compression error ", err);
return 0;
}
}
}
}

117
Base.h
View File

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

95
ChangeLog Normal file
View File

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

View File

@@ -6,6 +6,7 @@
#include "FS.h"
#include "Log.h"
#include "Identity.h"
#include "util.h"
#include "ClientContext.h"
namespace i2p
@@ -16,7 +17,7 @@ namespace client
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
m_BOBCommandChannel (nullptr)
m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr)
{
}
@@ -26,6 +27,7 @@ namespace client
delete m_SocksProxy;
delete m_SamBridge;
delete m_BOBCommandChannel;
delete m_I2CPServer;
}
void ClientContext::Start ()
@@ -52,8 +54,12 @@ namespace client
LoadPrivateKeys (keys, httpProxyKeys);
localDestination = CreateNewLocalDestination (keys, false);
}
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
m_HttpProxy->Start();
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());
}
}
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
@@ -70,8 +76,12 @@ namespace client
LoadPrivateKeys (keys, socksProxyKeys);
localDestination = CreateNewLocalDestination (keys, false);
}
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
m_SocksProxy->Start();
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
@@ -83,8 +93,12 @@ namespace client
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
m_SamBridge = new SAMBridge (samAddr, samPort);
m_SamBridge->Start ();
try {
m_SamBridge = new SAMBridge (samAddr, samPort);
m_SamBridge->Start ();
} catch (std::exception& e) {
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
}
}
// BOB
@@ -93,9 +107,33 @@ namespace client
std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr);
uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort);
LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort);
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
m_BOBCommandChannel->Start ();
try {
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
m_BOBCommandChannel->Start ();
} catch (std::exception& e) {
LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what());
}
}
// I2CP
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
if (i2cp)
{
std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr);
uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort);
LogPrint(eLogInfo, "Clients: starting I2CP at ", i2cpAddr, ":", i2cpPort);
try
{
m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort);
m_I2CPServer->Start ();
}
catch (std::exception& e)
{
LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what());
}
}
m_AddressBook.StartResolvers ();
}
void ClientContext::Stop ()
@@ -146,13 +184,26 @@ namespace client
m_BOBCommandChannel = nullptr;
}
if (m_I2CPServer)
{
LogPrint(eLogInfo, "Clients: stopping I2CP");
m_I2CPServer->Stop ();
delete m_I2CPServer;
m_I2CPServer = nullptr;
}
LogPrint(eLogInfo, "Clients: stopping AddressBook");
m_AddressBook.Stop ();
for (auto it: m_Destinations)
for (auto& it: m_Destinations)
it.second->Stop ();
m_Destinations.clear ();
m_SharedLocalDestination = nullptr;
}
void ClientContext::ReloadConfig ()
{
ReadTunnels (); // TODO: it reads new tunnels only, should be implemented better
}
void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
{
@@ -242,7 +293,7 @@ namespace client
template<typename Section, typename Type>
std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const
{
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
}
template<typename Section>
@@ -259,8 +310,15 @@ namespace client
{
boost::property_tree::ptree pt;
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
if (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");
}
}
LogPrint(eLogDebug, "FS: tunnels config file: ", tunConf);
try
{
@@ -303,11 +361,14 @@ namespace client
localDestination = CreateNewLocalDestination (k, false, &options);
}
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
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 with port ", port, " already exists");
numClientTunnels++;
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists");
}
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC)
{
@@ -357,12 +418,15 @@ namespace client
serverTunnel->SetAccessList (idents);
}
if (m_ServerTunnels.insert (std::make_pair (
std::make_tuple (localDestination->GetIdentHash (), inPort),
std::make_pair (localDestination->GetIdentHash (), inPort),
std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
{
serverTunnel->Start ();
numServerTunnels++;
}
else
LogPrint (eLogError, "Clients: 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, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf);

View File

@@ -2,15 +2,17 @@
#define CLIENT_CONTEXT_H__
#include <map>
#include <tuple>
#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"
namespace i2p
@@ -48,6 +50,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
@@ -78,10 +82,11 @@ 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<std::tuple<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->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
SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel;
I2CPServer * m_I2CPServer;
public:
// for HTTP

View File

@@ -26,113 +26,56 @@ namespace config {
options_description m_OptionsDesc;
variables_map m_Options;
/* list of renamed options */
std::map<std::string, std::string> remapped_options = {
{ "tunnelscfg", "tunconf" },
{ "v6", "ipv6" },
{ "httpaddress", "http.address" },
{ "httpport", "http.port" },
{ "httpproxyaddress", "httpproxy.address" },
{ "httpproxyport", "httpproxy.port" },
{ "socksproxyaddress", "socksproxy.address" },
{ "socksproxyport", "socksproxy.port" },
{ "samaddress", "sam.address" },
{ "samport", "sam.port" },
{ "bobaddress", "bob.address" },
{ "bobport", "bob.port" },
{ "i2pcontroladdress", "i2pcontrol.address" },
{ "i2pcontroladdress", "i2pcontrol.port" },
{ "proxykeys", "httpproxy.keys" },
};
/* list of options, that loose their argument and become simple switch */
std::set<std::string> boolean_options = {
"daemon", "floodfill", "notransit", "service", "ipv6"
};
/* this function is a solid piece of shit, remove it after 2.6.0 */
std::pair<std::string, std::string> old_syntax_parser(const std::string& s) {
std::string name = "";
std::string value = "";
std::size_t pos = 0;
/* shortcuts -- -h */
if (s.length() == 2 && s.at(0) == '-' && s.at(1) != '-')
return make_pair(s.substr(1), "");
/* old-style -- -log, /log, etc */
if (s.at(0) == '/' || (s.at(0) == '-' && s.at(1) != '-')) {
if ((pos = s.find_first_of("= ")) != std::string::npos) {
name = s.substr(1, pos - 1);
value = s.substr(pos + 1);
} else {
name = s.substr(1, pos);
value = "";
}
if (boolean_options.count(name) > 0 && value != "")
std::cerr << "args: don't give an argument to switch option: " << s << std::endl;
if (m_OptionsDesc.find_nothrow(name, false)) {
std::cerr << "args: option " << s << " style is DEPRECATED, use --" << name << " instead" << std::endl;
return std::make_pair(name, value);
}
if (remapped_options.count(name) > 0) {
name = remapped_options[name];
std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl;
return std::make_pair(name, value);
} /* else */
}
/* long options -- --help */
if (s.substr(0, 2) == "--") {
if ((pos = s.find_first_of("= ")) != std::string::npos) {
name = s.substr(2, pos - 2);
value = s.substr(pos + 1);
} else {
name = s.substr(2, pos);
value = "";
}
if (boolean_options.count(name) > 0 && value != "") {
std::cerr << "args: don't give an argument to switch option: " << s << std::endl;
value = "";
}
if (m_OptionsDesc.find_nothrow(name, false))
return std::make_pair(name, value);
if (remapped_options.count(name) > 0) {
name = remapped_options[name];
std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl;
return std::make_pair(name, value);
} /* else */
}
std::cerr << "args: unknown option -- " << s << std::endl;
return std::make_pair("", "");
}
void Init() {
bool nat = true;
#ifdef MESHNET
nat = false;
#endif
options_description general("General options");
general.add_options()
("help", "Show this message")
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)")
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.cfg or /var/lib/i2pd/tunnels.cfg)")
("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 (stdout if not set, file - otherwise, for compatibility)")
("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(nat), "should we assume we are behind NAT?")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", value<bool>()->zero_tokens()->default_value(true), "Enable communication through ipv4")
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
("daemon", value<bool>()->zero_tokens()->default_value(false), "Router will go to background after start")
("service", value<bool>()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'")
("notransit", value<bool>()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup")
("floodfill", value<bool>()->zero_tokens()->default_value(false), "Router will be floodfill")
("bandwidth", value<char>()->default_value('-'), "Bandwidth limiting: L - 32kbps, O - 256Kbps, P - unlimited")
("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.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");
@@ -167,6 +110,13 @@ namespace config {
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
;
options_description i2cp("I2CP options");
i2cp.add_options()
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
;
options_description i2pcontrol("I2PControl options");
i2pcontrol.add_options()
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
@@ -177,14 +127,52 @@ namespace config {
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
;
bool upnp_default = false;
#if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
upnp_default = true; // enable UPNP for windows GUI and android by default
#endif
options_description upnp("UPnP options");
upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list")
;
options_description precomputation("Precomputation options");
precomputation.add_options()
("precomputation.elgamal",
#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.file", value<std::string>()->default_value(""), "Path to .su3 file")
;
options_description trust("Trust options");
trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
("trust.hidden", value<bool>()->default_value(false), "should we hide our router from other routers?");
m_OptionsDesc
.add(general)
.add(limits)
.add(httpserver)
.add(httpproxy)
.add(socksproxy)
.add(sam)
.add(bob)
.add(i2cp)
.add(i2pcontrol)
.add(upnp)
.add(precomputation)
.add(reseed)
.add(trust)
;
}
@@ -193,7 +181,7 @@ namespace config {
auto style = boost::program_options::command_line_style::unix_style
| boost::program_options::command_line_style::allow_long_disguise;
style &= ~ boost::program_options::command_line_style::allow_guessing;
store(parse_command_line(argc, argv, m_OptionsDesc, style, old_syntax_parser), m_Options);
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
} catch (boost::program_options::error& e) {
std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE);

View File

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

View File

@@ -3,10 +3,8 @@
#include <vector>
#include <mutex>
#include <memory>
#include <openssl/sha.h>
#include <openssl/dh.h>
#include <openssl/md5.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
#include "TunnelBase.h"
#include <openssl/ssl.h>
@@ -146,9 +144,87 @@ namespace crypto
}
// 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)
@@ -169,7 +245,23 @@ namespace crypto
{
if (m_DH->priv_key) { BN_free (m_DH->priv_key); m_DH->priv_key = NULL; };
if (m_DH->pub_key) { BN_free (m_DH->pub_key); m_DH->pub_key = NULL; };
DH_generate_key (m_DH);
#if !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;
@@ -200,11 +292,20 @@ namespace crypto
ctx = BN_CTX_new ();
// select random k
BIGNUM * k = BN_new ();
BN_rand_range (k, elgp);
if (BN_is_zero (k)) BN_one (k);
// caulculate a
a = BN_new ();
BN_mod_exp (a, elgg, k, elgp, ctx);
#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
@@ -279,6 +380,14 @@ namespace crypto
{
#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);
@@ -286,11 +395,6 @@ namespace crypto
bn2buf (p, pub, 256);
BN_free (p);
BN_CTX_free (ctx);
#else
DHKeys dh;
dh.GenerateKeys (priv, pub);
#endif
}
// HMAC
@@ -695,17 +799,38 @@ namespace crypto
}
}*/
void InitCrypto ()
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

@@ -7,7 +7,11 @@
#include <openssl/dh.h>
#include <openssl/aes.h>
#include <openssl/dsa.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Base.h"
#include "Tag.h"
namespace i2p
{
@@ -273,7 +277,7 @@ namespace crypto
#endif
};
void InitCrypto ();
void InitCrypto (bool precomputation);
void TerminateCrypto ();
}
}

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

@@ -13,6 +13,7 @@
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "HTTP.h"
#include "NetDb.h"
#include "Garlic.h"
#include "Streaming.h"
@@ -21,10 +22,8 @@
#include "I2PControl.h"
#include "ClientContext.h"
#include "Crypto.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
#include "util.h"
namespace i2p
{
@@ -36,18 +35,15 @@ namespace i2p
Daemon_Singleton_Private() {};
~Daemon_Singleton_Private() {};
std::unique_ptr<i2p::util::HTTPServer> httpServer;
std::unique_ptr<i2p::http::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
#ifdef USE_UPNP
i2p::transport::UPnP m_UPnP;
#endif
std::unique_ptr<i2p::transport::UPnP> UPnP;
};
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
Daemon_Singleton::~Daemon_Singleton() {
delete &d;
};
}
bool Daemon_Singleton::IsService () const
{
@@ -69,123 +65,185 @@ namespace i2p
i2p::fs::Init();
datadir = i2p::fs::GetDataDir();
if (config == "")
// TODO: drop old name detection in v2.8.0
if (config == "")
{
config = i2p::fs::DataDirPath("i2p.conf");
// use i2p.cong only if exists
if (!i2p::fs::Exists (config)) config = ""; /* reset */
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::crypto::InitCrypto ();
i2p::context.Init ();
i2p::config::GetOption("daemon", isDaemon);
// TODO: move log init here
std::string logs = ""; i2p::config::GetOption("log", logs);
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
/* 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);
i2p::context.Init ();
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"))
{
{
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
std::string host; i2p::config::GetOption("host", host);
if (!i2p::config::IsDefault("host"))
{
LogPrint(eLogInfo, "Daemon: setting address for incoming connections to ", host);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
}
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetAcceptsTunnels (!transit);
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
SetMaxNumTransitTunnels (transitTunnels);
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
char bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
if (isFloodfill)
{
if (isFloodfill) {
LogPrint(eLogInfo, "Daemon: router will be floodfill");
i2p::context.SetFloodfill (true);
}
else
} else {
i2p::context.SetFloodfill (false);
if (bandwidth != '-')
{
LogPrint(eLogInfo, "Daemon: bandwidth set to ", bandwidth);
if (bandwidth > 'O')
i2p::context.SetExtraBandwidth ();
else if (bandwidth > 'L')
i2p::context.SetHighBandwidth ();
else
i2p::context.SetLowBandwidth ();
}
/* 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.SetExtraBandwidth ();
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1);
}
else
{
LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'");
i2p::context.SetLowBandwidth ();
}
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
}
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);
if (fam.length() > 0)
{
LogPrint(eLogInfo, "Daemon: setting restricted routes to use family ", fam);
i2p::transport::transports.RestrictRoutes({fam});
} else
LogPrint(eLogError, "Daemon: no family specified for restricted routes");
}
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
if (hidden)
{
LogPrint(eLogInfo, "Daemon: using hidden mode");
i2p::data::netdb.SetHidden(true);
}
return true;
}
bool Daemon_Singleton::start()
{
std::string logs = ""; i2p::config::GetOption("log", logs);
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
if (isDaemon && (logs == "" || logs == "stdout"))
logs = "file";
if (logs == "file") {
if (logfile == "")
logfile = i2p::fs::DataDirPath("i2pd.log");
StartLog (logfile);
} else {
// use stdout
StartLog ("");
}
SetLogLevel(loglevel);
bool http; i2p::config::GetOption("http.enabled", http);
if (http) {
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
d.httpServer = std::unique_ptr<i2p::util::HTTPServer>(new i2p::util::HTTPServer(httpAddr, httpPort));
d.httpServer->Start();
}
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: starting UPnP");
d.m_UPnP.Start ();
#endif
LogPrint(eLogInfo, "Daemon: starting Transports");
i2p::transport::transports.Start();
bool upnp; i2p::config::GetOption("upnp.enabled", upnp);
if (upnp) {
d.UPnP = std::unique_ptr<i2p::transport::UPnP>(new i2p::transport::UPnP);
d.UPnP->Start ();
}
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
bool ssu; i2p::config::GetOption("ssu", ssu);
LogPrint(eLogInfo, "Daemon: starting Transports");
if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled");
i2p::transport::transports.Start(ntcp, ssu);
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) {
LogPrint(eLogInfo, "Daemon: Transports started");
} else {
LogPrint(eLogError, "Daemon: failed to start Transports");
/** shut down netdb right away */
i2p::transport::transports.Stop();
i2p::data::netdb.Stop();
return false;
}
bool http; i2p::config::GetOption("http.enabled", http);
if (http) {
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
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();
@@ -201,6 +259,7 @@ namespace i2p
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
d.m_I2PControlService->Start ();
}
return true;
}
@@ -211,10 +270,12 @@ namespace i2p
i2p::client::context.Stop();
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
i2p::tunnel::tunnels.Stop();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: stopping UPnP");
d.m_UPnP.Stop ();
#endif
if (d.UPnP) {
d.UPnP->Stop ();
d.UPnP = nullptr;
}
LogPrint(eLogInfo, "Daemon: stopping Transports");
i2p::transport::transports.Stop();
LogPrint(eLogInfo, "Daemon: stopping NetDB");
@@ -231,9 +292,8 @@ namespace i2p
d.m_I2PControlService = nullptr;
}
i2p::crypto::TerminateCrypto ();
StopLog ();
return true;
}
}
}
}

View File

@@ -1,14 +1,9 @@
#ifndef DAEMON_H__
#define DAEMON_H__
#include <memory>
#include <string>
#ifdef _WIN32
#define Daemon i2p::util::DaemonWin32::Instance()
#else
#define Daemon i2p::util::DaemonLinux::Instance()
#endif
namespace i2p
{
namespace util
@@ -22,9 +17,7 @@ namespace i2p
virtual bool stop();
virtual void run () {};
bool isLogging;
bool isDaemon;
bool running;
protected:
@@ -38,7 +31,36 @@ namespace i2p
Daemon_Singleton_Private &d;
};
#ifdef _WIN32
#if defined(QT_GUI_LIB) // check if QT
#define Daemon i2p::util::DaemonQT::Instance()
// dummy, invoked from RunQT
class DaemonQT: public i2p::util::Daemon_Singleton
{
public:
static DaemonQT& Instance()
{
static DaemonQT instance;
return instance;
}
};
#elif defined(ANDROID)
#define Daemon i2p::util::DaemonAndroid::Instance()
// dummy, invoked from android/jni/DaemonAndroid.*
class DaemonAndroid: public i2p::util::Daemon_Singleton
{
public:
static DaemonAndroid& Instance()
{
static DaemonAndroid instance;
return instance;
}
};
#elif defined(_WIN32)
#define Daemon i2p::util::DaemonWin32::Instance()
class DaemonWin32 : public Daemon_Singleton
{
public:
@@ -50,26 +72,32 @@ namespace i2p
bool init(int argc, char* argv[]);
bool start();
bool stop();
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;
}
bool start();
bool stop();
; void run ();
bool start();
bool stop();
void run ();
private:
std::string pidfile;
int pidFH;
private:
std::string pidfile;
int pidFH;
public:
int gracefullShutdownInterval; // in seconds
};
#endif

View File

@@ -12,19 +12,31 @@
#include "Config.h"
#include "FS.h"
#include "Log.h"
#include "RouterContext.h"
#include "ClientContext.h"
void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
ReopenLogFile ();
break;
case SIGABRT:
case SIGTERM:
case SIGINT:
Daemon.running = 0; // Exit loop
case SIGHUP:
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
i2p::log::Logger().Reopen ();
i2p::client::context.ReloadConfig();
break;
case SIGINT:
if (i2p::context.AcceptsTunnels () && !Daemon.gracefullShutdownInterval)
{
i2p::context.SetAcceptsTunnels (false);
Daemon.gracefullShutdownInterval = 10*60; // 10 minutes
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefullShutdownInterval, " seconds");
}
else
Daemon.running = 0;
break;
case SIGABRT:
case SIGTERM:
Daemon.running = 0; // Exit loop
break;
}
}
@@ -35,7 +47,7 @@ namespace i2p
{
bool DaemonLinux::start()
{
if (isDaemon == 1)
if (isDaemon)
{
pid_t pid;
pid = fork();
@@ -63,13 +75,10 @@ namespace i2p
return false;
}
// 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);
// point std{in,out,err} descriptors to /dev/null
stdin = freopen("/dev/null", "r", stdin);
stdout = freopen("/dev/null", "w", stdout);
stderr = freopen("/dev/null", "w", stderr);
}
// Pidfile
@@ -99,6 +108,7 @@ namespace i2p
return false;
}
}
gracefullShutdownInterval = 0; // not specified
// Signal handler
struct sigaction sa;
@@ -125,6 +135,15 @@ namespace i2p
while (running)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
if (gracefullShutdownInterval)
{
gracefullShutdownInterval--; // - 1 second
if (gracefullShutdownInterval <= 0)
{
LogPrint(eLogInfo, "Graceful shutdown");
return;
}
}
}
}
}

View File

@@ -1,3 +1,5 @@
#include <thread>
#include <clocale>
#include "Config.h"
#include "Daemon.h"
#include "util.h"
@@ -5,7 +7,10 @@
#ifdef _WIN32
#include "Win32/Win32App.h"
#include "Win32/Win32Service.h"
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#endif
namespace i2p
{
@@ -16,9 +21,48 @@ namespace i2p
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
return Daemon_Singleton::init(argc, argv);
}
setlocale(LC_ALL, "Russian");
if (!Daemon_Singleton::init(argc, argv))
return false;
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
if (serviceControl == "install")
{
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
return false;
}
else if (serviceControl == "remove")
{
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
UninstallService(SERVICE_NAME);
return false;
}
if (isDaemon)
{
LogPrint(eLogDebug, "Daemon: running as service");
I2PService service(SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
return false;
}
return false;
}
else
LogPrint(eLogDebug, "Daemon: running as user");
return true;
}
bool DaemonWin32::start()
{
@@ -26,29 +70,44 @@ namespace i2p
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
if (!i2p::win32::StartWin32App ()) return false;
// override log
#ifdef WIN32_APP
if (!i2p::win32::StartWin32App ()) return false;
// override log
i2p::config::SetOption("log", std::string ("file"));
#endif
bool ret = Daemon_Singleton::start();
if (ret && IsLogToFile ())
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 ()
{
i2p::win32::RunWin32App ();
}
void DaemonWin32::run ()
{
#ifdef WIN32_APP
i2p::win32::RunWin32App ();
#else
while (running)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
}
#endif
}
}
}

View File

@@ -1,7 +1,6 @@
#include <string.h>
#include <vector>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "TunnelBase.h"
#include "RouterContext.h"

View File

@@ -1,30 +1,23 @@
#include <algorithm>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "FS.h"
#include "Crypto.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "Destination.h"
#include "util.h"
namespace i2p
{
namespace client
{
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service),
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
{
if (m_IsPublic)
PersistTemporaryKeys ();
else
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
@@ -35,28 +28,30 @@ namespace client
{
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
if (len > 0)
{
int len = i2p::util::lexical_cast<int>(it->second, inboundTunnelLen);
if (len >= 0)
{
inboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", len);
inboundTunnelLen = len;
}
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", inboundTunnelLen);
}
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
if (len > 0)
int len = i2p::util::lexical_cast<int>(it->second, outboundTunnelLen);
if (len >= 0)
{
outboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", len);
outboundTunnelLen = len;
}
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", outboundTunnelLen);
}
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = boost::lexical_cast<int>(it->second);
int quantity = i2p::util::lexical_cast<int>(it->second, inboundTunnelsQuantity);
if (quantity > 0)
{
inboundTunnelsQuantity = quantity;
@@ -66,7 +61,7 @@ namespace client
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = boost::lexical_cast<int>(it->second);
int quantity = i2p::util::lexical_cast<int>(it->second, outboundTunnelsQuantity);
if (quantity > 0)
{
outboundTunnelsQuantity = quantity;
@@ -76,11 +71,11 @@ namespace client
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
if (it != params->end ())
{
int tagsToSend = boost::lexical_cast<int>(it->second);
int tagsToSend = i2p::util::lexical_cast<int>(it->second, numTags);
if (tagsToSend > 0)
{
numTags = tagsToSend;
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
}
}
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
@@ -102,24 +97,20 @@ namespace client
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers);
if (m_IsPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
}
ClientDestination::~ClientDestination ()
LeaseSetDestination::~LeaseSetDestination ()
{
if (m_IsRunning)
Stop ();
for (auto it: m_LeaseSetRequests)
for (auto& it: m_LeaseSetRequests)
if (it.second->requestComplete) it.second->requestComplete (nullptr);
m_LeaseSetRequests.clear ();
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
if (m_DatagramDestination)
delete m_DatagramDestination;
}
void ClientDestination::Run ()
void LeaseSetDestination::Run ()
{
while (m_IsRunning)
{
@@ -134,26 +125,25 @@ namespace client
}
}
void ClientDestination::Start ()
bool LeaseSetDestination::Start ()
{
if (!m_IsRunning)
{
m_IsRunning = true;
m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&ClientDestination::Run, shared_from_this ()));
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this ()); // TODO:
m_StreamingDestination->Start ();
for (auto it: m_StreamingDestinationsByPorts)
it.second->Start ();
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
return true;
}
else
return false;
}
void ClientDestination::Stop ()
bool LeaseSetDestination::Stop ()
{
if (m_IsRunning)
{
@@ -161,16 +151,6 @@ namespace client
m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
m_IsRunning = false;
m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto it: m_StreamingDestinationsByPorts)
it.second->Stop ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
if (m_Pool)
{
m_Pool->SetLocalDestination (nullptr);
@@ -183,10 +163,13 @@ namespace client
delete m_Thread;
m_Thread = 0;
}
return true;
}
else
return false;
}
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
@@ -209,7 +192,7 @@ namespace client
return nullptr;
}
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::GetLeaseSet ()
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
{
if (!m_Pool) return nullptr;
if (!m_LeaseSet)
@@ -217,12 +200,24 @@ namespace client
return m_LeaseSet;
}
void ClientDestination::UpdateLeaseSet ()
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
{
m_LeaseSet.reset (new i2p::data::LeaseSet (m_Pool));
m_LeaseSet.reset (newLeaseSet);
if (m_IsPublic)
{
m_PublishVerificationTimer.cancel ();
Publish ();
}
}
void LeaseSetDestination::UpdateLeaseSet ()
{
int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
CreateNewLeaseSet (m_Pool->GetInboundTunnels (numTunnels));
}
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{
struct
{
@@ -238,17 +233,17 @@ namespace client
return true;
}
void ClientDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, shared_from_this (), msg));
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
}
void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
}
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID)
@@ -271,7 +266,7 @@ namespace client
}
}
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
{
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
@@ -335,7 +330,7 @@ namespace client
}
}
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
{
i2p::data::IdentHash key (buf);
int num = buf[32]; // num
@@ -378,35 +373,30 @@ namespace client
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
}
void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed");
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
m_ExcludedFloodfills.clear ();
m_PublishReplyToken = 0;
// schedule verification
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer,
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1));
}
else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
}
void ClientDestination::SetLeaseSetUpdated ()
void LeaseSetDestination::SetLeaseSetUpdated ()
{
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
UpdateLeaseSet ();
if (m_IsPublic)
{
m_PublishVerificationTimer.cancel ();
Publish ();
}
}
void ClientDestination::Publish ()
void LeaseSetDestination::Publish ()
{
if (!m_LeaseSet || !m_Pool)
{
@@ -424,6 +414,12 @@ namespace client
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
return;
}
auto inbound = m_Pool->GetNextInboundTunnel ();
if (!inbound)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
return;
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
if (!floodfill)
{
@@ -434,14 +430,14 @@ namespace client
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken, inbound));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1));
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
}
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
@@ -454,7 +450,7 @@ namespace client
}
}
void ClientDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
@@ -463,25 +459,239 @@ namespace client
// "this" added due to bug in gcc 4.7-4.8
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet)
if (leaseSet && s->m_LeaseSet)
{
if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet)
{
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified");
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return;
}
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return;
}
else
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet");
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
// we have to publish again
s->Publish ();
s->Publish ();
});
}
}
bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
if (!m_Pool || !IsReady ())
{
if (requestComplete)
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
return false;
}
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true;
}
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
{
auto s = shared_from_this ();
m_Service.post ([dest, notify, s](void)
{
auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ())
{
auto requestComplete = it->second->requestComplete;
s->m_LeaseSetRequests.erase (it);
if (notify && requestComplete) requestComplete (nullptr);
}
});
}
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill)
{
auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestComplete = requestComplete;
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
if (ret.second) // inserted
{
if (!SendLeaseSetRequest (dest, floodfill, request))
{
// request failed
m_LeaseSetRequests.erase (dest);
if (request->requestComplete) request->requestComplete (nullptr);
}
}
else // duplicate
{
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
// TODO: queue up requests
if (request->requestComplete) request->requestComplete (nullptr);
}
}
else
{
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
if (requestComplete) requestComplete (nullptr);
}
}
bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
{
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
if (request->replyTunnel && request->outboundTunnel)
{
request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
request->requestTimeoutTimer.cancel ();
uint8_t replyKey[32], replyTag[32];
RAND_bytes (replyKey, 32); // random session key
RAND_bytes (replyTag, 32); // random session tag
AddSessionKey (replyKey, replyTag);
auto msg = WrapMessage (nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
request->replyTunnel, replyKey, replyTag));
request->outboundTunnel->SendTunnelDataMsg (
{
i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0, msg
}
});
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
shared_from_this (), std::placeholders::_1, dest));
}
else
return false;
return true;
}
void LeaseSetDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto it = m_LeaseSetRequests.find (dest);
if (it != m_LeaseSetRequests.end ())
{
bool done = false;
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
{
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
if (floodfill)
{
// reset tunnels, because one them might fail
it->second->outboundTunnel = nullptr;
it->second->replyTunnel = nullptr;
done = !SendLeaseSetRequest (dest, floodfill, it->second);
}
else
done = true;
}
else
{
LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
done = true;
}
if (done)
{
auto requestComplete = it->second->requestComplete;
m_LeaseSetRequests.erase (it);
if (requestComplete) requestComplete (nullptr);
}
}
}
}
void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
CleanupExpiredTags ();
CleanupRemoteLeaseSets ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
}
}
void LeaseSetDestination::CleanupRemoteLeaseSets ()
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
{
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
{
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_RemoteLeaseSets.erase (it);
}
else
++it;
}
}
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (isPublic, params),
m_Keys (keys), m_DatagramDestination (nullptr)
{
if (isPublic)
PersistTemporaryKeys ();
else
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
if (isPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
}
ClientDestination::~ClientDestination ()
{
if (m_DatagramDestination)
delete m_DatagramDestination;
}
bool ClientDestination::Start ()
{
if (LeaseSetDestination::Start ())
{
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Start ();
return true;
}
else
return false;
}
bool ClientDestination::Stop ()
{
if (LeaseSetDestination::Stop ())
{
m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Stop ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
return true;
}
else
return false;
}
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = bufbe32toh (buf);
@@ -511,8 +721,8 @@ namespace client
default:
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
}
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
{
if (!streamRequestComplete)
@@ -525,7 +735,7 @@ namespace client
streamRequestComplete(CreateStream (leaseSet, port));
else
{
auto s = shared_from_this ();
auto s = GetSharedFromThis ();
RequestDestination (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
@@ -578,7 +788,7 @@ namespace client
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
{
auto dest = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this (), port, gzip);
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
if (port)
m_StreamingDestinationsByPorts[port] = dest;
else // update default
@@ -589,173 +799,23 @@ namespace client
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{
if (!m_DatagramDestination)
m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
return m_DatagramDestination;
}
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
{
if (!m_Pool || !IsReady ())
{
if (requestComplete) requestComplete (nullptr);
return false;
}
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true;
}
void ClientDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
{
auto s = shared_from_this ();
m_Service.post ([dest, s](void)
{
auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ())
{
auto requestComplete = it->second->requestComplete;
s->m_LeaseSetRequests.erase (it);
if (requestComplete) requestComplete (nullptr);
}
});
}
void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill)
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
if (m_StreamingDestination)
{
auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestComplete = requestComplete;
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
if (ret.second) // inserted
{
if (!SendLeaseSetRequest (dest, floodfill, request))
{
// request failed
m_LeaseSetRequests.erase (dest);
if (request->requestComplete) request->requestComplete (nullptr);
}
}
else // duplicate
{
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
// TODO: queue up requests
if (request->requestComplete) request->requestComplete (nullptr);
}
}
else
{
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
if (requestComplete) requestComplete (nullptr);
for (auto& it: m_StreamingDestination->GetStreams ())
ret.push_back (it.second);
}
for (auto& it: m_StreamingDestinationsByPorts)
for (auto& it1: it.second->GetStreams ())
ret.push_back (it1.second);
return ret;
}
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
{
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
if (request->replyTunnel && request->outboundTunnel)
{
request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
request->requestTimeoutTimer.cancel ();
uint8_t replyKey[32], replyTag[32];
RAND_bytes (replyKey, 32); // random session key
RAND_bytes (replyTag, 32); // random session tag
AddSessionKey (replyKey, replyTag);
auto msg = WrapMessage (nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
request->replyTunnel, replyKey, replyTag));
request->outboundTunnel->SendTunnelDataMsg (
{
i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0, msg
}
});
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer,
shared_from_this (), std::placeholders::_1, dest));
}
else
return false;
return true;
}
void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto it = m_LeaseSetRequests.find (dest);
if (it != m_LeaseSetRequests.end ())
{
bool done = false;
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
{
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
if (floodfill)
{
// reset tunnels, because one them might fail
it->second->outboundTunnel = nullptr;
it->second->replyTunnel = nullptr;
done = !SendLeaseSetRequest (dest, floodfill, it->second);
}
else
done = true;
}
else
{
LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
done = true;
}
if (done)
{
auto requestComplete = it->second->requestComplete;
m_LeaseSetRequests.erase (it);
if (requestComplete) requestComplete (nullptr);
}
}
}
}
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
CleanupExpiredTags ();
CleanupRemoteLeaseSets ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
}
}
void ClientDestination::CleanupRemoteLeaseSets ()
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
{
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
{
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_RemoteLeaseSets.erase (it);
}
else
it++;
}
}
void ClientDestination::PersistTemporaryKeys ()
{
@@ -779,6 +839,14 @@ namespace client
return;
}
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
}
}
void ClientDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
{
auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), m_EncryptionPublicKey, tunnels);
// sign
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
SetLeaseSet (leaseSet);
}
}
}

View File

@@ -49,8 +49,8 @@ namespace client
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class ClientDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<ClientDestination>
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<LeaseSetDestination>
{
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found
@@ -68,40 +68,21 @@ namespace client
public:
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~LeaseSetDestination ();
virtual void Start ();
virtual void Stop ();
virtual bool Start ();
virtual bool Stop ();
bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
void CancelDestinationRequest (const i2p::data::IdentHash& dest);
// streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
// datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
// implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
// implements GarlicDestination
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet ();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@@ -111,9 +92,13 @@ namespace client
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated ();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
protected:
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
private:
void Run ();
@@ -129,29 +114,22 @@ namespace client
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets ();
void PersistTemporaryKeys ();
void CleanupRemoteLeaseSets ();
private:
volatile bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
i2p::data::PrivateKeys m_Keys;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::shared_ptr<i2p::data::LeaseSet> m_LeaseSet;
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination;
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
@@ -160,6 +138,64 @@ namespace client
// for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
};
class ClientDestination: public LeaseSetDestination
{
public:
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:
// 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 ();
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;
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
};
}
}

19
FS.cpp
View File

@@ -9,7 +9,7 @@
#include <algorithm>
#include <boost/filesystem.hpp>
#ifdef WIN32
#ifdef _WIN32
#include <shlobj.h>
#endif
@@ -55,6 +55,14 @@ namespace fs {
dataDir += "/Library/Application Support/" + appName;
return;
#else /* other unix */
#if defined(ANDROID)
if (boost::filesystem::exists("/sdcard"))
{
dataDir = "/sdcard/" + appName;
return;
}
// otherwise use /data/files
#endif
char *home = getenv("HOME");
if (isService) {
dataDir = "/var/lib/" + appName;
@@ -102,13 +110,20 @@ namespace fs {
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_directory(root);
boost::filesystem::create_directories(root);
}
for (size_t i = 0; i < count; i++) {

17
FS.h
View File

@@ -48,8 +48,8 @@ namespace fs {
/** create subdirs in storage */
bool Init(const char* chars, size_t cnt);
const std::string & GetRoot() const { return this->root; }
const std::string & GetName() const { return this->name; }
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 */
@@ -108,6 +108,8 @@ namespace fs {
* @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) {
@@ -136,6 +138,17 @@ namespace fs {
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

View File

@@ -1,9 +1,9 @@
#include <string.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include "Crypto.h"
#include "FS.h"
#include "Log.h"
#include "Crypto.h"
#include "Family.h"
namespace i2p
@@ -94,7 +94,7 @@ namespace data
int numCertificates = 0;
if (!i2p::fs::ReadDir(certDir, files)) {
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir);
return;
}

View File

@@ -2,12 +2,12 @@
#include "I2PEndian.h"
#include <map>
#include <string>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Crypto.h"
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Transports.h"
#include "Timestamp.h"
#include "Log.h"
#include "Garlic.h"
@@ -38,9 +38,6 @@ namespace garlic
GarlicRoutingSession::~GarlicRoutingSession ()
{
for (auto it: m_UnconfirmedTagsMsgs)
delete it.second;
m_UnconfirmedTagsMsgs.clear ();
}
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
@@ -94,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;
}
}
@@ -117,23 +123,31 @@ namespace garlic
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
it++;
++it;
}
CleanupUnconfirmedTags ();
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
bool GarlicRoutingSession::CleanupUnconfirmedTags ()
{
bool ret = false;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (it->first);
delete it->second;
m_Owner->RemoveDeliveryStatusSession ((*it)->msgID);
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
else
it++;
++it;
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
return ret;
}
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
@@ -258,7 +272,10 @@ namespace garlic
size += cloveSize;
(*numCloves)++;
if (newTags) // new tags created
m_UnconfirmedTagsMsgs[msgID] = newTags;
{
newTags->msgID = msgID;
m_UnconfirmedTagsMsgs.emplace_back (newTags);
}
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
}
else
@@ -515,22 +532,34 @@ namespace garlic
buf += 32;
uint32_t gwTunnel = bufbe32toh (buf);
buf += 4;
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from && from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel) // we have send it through an outbound tunnel
{
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
}
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
if (from) // received through an inbound tunnel
{
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
if (tunnel) // we have send it through an outbound tunnel
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
}
else // received directly
i2p::transport::transports.SendMessage (gwHash, i2p::CreateTunnelGatewayMsg (gwTunnel, msg)); // send directly
break;
}
case eGarlicDeliveryTypeRouter:
LogPrint (eLogWarning, "Garlic: type router not supported");
{
uint8_t * ident = buf;
buf += 32;
break;
if (!from) // received directly
i2p::transport::transports.SendMessage (ident,
CreateI2NPMessage (buf, GetI2NPMessageLength (buf)));
else
LogPrint (eLogWarning, "Garlic: type router for inbound tunnels not supported");
break;
}
default:
LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
}
@@ -586,7 +615,7 @@ namespace garlic
it = m_Tags.erase (it);
}
else
it++;
++it;
}
if (numExpiredTags > 0)
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
@@ -602,10 +631,10 @@ namespace garlic
it = m_Sessions.erase (it);
}
else
it++;
++it;
}
}
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
{
m_DeliveryStatusSessions.erase (msgID);
@@ -614,26 +643,26 @@ namespace garlic
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
{
m_DeliveryStatusSessions[msgID] = session;
}
}
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload ());
{
auto it = m_DeliveryStatusSessions.find (msgID);
if (it != m_DeliveryStatusSessions.end ())
if (it != m_DeliveryStatusSessions.end ())
{
it->second->MessageConfirmed (msgID);
m_DeliveryStatusSessions.erase (it);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
}
}
}
void GarlicDestination::SetLeaseSetUpdated ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it: m_Sessions)
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->SetLeaseSetUpdated ();
}

View File

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

108
Gzip.cpp Normal file
View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <inttypes.h>
#include <string.h> /* memset */
#include <iostream>
#include "Gzip.h"
namespace i2p {
namespace data {
const size_t GZIP_CHUNK_SIZE = 16384;
GzipInflator::GzipInflator (): m_IsDirty (false)
{
memset (&m_Inflator, 0, sizeof (m_Inflator));
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
}
GzipInflator::~GzipInflator ()
{
inflateEnd (&m_Inflator);
}
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) inflateReset (&m_Inflator);
m_IsDirty = true;
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
m_Inflator.next_out = out;
m_Inflator.avail_out = outLen;
int err;
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) {
return outLen - m_Inflator.avail_out;
}
return 0;
}
void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os)
{
m_IsDirty = true;
uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE];
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
int ret;
do {
m_Inflator.next_out = out;
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
ret = inflate (&m_Inflator, Z_NO_FLUSH);
if (ret < 0) {
inflateEnd (&m_Inflator);
os.setstate(std::ios_base::failbit);
break;
}
os.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
} while (!m_Inflator.avail_out); // more data to read
delete[] out;
}
void GzipInflator::Inflate (std::istream& in, std::ostream& out)
{
uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE];
while (!in.eof ())
{
in.read ((char *) buf, GZIP_CHUNK_SIZE);
Inflate (buf, in.gcount (), out);
}
delete[] buf;
}
GzipDeflator::GzipDeflator (): m_IsDirty (false)
{
memset (&m_Deflator, 0, sizeof (m_Deflator));
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
}
GzipDeflator::~GzipDeflator ()
{
deflateEnd (&m_Deflator);
}
void GzipDeflator::SetCompressionLevel (int level)
{
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
}
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) deflateReset (&m_Deflator);
m_IsDirty = true;
m_Deflator.next_in = const_cast<uint8_t *>(in);
m_Deflator.avail_in = inLen;
m_Deflator.next_out = out;
m_Deflator.avail_out = outLen;
int err;
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) {
return outLen - m_Deflator.avail_out;
} /* else */
return 0;
}
} // data
} // i2p

44
Gzip.h Normal file
View File

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

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,70 +17,78 @@
#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];
uint8_t m_recv_chunk[8192];
std::string m_recv_buf; // from client
std::string m_send_buf; // to upstream
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::string m_request; //Data left to be sent
std::string m_url; //URL
std::string m_method; //Method
std::string m_version; //HTTP version
std::string m_address; //Address
std::string m_path; //Path
int m_port; //Port
state m_state;//Parsing state
public:
HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock)
{ EnterState(GET_METHOD); }
~HTTPProxyHandler() { Terminate(); }
void Handle () { AsyncSockRead(); }
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock) {}
~HTTPReqHandler() { Terminate(); }
void Handle () { AsyncSockRead(); } /* overload */
};
void HTTPProxyHandler::AsyncSockRead()
void HTTPReqHandler::AsyncSockRead()
{
LogPrint(eLogDebug, "HTTPProxy: async sock read");
if(m_sock) {
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
if (!m_sock) {
LogPrint(eLogError, "HTTPProxy: no socket for read");
return;
}
m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)),
std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
void HTTPProxyHandler::Terminate() {
void HTTPReqHandler::Terminate() {
if (Kill()) return;
if (m_sock)
{
@@ -86,181 +99,192 @@ namespace proxy
Done(shared_from_this());
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
{
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
void HTTPReqHandler::GenericProxyError(const char *title, const char *description) {
std::stringstream ss;
ss << "<h1>Proxy error: " << title << "</h1>\r\n";
ss << "<p>" << description << "</p>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate)
{
m_state = nstate;
}
void HTTPProxyHandler::ExtractRequest()
{
LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url);
std::string server="";
std::string port="80";
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
boost::smatch m;
std::string path;
if(boost::regex_search(m_url, m, rHTTP, boost::match_extra))
{
server=m[1].str();
if (m[2].str() != "") port=m[3].str();
path=m[4].str();
void HTTPReqHandler::HostNotFound(std::string & host) {
std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n"
<< "<p>Remote host not found in router's addressbook</p>\r\n"
<< "<p>You may try to find this host on jumpservices below:</p>\r\n"
<< "<ul>\r\n";
for (const auto& js : jumpservices) {
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
}
LogPrint(eLogDebug, "HTTPProxy: server: ", server, ", port: ", port, ", path: ", path);
m_address = server;
m_port = boost::lexical_cast<int>(port);
m_path = path;
ss << "</ul>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
bool HTTPProxyHandler::ValidateHTTPRequest()
void HTTPReqHandler::SendProxyError(std::string & content)
{
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
{
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
HTTPRequestFailed(); //TODO: send right stuff
i2p::http::HTTPRes res;
res.code = 500;
res.add_header("Content-Type", "text/html; charset=UTF-8");
res.add_header("Connection", "close");
std::stringstream ss;
ss << "<html>\r\n" << pageHead
<< "<body>" << content << "</body>\r\n"
<< "</html>\r\n";
res.body = ss.str();
std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(response),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64)
{
const char *param = "i2paddresshelper=";
std::size_t pos = url.query.find(param);
std::size_t len = std::strlen(param);
std::map<std::string, std::string> params;
if (pos == std::string::npos)
return false; /* not found */
if (!url.parse_query(params))
return false;
}
std::string value = params["i2paddresshelper"];
len += value.length();
b64 = i2p::http::UrlDecode(value);
url.query.replace(pos, len, "");
return true;
}
void HTTPProxyHandler::HandleJumpServices()
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
{
static const char * helpermark1 = "?i2paddresshelper=";
static const char * helpermark2 = "&i2paddresshelper=";
size_t addressHelperPos1 = m_path.rfind (helpermark1);
size_t addressHelperPos2 = m_path.rfind (helpermark2);
size_t addressHelperPos;
if (addressHelperPos1 == std::string::npos)
{
if (addressHelperPos2 == std::string::npos)
return; //Not a jump service
else
addressHelperPos = addressHelperPos2;
}
else
{
if (addressHelperPos2 == std::string::npos)
addressHelperPos = addressHelperPos1;
else if ( addressHelperPos1 > addressHelperPos2 )
addressHelperPos = addressHelperPos1;
else
addressHelperPos = addressHelperPos2;
}
auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1));
base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded
LogPrint (eLogInfo, "HTTPProxy: jump service for ", m_address, ", inserting to address book");
//TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/
//TODO: we could redirect the user again to avoid dirtiness in the browser
i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64);
m_path.erase(addressHelperPos);
}
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
{
ExtractRequest(); //TODO: parse earlier
if (!ValidateHTTPRequest()) return false;
HandleJumpServices();
m_request = m_method;
m_request.push_back(' ');
m_request += m_path;
m_request.push_back(' ');
m_request += m_version;
m_request.push_back('\r');
m_request.push_back('\n');
m_request.append("Connection: close\r\n");
// TODO: temporary shortcut. Must be implemented properly
uint8_t * eol = nullptr;
bool isEndOfHeader = false;
while (!isEndOfHeader && len && (eol = (uint8_t *)memchr (http_buff, '\r', len)))
{
if (eol)
{
*eol = 0; eol++;
if (strncmp ((const char *)http_buff, "Referer", 7)) // strip out referer
{
if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent
m_request.append("User-Agent: MYOB/6.66 (AN/ON)");
else
m_request.append ((const char *)http_buff);
m_request.append ("\r\n");
}
isEndOfHeader = !http_buff[0];
auto l = eol - http_buff;
http_buff = eol;
len -= l;
if (len > 0) // \r
{
http_buff++;
len--;
}
/* 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 */
}
}
m_request.append(reinterpret_cast<const char *>(http_buff),len);
return true;
}
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
{
while (len > 0)
{
//TODO: fallback to finding HOst: header if needed
switch (m_state)
{
case GET_METHOD:
switch (*http_buff)
{
case ' ': EnterState(GET_HOSTNAME); break;
default: m_method.push_back(*http_buff); break;
}
break;
case GET_HOSTNAME:
switch (*http_buff)
{
case ' ': EnterState(GET_HTTPV); break;
default: m_url.push_back(*http_buff); break;
}
break;
case GET_HTTPV:
switch (*http_buff)
{
case '\r': EnterState(GET_HTTPVNL); break;
default: m_version.push_back(*http_buff); break;
}
break;
case GET_HTTPVNL:
switch (*http_buff)
{
case '\n': EnterState(DONE); break;
default:
LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
HTTPRequestFailed(); //TODO: add correct code
return false;
}
break;
default:
LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
HTTPRequestFailed(); //TODO: add correct code 500
return false;
}
http_buff++;
len--;
if (m_state == DONE)
return CreateHTTPRequest(http_buff,len);
}
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, "HTTPProxy: sock recv: ", len, " bytes");
LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length());
if(ecode)
{
LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
@@ -268,54 +292,45 @@ namespace proxy
return;
}
if (HandleData(m_http_buff, len))
{
if (m_state == DONE)
{
LogPrint(eLogDebug, "HTTPProxy: requested: ", m_url);
GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address, m_port);
}
else
AsyncSockRead();
m_recv_buf.append(reinterpret_cast<const char *>(m_recv_chunk), len);
if (HandleRequest()) {
m_recv_buf.clear();
return;
}
AsyncSockRead();
}
void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
{
if (ecode)
LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (Kill()) return;
LogPrint (eLogInfo, "HTTPProxy: New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
Done(shared_from_this());
}
else
{
if (!stream) {
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
GenericProxyError("Host is down", "Can't create connection to requested host, it may be down");
return;
}
if (Kill())
return;
LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID());
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_send_buf.data()), m_send_buf.length());
Done (shared_from_this());
}
HTTPProxyServer::HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
{
}
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
return std::make_shared<HTTPProxyHandler> (this, socket);
return std::make_shared<HTTPReqHandler> (this, socket);
}
}
}
} // http
} // i2p

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,106 +1,39 @@
#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: 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 status_string, content;
std::vector<boost::asio::const_buffer> to_buffers (int status);
};
public:
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_Timer (socket->get_io_service ()),
m_Stream (nullptr), m_BufferLen (0) {};
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void Receive ();
private:
void Terminate ();
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 Terminate (const boost::system::error_code& ecode);
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 ShowI2PTunnels (std::stringstream& s);
void StartAcceptingTunnels (std::stringstream& s);
void StopAcceptingTunnels (std::stringstream& s);
void RunPeerTest (std::stringstream& s);
void FillContent (std::stringstream& s);
std::string ExtractAddress ();
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
protected:
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:
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;
bool needAuth;
std::string user;
std::string pass;
};
class HTTPServer
@@ -108,7 +41,7 @@ namespace util
public:
HTTPServer (const std::string& address, int port);
virtual ~HTTPServer ();
~HTTPServer ();
void Start ();
void Stop ();
@@ -119,6 +52,7 @@ namespace util
void Accept ();
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:
@@ -126,13 +60,8 @@ namespace util
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor m_Acceptor;
protected:
virtual void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
};
}
}
#endif
} // http
} // i2p
#endif /* HTTP_SERVER_H__ */

726
I2CP.cpp Normal file
View File

@@ -0,0 +1,726 @@
/*
* 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 remote = FindLeaseSet (ident);
if (remote)
GetService ().post (std::bind (&I2CPDestination::SendMsg, GetSharedFromThis (), msg, remote));
else
{
auto s = GetSharedFromThis ();
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
{
auto 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,7 +1,5 @@
#include <string.h>
#include <atomic>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Base.h"
#include "Log.h"
#include "Crypto.h"
@@ -13,6 +11,7 @@
#include "Transports.h"
#include "Garlic.h"
#include "I2NPProtocol.h"
#include "version.h"
using namespace i2p::transport;
@@ -103,7 +102,7 @@ namespace i2p
{
RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, I2PD_NET_ID);
}
m->len += DELIVERY_STATUS_SIZE;
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
@@ -166,9 +165,10 @@ namespace i2p
buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 5;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
buf ++;
htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 4;
// excluded
htobe16buf (buf, cnt);
@@ -183,7 +183,7 @@ namespace i2p
}
// encryption
memcpy (buf, replyKey, 32);
buf[32] = 1; // 1 tag
buf[32] = uint8_t( 1 ); // 1 tag
memcpy (buf + 33, replyTag, 32);
buf += 65;
@@ -202,7 +202,7 @@ namespace i2p
len += 32;
buf[len] = routers.size ();
len++;
for (auto it: routers)
for (const auto& it: routers)
{
memcpy (buf + len, it, 32);
len += 32;
@@ -251,7 +251,23 @@ namespace i2p
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
size_t size = DATABASE_STORE_HEADER_SIZE;
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen ();
m->len += size;
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
@@ -260,14 +276,13 @@ namespace i2p
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
if (replyToken && replyTunnel)
{
auto leases = leaseSet->GetNonExpiredLeases ();
if (leases.size () > 0)
if (replyTunnel)
{
htobe32buf (payload + size, leases[0]->tunnelID);
htobe32buf (payload + size, replyTunnel->GetNextTunnelID ());
size += 4; // reply tunnelID
memcpy (payload + size, leases[0]->tunnelGateway, 32);
memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32);
size += 32; // reply tunnel gateway
}
else
@@ -286,6 +301,16 @@ namespace i2p
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++)
@@ -298,7 +323,7 @@ namespace i2p
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 ())
{
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (

View File

@@ -5,7 +5,7 @@
#include <string.h>
#include <set>
#include <memory>
#include <openssl/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 unsigned int MAX_NUM_TRANSIT_TUNNELS = 2500;
namespace tunnel
{
class InboundTunnel;
@@ -226,7 +224,8 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
@@ -259,6 +258,9 @@ namespace tunnel
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

@@ -2,8 +2,6 @@
#include <sstream>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp>
@@ -13,8 +11,10 @@
#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"
@@ -23,6 +23,7 @@
#include "Timestamp.h"
#include "Transports.h"
#include "version.h"
#include "util.h"
#include "I2PControl.h"
namespace i2p
@@ -83,6 +84,10 @@ namespace client
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 ()
@@ -184,71 +189,64 @@ namespace client
if (ecode) {
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
return;
} else {
try
{
bool isHtml = !memcmp (buf->data (), "POST", 4);
std::stringstream ss;
ss.write (buf->data (), bytes_transferred);
if (isHtml)
{
std::string header;
size_t contentLength = 0;
while (!ss.eof () && header != "\r")
{
std::getline(ss, header);
auto colon = header.find (':');
if (colon != std::string::npos && header.substr (0, colon) == "Content-Length")
contentLength = std::stoi (header.substr (colon + 1));
}
if (ss.eof ())
{
LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected");
return; // TODO:
}
std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read
if (rem > 0)
{
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem));
ss.write (buf->data (), bytes_transferred);
}
}
std::ostringstream response;
#if GCC47_BOOST149
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
response << "{\"id\":null,\"error\":";
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
response << "\"jsonrpc\":\"2.0\"}";
#else
boost::property_tree::ptree pt;
boost::property_tree::read_json (ss, pt);
std::string id = pt.get<std::string>("id");
std::string method = pt.get<std::string>("method");
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ())
{
response << "{\"id\":" << id << ",\"result\":{";
(this->*(it->second))(pt.get_child ("params"), response);
response << "},\"jsonrpc\":\"2.0\"}";
} else {
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
response << "{\"id\":null,\"error\":";
response << "{\"code\":-32601,\"message\":\"Method not found\"},";
response << "\"jsonrpc\":\"2.0\"}";
}
#endif
SendResponse (socket, buf, response, isHtml);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
}
catch (...)
{
LogPrint (eLogError, "I2PControl: handle request unknown exception");
}
}
/* try to parse received data */
std::stringstream json;
std::string response;
bool isHTTP = false;
if (memcmp (buf->data (), "POST", 4) == 0) {
long int remains = 0;
isHTTP = true;
i2p::http::HTTPReq req;
std::size_t len = req.parse(buf->data(), bytes_transferred);
if (len <= 0) {
LogPrint(eLogError, "I2PControl: incomplete/malformed POST request");
return;
}
/* append to json chunk of data from 1st request */
json.write(buf->data() + len, bytes_transferred - len);
remains = req.content_length() - len;
/* if request has Content-Length header, fetch rest of data and store to json buffer */
while (remains > 0) {
len = ((long int) buf->size() < remains) ? buf->size() : remains;
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len));
json.write(buf->data(), bytes_transferred);
remains -= bytes_transferred;
}
} else {
json.write(buf->data(), bytes_transferred);
}
LogPrint(eLogDebug, "I2PControl: json from request: ", json.str());
#if GCC47_BOOST149
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost");
#else
/* now try to parse json itself */
try {
boost::property_tree::ptree pt;
boost::property_tree::read_json (json, pt);
std::string id = pt.get<std::string>("id");
std::string method = pt.get<std::string>("method");
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ()) {
std::ostringstream ss;
ss << "{\"id\":" << id << ",\"result\":{";
(this->*(it->second))(pt.get_child ("params"), ss);
ss << "},\"jsonrpc\":\"2.0\"}";
response = ss.str();
} else {
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
BuildErrorResponse(response, 32601, "Method not found");
}
} catch (std::exception& ex) {
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
BuildErrorResponse(response, 32603, ex.what());
} catch (...) {
LogPrint (eLogError, "I2PControl: handle request unknown exception");
}
#endif
SendResponse (socket, buf, response, isHTTP);
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
@@ -270,27 +268,28 @@ namespace client
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
std::stringstream ss;
ss << "{\"id\":null,\"error\":";
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
ss << "\"jsonrpc\":\"2.0\"}";
content = ss.str();
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
{
size_t len = response.str ().length (), offset = 0;
if (isHtml)
{
std::ostringstream header;
header << "HTTP/1.1 200 OK\r\n";
header << "Connection: close\r\n";
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
header << "Content-Type: application/json\r\n";
header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
header.imbue(std::locale (header.getloc(), facet));
header << boost::posix_time::second_clock::local_time() << "\r\n";
header << "\r\n";
offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset);
if (isHTTP) {
i2p::http::HTTPRes res;
res.code = 200;
res.add_header("Content-Type", "application/json");
res.add_header("Connection", "close");
res.body = content;
std::string tmp = res.to_string();
content = tmp;
}
memcpy (buf->data () + offset, response.str ().c_str (), len);
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
std::copy(content.begin(), content.end(), buf->begin());
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
boost::asio::transfer_all (),
std::bind(&I2PControlService::HandleResponseSent, this,
std::placeholders::_1, std::placeholders::_2, socket, buf));
@@ -317,7 +316,7 @@ namespace client
}
InsertParam (results, "API", api);
results << ",";
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
m_Tokens.insert (token);
InsertParam (results, "Token", token);
}
@@ -359,7 +358,7 @@ namespace client
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
for (auto it = params.begin (); it != params.end (); ++it)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
@@ -435,7 +434,7 @@ namespace client
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
for (auto it = params.begin (); it != params.end (); ++it)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
@@ -484,7 +483,7 @@ namespace client
// network setting
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
for (auto it = params.begin (); it != params.end (); ++it)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
@@ -496,6 +495,22 @@ namespace client
}
}
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)
{

View File

@@ -10,7 +10,7 @@
#include <map>
#include <set>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/property_tree/ptree.hpp>
namespace i2p
@@ -45,8 +45,9 @@ namespace client
void ReadRequest (std::shared_ptr<ssl_socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void BuildErrorResponse (std::string & content, int code, const char *message);
void SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
std::shared_ptr<I2PControlBuffer> buf, std::string& response, bool isHtml);
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
@@ -94,6 +95,8 @@ namespace client
// NetworkSetting
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:

View File

@@ -3,7 +3,6 @@
#include "ClientContext.h"
#include "I2PService.h"
namespace i2p
{
namespace client
@@ -28,7 +27,7 @@ namespace client
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "I2PService: Remote destination ", dest, " not found");
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
streamRequestComplete (nullptr);
}
}
@@ -71,7 +70,7 @@ namespace client
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: no upstream socket for read");
LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
}
}
@@ -82,14 +81,14 @@ namespace client
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError, "TCPIPPipe: no downstream socket for read");
LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
}
}
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
{
if (m_up) {
LogPrint(eLogDebug, "TCPIPPipe: write upstream ", (int)len);
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleUpstreamWrite,
@@ -97,14 +96,14 @@ namespace client
std::placeholders::_1)
);
} else {
LogPrint(eLogError, "tcpip pipe upstream socket null");
LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
}
}
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
{
if (m_down) {
LogPrint(eLogDebug, "TCPIPPipe: write downstream ", (int)len);
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleDownstreamWrite,
@@ -112,16 +111,16 @@ namespace client
std::placeholders::_1)
);
} else {
LogPrint(eLogError, "tcpip pipe downstream socket null");
LogPrint(eLogError, "TCPIPPipe: downstream write: no socket");
}
}
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe downstream got ", (int) bytes_transfered);
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Downstream read error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
@@ -135,7 +134,7 @@ namespace client
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Downstream write error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
@@ -143,7 +142,7 @@ namespace client
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Upstream write error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
@@ -151,9 +150,9 @@ namespace client
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe upstream got ", (int) bytes_transfered);
LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
if (ecode) {
LogPrint(eLogError, "TCPIPPipe Upstream read error:" , ecode.message());
LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
@@ -206,6 +205,5 @@ namespace client
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
}
}
}
}

View File

@@ -118,6 +118,9 @@ namespace client
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(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }

View File

@@ -1,8 +1,5 @@
#include <time.h>
#include <stdio.h>
#include <openssl/sha.h>
#include <openssl/dh.h>
#include <openssl/rand.h>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Log.h"
@@ -244,21 +241,20 @@ namespace data
size_t IdentityEx::FromBase64(const std::string& s)
{
const size_t slen = s.length();
uint8_t buf[slen]; // binary data can't exceed base64
const size_t len = Base64ToByteStream (s.c_str(), slen, buf, slen);
return FromBuffer (buf, len);
std::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
{
const size_t bufLen = GetFullLen();
const size_t strLen = Base64EncodingBufferSize(bufLen);
uint8_t buf[bufLen];
char str[strLen];
size_t l = ToBuffer (buf, bufLen);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, strLen);
str[l1] = 0;
return std::string (str);
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
@@ -312,18 +308,18 @@ namespace data
switch (keyType)
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Verifier.reset (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.reset (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.reset (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:
@@ -332,7 +328,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.reset (new i2p::crypto::ECDSAP521Verifier (signingKey));
UpdateVerifier (new i2p::crypto::ECDSAP521Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
@@ -341,7 +337,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.reset (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
UpdateVerifier (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
@@ -350,7 +346,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.reset (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
UpdateVerifier (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
@@ -359,20 +355,28 @@ 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.reset (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
m_Verifier.reset (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
UpdateVerifier (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
break;
}
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
}
}
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
{
if (!m_Verifier || !verifier)
m_Verifier.reset (verifier);
else
delete verifier;
}
void IdentityEx::DropVerifier () const
{
// TODO: potential race condition with Verify

View File

@@ -95,6 +95,7 @@ namespace data
private:
void CreateVerifier () const;
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
private:
@@ -177,16 +178,10 @@ namespace data
public:
virtual ~LocalDestination() {};
virtual const PrivateKeys& GetPrivateKeys () const = 0;
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
std::shared_ptr<const IdentityEx> GetIdentity () const { return GetPrivateKeys ().GetPublic (); };
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
GetPrivateKeys ().Sign (buf, len, signature);
};
};
}
}

View File

@@ -4,7 +4,7 @@
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "TunnelPool.h"
#include "Tunnel.h"
#include "LeaseSet.h"
namespace i2p
@@ -21,56 +21,6 @@ namespace data
ReadFromBuffer ();
}
LeaseSet::LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool):
m_IsValid (true), m_StoreLeases (true), m_ExpirationTime (0)
{
if (!pool) return;
// header
auto localDestination = pool->GetLocalDestination ();
if (!localDestination)
{
m_Buffer = nullptr;
m_BufferLen = 0;
m_IsValid = false;
LogPrint (eLogError, "LeaseSet: Destination for local LeaseSet doesn't exist");
return;
}
m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE];
m_BufferLen = localDestination->GetIdentity ()->ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256);
m_BufferLen += 256;
auto signingKeyLen = localDestination->GetIdentity ()->GetSigningPublicKeyLen ();
memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
m_BufferLen += signingKeyLen;
int numTunnels = pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
if (numTunnels > 16) numTunnels = 16; // 16 tunnels maximum
auto tunnels = pool->GetInboundTunnels (numTunnels);
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
m_BufferLen++;
// leases
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: tunnels)
{
memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32);
m_BufferLen += 32; // gateway id
htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ());
m_BufferLen += 4; // tunnel id
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - it->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + m_BufferLen, ts);
m_BufferLen += 8; // end date
}
// signature
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += localDestination->GetIdentity ()->GetSignatureLen ();
LogPrint (eLogDebug, "LeaseSet: Local LeaseSet of ", tunnels.size (), " leases created");
ReadFromBuffer ();
}
void LeaseSet::Update (const uint8_t * buf, size_t len)
{
if (len > m_BufferLen)
@@ -114,10 +64,10 @@ namespace data
return;
}
// reset existing leases
// reset existing leases
if (m_StoreLeases)
for (auto it: m_Leases)
it->isUpdated = false;
for (auto& it: m_Leases)
it->isUpdated = false;
else
m_Leases.clear ();
@@ -173,7 +123,7 @@ namespace data
m_Leases.erase (it++);
}
else
it++;
++it;
}
}
@@ -195,7 +145,7 @@ namespace data
if (size > len) return 0;
uint8_t num = buf[size];
size++; // num
if (size + num*44 > len) return 0;
if (size + num*LEASE_SIZE > len) return 0;
uint64_t timestamp= 0 ;
for (int i = 0; i < num; i++)
{
@@ -217,7 +167,7 @@ namespace data
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<std::shared_ptr<const Lease> > leases;
for (auto it: m_Leases)
for (const auto& it: m_Leases)
{
auto endDate = it->endDate;
if (withThreshold)
@@ -233,14 +183,65 @@ namespace data
bool LeaseSet::HasExpiredLeases () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_Leases)
for (const auto& it: m_Leases)
if (ts >= it->endDate) return true;
return false;
}
bool LeaseSet::IsExpired () const
{
if (IsEmpty ()) return true;
if (m_StoreLeases && IsEmpty ()) return true;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
return ts > m_ExpirationTime;
}
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
m_ExpirationTime (0), m_Identity (identity)
{
int num = tunnels.size ();
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
// identity
auto signingKeyLen = m_Identity->GetSigningPublicKeyLen ();
m_BufferLen = m_Identity->GetFullLen () + 256 + signingKeyLen + 1 + num*LEASE_SIZE + m_Identity->GetSignatureLen ();
m_Buffer = new uint8_t[m_BufferLen];
auto offset = m_Identity->ToBuffer (m_Buffer, m_BufferLen);
memcpy (m_Buffer + offset, encryptionPublicKey, 256);
offset += 256;
memset (m_Buffer + offset, 0, signingKeyLen);
offset += signingKeyLen;
// num leases
m_Buffer[offset] = num;
offset++;
// leases
m_Leases = m_Buffer + offset;
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
for (int i = 0; i < num; i++)
{
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
offset += 32; // gateway id
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
offset += 4; // tunnel id
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + offset, ts);
offset += 8; // end date
}
// we don't sign it yet. must be signed later on
}
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
m_ExpirationTime (0), m_Identity (identity)
{
m_BufferLen = len;
m_Buffer = new uint8_t[m_BufferLen];
memcpy (m_Buffer, buf, len);
}
bool LocalLeaseSet::IsExpired () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
return ts > m_ExpirationTime;
}

View File

@@ -4,6 +4,7 @@
#include <inttypes.h>
#include <string.h>
#include <vector>
#include <set>
#include <memory>
#include "Identity.h"
@@ -12,7 +13,7 @@ namespace i2p
namespace tunnel
{
class TunnelPool;
class InboundTunnel;
}
namespace data
@@ -37,14 +38,14 @@ namespace data
};
};
const int MAX_LS_BUFFER_SIZE = 3072;
const size_t MAX_LS_BUFFER_SIZE = 3072;
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
const uint8_t MAX_NUM_LEASES = 16;
class LeaseSet: public RoutingDestination
{
public:
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true);
LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool);
~LeaseSet () { delete[] m_Buffer; };
void Update (const uint8_t * buf, size_t len);
bool IsNewer (const uint8_t * buf, size_t len) const;
@@ -82,6 +83,36 @@ namespace data
uint8_t * m_Buffer;
size_t m_BufferLen;
};
class LocalLeaseSet
{
public:
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len);
~LocalLeaseSet () { delete[] m_Buffer; };
const uint8_t * GetBuffer () const { return m_Buffer; };
uint8_t * GetSignature () { return m_Buffer + m_BufferLen - GetSignatureLen (); };
size_t GetBufferLen () const { return m_BufferLen; };
size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); };
uint8_t * GetLeases () { return m_Leases; };
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
bool IsExpired () const;
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.GetBufferLen () && !memcmp (other.GetBuffer (), other.GetBuffer (), m_BufferLen); };
private:
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_Buffer, * m_Leases;
size_t m_BufferLen;
};
}
}

241
Log.cpp
View File

@@ -1,86 +1,171 @@
#include <boost/date_time/posix_time/posix_time.hpp>
/*
* 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"
Log * g_Log = nullptr;
static const char * g_LogLevelStr[eNumLogLevels] =
{
"error", // eLogError
"warn", // eLogWarning
"info", // eLogInfo
"debug" // eLogDebug
};
void LogMsg::Process()
{
auto stream = log ? log->GetLogStream () : nullptr;
auto& output = stream ? *stream : std::cout;
if (log)
output << log->GetTimestamp ();
else
output << boost::posix_time::second_clock::local_time().time_of_day ();
output << "/" << g_LogLevelStr[level] << " - ";
output << s.str();
}
const std::string& Log::GetTimestamp ()
{
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__)
auto ts = std::chrono::monotonic_clock::now ();
#else
auto ts = std::chrono::steady_clock::now ();
#endif
if (ts > m_LastTimestampUpdate + std::chrono::milliseconds (500)) // 0.5 second
namespace i2p {
namespace log {
Log logger;
/**
* @brief Maps our loglevel to their symbolic name
*/
static const char * g_LogLevelStr[eNumLogLevels] =
{
m_LastTimestampUpdate = ts;
m_Timestamp = boost::posix_time::to_simple_string (boost::posix_time::second_clock::local_time().time_of_day ());
}
return m_Timestamp;
}
"error", // eLogError
"warn", // eLogWarn
"info", // eLogInfo
"debug" // eLogDebug
};
void Log::Flush ()
{
if (m_LogStream)
m_LogStream->flush();
}
void Log::SetLogFile (const std::string& fullFilePath, bool truncate)
{
m_FullFilePath = fullFilePath;
auto mode = std::ofstream::out | std::ofstream::binary;
mode |= truncate ? std::ofstream::trunc : std::ofstream::app;
auto logFile = std::make_shared<std::ofstream> (fullFilePath, mode);
if (logFile->is_open ())
{
SetLogStream (logFile);
LogPrint(eLogInfo, "Log: will send messages to ", fullFilePath);
}
}
void Log::ReopenLogFile ()
{
if (m_FullFilePath.length () > 0)
{
SetLogFile (m_FullFilePath, false); // don't truncate
LogPrint(eLogInfo, "Log: file ", m_FullFilePath, " reopen");
#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)
{
}
void Log::SetLogLevel (const std::string& level)
{
if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; }
else if (level == "info") { m_MinLevel = eLogInfo; }
else if (level == "debug") { m_MinLevel = eLogDebug; }
else {
LogPrint(eLogError, "Log: Unknown loglevel: ", level);
return;
}
LogPrint(eLogInfo, "Log: min msg level set to ", level);
}
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::SetLogStream (std::shared_ptr<std::ostream> logStream)
{
m_LogStream = logStream;
}
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
<< "/" << g_LogLevelStr[msg->level]
<< " - " << 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_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_Destination = eLogStream;
m_LogStream = os;
}
#ifndef _WIN32
void Log::SendTo(const char *name, int facility) {
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

251
Log.h
View File

@@ -1,15 +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,
@@ -19,130 +31,157 @@ enum LogLevel
eNumLogLevels
};
class Log;
struct LogMsg
{
std::stringstream s;
Log * log;
LogLevel level;
LogMsg (Log * l = nullptr, LogLevel lv = eLogInfo): log (l), level (lv) {};
void Process();
enum LogType {
eLogStdout = 0,
eLogStream,
eLogFile,
#ifndef _WIN32
eLogSyslog,
#endif
};
class Log: public i2p::util::MsgQueue<LogMsg>
{
public:
namespace i2p {
namespace log {
struct LogMsg; /* forward declaration */
Log () { SetOnEmpty (std::bind (&Log::Flush, this)); };
~Log () {};
void SetLogFile (const std::string& fullFilePath, bool truncate = true);
void ReopenLogFile ();
void SetLogLevel (const std::string& level);
void SetLogStream (std::shared_ptr<std::ostream> logStream);
std::shared_ptr<std::ostream> GetLogStream () const { return m_LogStream; };
const std::string& GetTimestamp ();
LogLevel GetLogLevel () { return m_MinLevel; };
const std::string& GetFullFilePath () const { return m_FullFilePath; };
private:
void Flush ();
private:
std::string m_FullFilePath; // empty if stream
std::shared_ptr<std::ostream> m_LogStream;
enum LogLevel m_MinLevel;
std::string m_Timestamp;
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__) // gcc 4.6
std::chrono::monotonic_clock::time_point m_LastTimestampUpdate;
#else
std::chrono::steady_clock::time_point m_LastTimestampUpdate;
#endif
};
extern Log * g_Log;
inline void StartLog (const std::string& fullFilePath)
{
if (!g_Log)
{
auto log = new Log ();
if (fullFilePath.length () > 0)
log->SetLogFile (fullFilePath);
g_Log = log;
}
}
inline void StartLog (std::shared_ptr<std::ostream> s)
{
if (!g_Log)
{
auto log = new Log ();
if (s)
log->SetLogStream (s);
g_Log = log;
}
}
inline void StopLog ()
{
if (g_Log)
class Log
{
auto log = g_Log;
g_Log = nullptr;
log->Stop ();
delete log;
}
}
private:
inline void SetLogLevel (const std::string& level)
{
if (g_Log)
g_Log->SetLogLevel(level);
}
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;
mutable std::mutex m_OutputLock;
inline void ReopenLogFile ()
{
if (g_Log)
g_Log->ReopenLogFile ();
}
private:
inline bool IsLogToFile ()
{
return g_Log ? !g_Log->GetFullFilePath ().empty () : false;
}
/** 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
} // i2p
/** internal usage only -- folding args array to single string */
template<typename TValue>
void LogPrint (std::stringstream& s, TValue arg)
void LogPrint (std::stringstream& s, TValue arg)
{
s << arg;
}
/** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
{
LogPrint (s, arg);
LogPrint (s, 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)
void LogPrint (LogLevel level, TArgs... args)
{
if (g_Log && level > g_Log->GetLogLevel ())
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
return;
LogMsg * msg = new LogMsg (g_Log, level);
LogPrint (msg->s, args...);
msg->s << std::endl;
if (g_Log) {
g_Log->Put (msg);
} else {
msg->Process ();
delete msg;
}
// fold message to single string
std::stringstream ss("");
LogPrint (ss, 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
#endif // LOG_H__

197
Makefile
View File

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

View File

@@ -9,4 +9,4 @@ CXXFLAGS = -O2
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread

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,5 @@
# set defaults instead redefine
CXXFLAGS ?= -g -Wall
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic
INCFLAGS ?=
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
@@ -19,7 +19,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
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 ${CXXVER} "5\.[0-9]"),3) # gcc >= 5.0
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)
@@ -32,20 +32,19 @@ ifeq ($(USE_STATIC),yes)
LDLIBS = $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_regex.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt
USE_AESNI := no
else
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
endif
# UPNP Support (miniupnpc 1.5 or 1.6)
ifeq ($(USE_UPNP),1)
LDFLAGS += -ldl
ifeq ($(USE_UPNP),yes)
LDFLAGS += -lminiupnpc
CXXFLAGS += -DUSE_UPNP
endif

View File

@@ -1,18 +1,23 @@
USE_WIN32_APP=yes
CXX = g++
WINDRES = windres
CXXFLAGS = -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
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 = -mwindows -Wl,-rpath,/usr/local/lib \
-L/usr/local/lib \
-L/c/dev/openssl \
-L/c/dev/boost/lib
LDLIBS = \
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_regex$(BOOST_SUFFIX) \
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
-Wl,-Bstatic -lssl \
-Wl,-Bstatic -lcrypto \
@@ -24,8 +29,13 @@ LDLIBS = \
-static-libgcc -static-libstdc++ \
-Wl,-Bstatic -lstdc++ \
-Wl,-Bstatic -lpthread
DAEMON_RC += Win32/Resource.rc
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
ifeq ($(USE_WIN32_APP), yes)
CXXFLAGS += -DWIN32_APP
LDFLAGS += -mwindows -s
DAEMON_RC += Win32/Resource.rc
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif
ifeq ($(USE_AESNI),1)
CPU_FLAGS = -maes -DAESNI
@@ -35,4 +45,3 @@ endif
obj/%.o : %.rc
$(WINDRES) -i $< -o $@

View File

@@ -3,9 +3,9 @@ CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
#CXXFLAGS = -g -O2 -Wall -std=c++11
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),1)
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif

View File

@@ -1,13 +1,11 @@
#include <string.h>
#include <stdlib.h>
#include <openssl/dh.h>
#include <openssl/sha.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "RouterContext.h"
#include "Transports.h"
@@ -21,7 +19,8 @@ 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 ()),
TransportSession (in_RemoteRouter, NTCP_TERMINATION_TIMEOUT),
m_Server (server), m_Socket (m_Server.GetService ()),
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
{
@@ -93,9 +92,7 @@ namespace transport
m_DHKeysPair = nullptr;
SendTimeSyncMessage ();
m_SendQueue.push_back (CreateDatabaseStoreMsg ()); // we tell immediately who we are
SendTimeSyncMessage ();
transports.PeerConnected (shared_from_this ());
}
@@ -434,7 +431,7 @@ namespace transport
}
else
{
LogPrint (eLogInfo, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
LogPrint (eLogDebug, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
m_Server.AddNTCPSession (shared_from_this ());
Connected ();
@@ -669,7 +666,7 @@ namespace transport
{
m_IsSending = true;
std::vector<boost::asio::const_buffer> bufs;
for (auto it: msgs)
for (const auto& it: msgs)
bufs.push_back (CreateMsgBuffer (it));
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
@@ -717,8 +714,16 @@ namespace transport
if (m_IsTerminated) return;
if (m_IsSending)
{
for (auto it: msgs)
m_SendQueue.push_back (it);
if (m_SendQueue.size () < NTCP_MAX_OUTGOING_QUEUE_SIZE)
{
for (const auto& it: msgs)
m_SendQueue.push_back (it);
}
else
{
LogPrint (eLogWarning, "NTCP: outgoing messages queue size exceeds ", NTCP_MAX_OUTGOING_QUEUE_SIZE);
Terminate ();
}
}
else
Send (msgs);
@@ -727,7 +732,7 @@ namespace transport
void NTCPSession::ScheduleTermination ()
{
m_TerminationTimer.cancel ();
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer,
shared_from_this (), std::placeholders::_1));
}
@@ -736,7 +741,7 @@ namespace transport
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogDebug, "NTCP: No activity for ", NTCP_TERMINATION_TIMEOUT, " seconds");
LogPrint (eLogDebug, "NTCP: No activity for ", GetTerminationTimeout (), " seconds");
//Terminate ();
m_Socket.close ();// invoke Terminate () from HandleReceive
}
@@ -761,33 +766,49 @@ 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, "NTCP: Start listening TCP port ", address.port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
if (context.SupportsV6 ())
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
if (address->host.is_v4())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address.port));
m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address.port);
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1));
try
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
} catch ( std::exception & ex ) {
/** fail to bind ip4 */
LogPrint(eLogError, "NTCP: Failed to bind to ip4 port ",address->port, ex.what());
continue;
}
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
}
else if (address->host.is_v6() && context.SupportsV6 ())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
try
{
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1));
} catch ( std::exception & ex ) {
LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port);
continue;
}
}
}
}
}
}
}
@@ -799,9 +820,11 @@ namespace transport
if (m_IsRunning)
{
m_IsRunning = false;
delete m_NTCPAcceptor;
if (m_NTCPAcceptor)
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
delete m_NTCPV6Acceptor;
if (m_NTCPV6Acceptor)
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
m_Service.stop ();
@@ -946,7 +969,7 @@ namespace transport
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Connect error: ", ecode.message ());
LogPrint (eLogError, "NTCP: Can't connect to ", conn->GetSocket ().remote_endpoint (), ": ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();

View File

@@ -40,6 +40,7 @@ namespace transport
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>
@@ -144,7 +145,10 @@ namespace transport
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
boost::asio::io_service& GetService () { return m_Service; };
void Ban (const boost::asio::ip::address& addr);

195
NetDb.cpp
View File

@@ -2,10 +2,10 @@
#include <fstream>
#include <vector>
#include <boost/asio.hpp>
#include <openssl/rand.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "I2NPProtocol.h"
@@ -14,7 +14,6 @@
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
#include "util.h"
using namespace i2p::transport;
@@ -24,7 +23,7 @@ namespace data
{
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat")
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_HiddenMode(false)
{
}
@@ -52,7 +51,7 @@ namespace data
{
if (m_IsRunning)
{
for (auto it: m_RouterInfos)
for (auto& it: m_RouterInfos)
it.second->SaveProfile ();
DeleteObsoleteProfiles ();
m_RouterInfos.clear ();
@@ -67,12 +66,12 @@ namespace data
}
m_LeaseSets.clear();
m_Requests.Stop ();
}
}
}
void NetDb::Run ()
{
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0;
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
while (m_IsRunning)
{
try
@@ -118,10 +117,19 @@ namespace data
{
SaveUpdated ();
ManageLeaseSets ();
ManageLookupResponses ();
}
lastSave = ts;
}
if (ts - lastPublish >= 2400) // publish every 40 minutes
}
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
i2p::context.CleanupDestination ();
lastDestinationCleanup = ts;
}
// if we're in hidden mode don't publish or explore
// if (m_HiddenMode) continue;
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // publish
{
Publish ();
lastPublish = ts;
@@ -160,6 +168,11 @@ namespace data
return false;
}
void NetDb::SetHidden(bool hide) {
// TODO: remove reachable addresses from router info
m_HiddenMode = hide;
}
bool NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len)
{
bool updated = true;
@@ -173,10 +186,8 @@ namespace data
// TODO: check if floodfill has been changed
}
else
{
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
updated = false;
}
}
else
{
@@ -205,6 +216,7 @@ namespace data
bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
bool updated = false;
if (!from) // unsolicited LS must be received directly
{
@@ -216,29 +228,29 @@ namespace data
it->second->Update (buf, len);
if (it->second->IsValid ())
{
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase64());
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32());
updated = true;
}
else
{
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase64());
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase32());
m_LeaseSets.erase (it);
}
}
else
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase64());
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32());
}
else
{
auto leaseSet = std::make_shared<LeaseSet> (buf, len, false); // we don't need leases in netdb
if (leaseSet->IsValid ())
{
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase64());
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
updated = true;
}
else
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase64());
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase32());
}
}
return updated;
@@ -256,6 +268,7 @@ namespace data
std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
auto it = m_LeaseSets.find (destination);
if (it != m_LeaseSets.end ())
return it->second;
@@ -310,6 +323,13 @@ namespace data
return true;
}
void NetDb::VisitLeaseSets(LeaseSetVisitor v)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
for ( auto & entry : m_LeaseSets)
v(entry.first, entry.second);
}
void NetDb::Load ()
{
// make sure we cleanup netDb from previous attempts
@@ -319,7 +339,7 @@ namespace data
m_LastLoad = i2p::util::GetSecondsSinceEpoch();
std::vector<std::string> files;
m_Storage.Traverse(files);
for (auto path : files)
for (const auto& path : files)
LoadRouterInfo(path);
LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size (), " floodfils)");
@@ -337,7 +357,7 @@ namespace data
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
for (auto it: m_RouterInfos)
for (auto& it: m_RouterInfos)
{
std::string ident = it.second->GetIdentHashBase64();
std::string path = m_Storage.Path(ident);
@@ -385,7 +405,7 @@ namespace data
it = m_RouterInfos.erase (it);
continue;
}
it++;
++it;
}
}
// clean up expired floodfiils
@@ -395,7 +415,7 @@ namespace data
if ((*it)->IsUnreachable ())
it = m_Floodfills.erase (it);
else
it++;
++it;
}
}
}
@@ -449,12 +469,18 @@ namespace data
}
offset += 32;
}
// we must send reply back before this check
if (ident == i2p::context.GetIdentHash ())
{
LogPrint (eLogError, "NetDb: database store with own RouterInfo received, dropped");
return;
}
size_t payloadOffset = offset;
bool updated = false;
if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
{
LogPrint (eLogDebug, "NetDb: store request: LeaseSet");
LogPrint (eLogDebug, "NetDb: store request: LeaseSet for ", ident.ToBase32());
updated = AddLeaseSet (ident, buf + offset, len - offset, m->from);
}
else
@@ -480,25 +506,32 @@ namespace data
uint8_t * payload = floodMsg->GetPayload ();
memcpy (payload, buf, 33); // key + type
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token
auto msgLen = len - payloadOffset;
size_t msgLen = len - payloadOffset;
floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen;
if (floodMsg->len < floodMsg->maxLen)
{
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen);
floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore);
std::set<IdentHash> excluded;
for (int i = 0; i < 3; i++)
excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself
excluded.insert (ident); // don't flood back
for (int i = 0; i < 3; i++)
{
auto floodfill = GetClosestFloodfill (ident, excluded);
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), floodMsg);
{
auto h = floodfill->GetIdentHash();
LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64());
transports.SendMessage (h, CopyI2NPMessage(floodMsg));
excluded.insert (h);
}
else
break;
}
}
else
LogPrint (eLogError, "Database store message is too long ", floodMsg->len);
}
LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len);
}
}
void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
@@ -602,14 +635,18 @@ namespace data
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
IdentHash replyIdent(buf + 32);
uint8_t flag = buf[64];
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " recieved flags=", (int)flag);
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
const uint8_t * excluded = buf + 65;
uint32_t replyTunnelID = 0;
if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel
{
replyTunnelID = bufbe32toh (buf + 64);
replyTunnelID = bufbe32toh (excluded);
excluded += 4;
}
uint16_t numExcluded = bufbe16toh (excluded);
@@ -617,7 +654,7 @@ namespace data
if (numExcluded > 512)
{
LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512");
numExcluded = 0; // TODO:
return;
}
std::shared_ptr<I2NPMessage> replyMsg;
@@ -661,7 +698,12 @@ namespace data
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP))
{
auto leaseSet = FindLeaseSet (ident);
if (leaseSet && !leaseSet->IsExpired ()) // we don't send back our LeaseSets
if (!leaseSet)
{
// no lease set found
LogPrint(eLogDebug, "NetDb: requested LeaseSet not found for ", ident.ToBase32());
}
else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets
{
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
@@ -670,42 +712,64 @@ namespace data
if (!replyMsg)
{
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found. ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 32;
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
// find or cleate response
std::vector<IdentHash> closestFloodfills;
bool found = false;
if (!numExcluded)
{
auto it = m_LookupResponses.find (ident);
if (it != m_LookupResponses.end ())
{
closestFloodfills = it->second.first;
found = true;
}
}
if (!found)
{
std::set<IdentHash> excludedRouters;
const uint8_t * exclude_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (exclude_ident);
exclude_ident += 32;
}
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
if (!numExcluded) // save if no excluded
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
}
replyMsg = CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters, true));
}
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
}
}
excluded += numExcluded * 32;
if (replyMsg)
{
if (replyTunnelID)
{
// encryption might be used though tunnel only
if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested
if (flag & DATABASE_LOOKUP_ENCRYPTION_FLAG) // encrypted reply requested
{
const uint8_t * sessionKey = excluded;
uint8_t numTags = sessionKey[32];
if (numTags > 0)
const uint8_t numTags = excluded[32];
if (numTags)
{
const uint8_t * sessionTag = sessionKey + 33; // take first tag
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
}
else
LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided");
}
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
if (outbound)
outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
outbound->SendTunnelDataMsg (replyIdent, replyTunnelID, replyMsg);
else
transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
}
else
transports.SendMessage (buf+32, replyMsg);
transports.SendMessage (replyIdent, replyMsg);
}
}
@@ -830,13 +894,14 @@ namespace data
template<typename Filter>
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const
{
if (!m_RouterInfos.size ()) return 0;
uint32_t ind = rand () % m_RouterInfos.size ();
if (m_RouterInfos.empty())
return 0;
uint32_t ind = rand () % m_RouterInfos.size ();
for (int j = 0; j < 2; j++)
{
uint32_t i = 0;
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
for (auto it: m_RouterInfos)
for (const auto& it: m_RouterInfos)
{
if (i >= ind)
{
@@ -868,7 +933,7 @@ namespace data
else
minMetric.SetMax ();
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
for (const auto& it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
@@ -899,7 +964,7 @@ namespace data
if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash ();
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
for (const auto& it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
@@ -918,11 +983,11 @@ namespace data
std::vector<IdentHash> res;
size_t i = 0;
for (auto it: sorted)
for (const auto& it: sorted)
{
if (i < num)
{
auto& ident = it.r->GetIdentHash ();
const auto& ident = it.r->GetIdentHash ();
if (!excluded.count (ident))
{
res.push_back (ident);
@@ -935,6 +1000,14 @@ namespace data
return res;
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily(const std::string & fam) const {
return GetRandomRouter(
[fam](std::shared_ptr<const RouterInfo> router)->bool
{
return router->IsFamily(fam);
});
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
{
@@ -943,7 +1016,7 @@ namespace data
IdentHash destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
// must be called from NetDb thread only
for (auto it: m_RouterInfos)
for (const auto& it: m_RouterInfos)
{
if (!it.second->IsFloodfill ())
{
@@ -969,7 +1042,19 @@ namespace data
it = m_LeaseSets.erase (it);
}
else
it++;
++it;
}
}
void NetDb::ManageLookupResponses ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_LookupResponses.begin (); it != m_LookupResponses.end ();)
{
if (ts > it->second.second + 180) // 3 minutes
it = m_LookupResponses.erase (it);
else
++it;
}
}
}

26
NetDb.h
View File

@@ -8,7 +8,9 @@
#include <string>
#include <thread>
#include <mutex>
#include "Base.h"
#include "Gzip.h"
#include "FS.h"
#include "Queue.h"
#include "I2NPProtocol.h"
@@ -29,6 +31,10 @@ namespace data
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60;
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours
const int NETDB_PUBLISH_INTERVAL = 60*40;
/** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
class NetDb
{
@@ -62,10 +68,14 @@ namespace data
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(const std::string & fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
/** set hidden mode, aka don't publish our RI to netdb and don't explore */
void SetHidden(bool hide);
void Reseed ();
Families& GetFamilies () { return m_Families; };
@@ -73,7 +83,10 @@ namespace data
int GetNumRouters () const { return m_RouterInfos.size (); };
int GetNumFloodfills () const { return m_Floodfills.size (); };
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
/** visit all lease sets we currently store */
void VisitLeaseSets(LeaseSetVisitor v);
private:
void Load ();
@@ -84,12 +97,14 @@ namespace data
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;
@@ -108,6 +123,11 @@ namespace data
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;

View File

@@ -141,7 +141,7 @@ namespace data
if (done)
it = m_RequestedDestinations.erase (it);
else
it++;
++it;
}
}
}

View File

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

51
Queue.h
View File

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

View File

@@ -1,41 +1,55 @@
i2pd
====
Independent C++ implementation of I2P router
i2pd is a full-featured C++ implementation of
[I2P](https://geti2p.net/en/about/intro) client.
I2P (Invisible Internet Project) is anonymous network which works on top of
public Internet. Privacy and anonymity are achieved by strong encryption and
bouncing your traffic through thousands of I2P nodes all around the world.
We are building network which helps people to communicate and share information
without restrictions.
* [Website](http://i2pd.website)
* [Documentation](https://i2pd.readthedocs.io/en/latest/)
* [Wiki](https://github.com/PurpleI2P/i2pd/wiki)
* [Tickets/Issues](https://github.com/PurpleI2P/i2pd/issues)
* [Twitter](https://twitter.com/i2porignal)
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
Using i2pd
----------
See [documentation](https://i2pd.readthedocs.io/en/latest/) and
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf).
Donations
---------
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y
License
-------
This project is licensed under the BSD 3-clause license, which can be found in the file
LICENSE in the root of the project source code.
Donations
---------
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
Downloads
------------
Official binary releases could be found at:
http://i2pd.website/releases/
older releases
http://download.i2p.io/purplei2p/i2pd/releases/
Supported OS
------------
* Linux x86/x64 - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
* Mac OS X
* FreeBSD
More documentation
------------------
* [Building from source / unix](docs/build_notes_unix.md)
* [Building from source / windows](docs/build_notes_windows.md)
* [Configuring your i2pd](docs/configuration.md)
* [Github wiki](https://github.com/PurpleI2P/i2pd/wiki/)

View File

@@ -1,43 +1,47 @@
#include <string.h>
#include <fstream>
#include <sstream>
#include <boost/regex.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/bn.h>
#include <boost/asio/ssl.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 "Crypto.h"
#include "NetDb.h"
#include "HTTP.h"
#include "util.h"
#include "Config.h"
namespace i2p
{
namespace data
{
static std::vector<std::string> httpsReseedHostList =
static std::vector<std::string> httpsReseedHostList =
{
#ifdef MESHNET
// meshnet i2p reseeds
"https://reseed.i2p.rocks:8443/"
#else
// mainline i2p reseeds
"https://reseed.i2p-projekt.de/", // Only HTTPS
//"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v3) support
"https://i2p.mooo.com/netDb/",
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
"https://i2p.mooo.com/netDb/",
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
"https://us.reseed.i2p2.no:444/",
"https://uk.reseed.i2p2.no:444/",
"https://www.torontocrypto.org:8443/",
"https://i2p-0.manas.ca:8443/"
"https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
"https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
"https://download.xxlspeed.com/" // Only HTTPS and SU3 (v3) support
"https://uk.reseed.i2p2.no:444/",
"https://i2p.manas.ca:8443/",
"https://i2p-0.manas.ca:8443/",
"https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
"https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
"https://download.xxlspeed.com/" // Only HTTPS and SU3 (v3) support
#endif
};
Reseeder::Reseeder()
{
}
@@ -48,6 +52,13 @@ namespace data
int Reseeder::ReseedNowSU3 ()
{
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);
@@ -370,13 +381,19 @@ namespace data
std::string Reseeder::HttpsRequest (const std::string& address)
{
i2p::util::http::url u(address);
if (u.port_ == 80) u.port_ = 443;
i2p::http::URL url;
if (!url.parse(address)) {
LogPrint(eLogError, "Reseed: failed to parse url: ", address);
return "";
}
url.schema = "https";
if (!url.port)
url.port = 443;
boost::asio::io_service service;
boost::system::error_code ecode;
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode);
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
if (!ecode)
{
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
@@ -388,32 +405,52 @@ namespace data
s.handshake (boost::asio::ssl::stream_base::client, ecode);
if (!ecode)
{
LogPrint (eLogInfo, "Reseed: Connected to ", u.host_, ":", u.port_);
// send request
std::stringstream ss;
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
s.write_some (boost::asio::buffer (ss.str ()));
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
i2p::http::HTTPReq req;
req.uri = url.to_string();
req.add_header("User-Agent", "Wget/1.11.4");
req.add_header("Connection", "close");
s.write_some (boost::asio::buffer (req.to_string()));
// read response
std::stringstream rs;
char response[1024]; size_t l = 0;
do
{
l = s.read_some (boost::asio::buffer (response, 1024), ecode);
if (l) rs.write (response, l);
}
while (!ecode && l);
char recv_buf[1024]; size_t l = 0;
do {
l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode);
if (l) rs.write (recv_buf, l);
} while (!ecode && l);
// process response
return i2p::util::http::GetHttpContent (rs);
std::string data = rs.str();
i2p::http::HTTPRes res;
int len = res.parse(data);
if (len <= 0) {
LogPrint(eLogWarning, "Reseed: incomplete/broken response from ", url.host);
return "";
}
if (res.code != 200) {
LogPrint(eLogError, "Reseed: failed to reseed from ", url.host, ", http code ", res.code);
return "";
}
data.erase(0, len); /* drop http headers from response */
LogPrint(eLogDebug, "Reseed: got ", data.length(), " bytes of data from ", url.host);
if (res.is_chunked()) {
std::stringstream in(data), out;
if (!i2p::http::MergeChunkedResponse(in, out)) {
LogPrint(eLogWarning, "Reseed: failed to merge chunked response from ", url.host);
return "";
}
LogPrint(eLogDebug, "Reseed: got ", data.length(), "(", out.tellg(), ") bytes of data from ", url.host);
data = out.str();
}
return data;
}
else
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to ", u.host_, ": ", ecode.message ());
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't resolve address ", u.host_, ": ", ecode.message ());
LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
return "";
}
}

View File

@@ -49,14 +49,34 @@ namespace i2p
uint16_t port; i2p::config::GetOption("port", port);
if (!port)
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
std::string host; i2p::config::GetOption("host", host);
if (i2p::config::IsDefault("host"))
host = "127.0.0.1"; // replace default address with safe value
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ipv4)
{
std::string host = "127.0.0.1";
if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host);
else if (!nat && !ifname.empty())
/* bind to interface, we have no NAT so set external address too */
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
}
if (ipv6)
{
std::string host = "::";
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
i2p::config::GetOption("host", host);
else if (!ifname.empty())
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
}
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
@@ -94,9 +114,9 @@ namespace i2p
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;
}
}
@@ -109,9 +129,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;
}
}
@@ -149,6 +169,11 @@ namespace i2p
UpdateRouterInfo ();
}
std::string RouterContext::GetFamily () const
{
return m_RouterInfo.GetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
}
void RouterContext::SetFamily (const std::string& family)
{
std::string signature;
@@ -165,34 +190,49 @@ namespace i2p
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG);
}
}
void RouterContext::SetHighBandwidth ()
{
if (!m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
void RouterContext::SetBandwidth (char L) {
uint16_t limit = 0;
enum { low, high, extra } type = high;
/* detect parameters */
switch (L)
{
m_RouterInfo.SetCaps ((m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth) & ~i2p::data::RouterInfo::eExtraBandwidth);
UpdateRouterInfo ();
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::SetLowBandwidth ()
void RouterContext::SetBandwidth (int limit)
{
if (m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth & ~i2p::data::RouterInfo::eExtraBandwidth);
UpdateRouterInfo ();
}
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'); }
}
void RouterContext::SetExtraBandwidth ()
{
if (!m_RouterInfo.IsExtraBandwidth () || !m_RouterInfo.IsHighBandwidth ())
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eExtraBandwidth | i2p::data::RouterInfo::eHighBandwidth);
UpdateRouterInfo ();
}
}
bool RouterContext::IsUnreachable () const
{
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
@@ -204,17 +244,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 ();
@@ -233,18 +274,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 from SSU
m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port);
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
// delete previous introducers
for (auto& addr : addresses)
addr.introducers.clear ();
addr->introducers.clear ();
// update
UpdateRouterInfo ();
@@ -257,26 +299,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)
{
@@ -331,16 +383,24 @@ namespace i2p
delete[] buf;
}
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); // TODO
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
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 ();
}
// Migration to 0.9.24. TODO: remove later
m_RouterInfo.DeleteProperty ("coreVersion");
m_RouterInfo.DeleteProperty ("stat_uptime");
if (IsUnreachable ())
SetReachable (); // we assume reachable until we discover firewall through peer tests
@@ -379,6 +439,12 @@ namespace i2p
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
void RouterContext::CleanupDestination ()
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::CleanupExpiredTags ();
}
uint32_t RouterContext::GetUptime () const
{

View File

@@ -30,6 +30,7 @@ namespace i2p
RouterContext ();
void Init ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
{
@@ -45,6 +46,7 @@ namespace i2p
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);
@@ -58,24 +60,29 @@ namespace i2p
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
void SetFamily (const std::string& family);
void SetHighBandwidth ();
void SetLowBandwidth ();
void SetExtraBandwidth ();
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; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
void SetLeaseSetUpdated () {};
// implements GarlicDestination
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@@ -98,6 +105,7 @@ namespace i2p
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;
std::mutex m_GarlicMutex;
};

View File

@@ -15,10 +15,16 @@ namespace i2p
{
namespace data
{
RouterInfo::RouterInfo (): m_Buffer (nullptr)
{
m_Addresses = std::make_shared<Addresses>(); // create empty list
}
RouterInfo::RouterInfo (const std::string& fullPath):
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0), m_Caps (0)
{
m_Addresses = std::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
ReadFromFile ();
}
@@ -26,6 +32,7 @@ namespace data
RouterInfo::RouterInfo (const uint8_t * buf, int len):
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
{
m_Addresses = std::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
@@ -48,7 +55,7 @@ namespace data
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
// don't clean up m_Addresses, it will be replaced in ReadFromStream
m_Properties.clear ();
// copy buffer
if (!m_Buffer)
@@ -77,7 +84,7 @@ 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);
@@ -104,6 +111,8 @@ namespace data
{
if (LoadFile ())
ReadFromBuffer (false);
else
m_IsUnreachable = true;
}
void RouterInfo::ReadFromBuffer (bool verifySignature)
@@ -142,6 +151,7 @@ namespace data
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
auto addresses = std::make_shared<Addresses>();
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
@@ -232,10 +242,11 @@ namespace data
}
if (isValidAddress)
{
m_Addresses.push_back(address);
addresses->push_back(std::make_shared<Address>(address));
m_SupportedTransports |= supportedTransports;
}
}
m_Addresses = addresses;
// read peers
uint8_t numPeers;
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
@@ -286,9 +297,13 @@ namespace data
if (!s) return;
}
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers))
SetUnreachable (true);
}
}
bool RouterInfo::IsFamily(const std::string & fam) const {
return m_Family == fam;
}
void RouterInfo::ExtractCaps (const char * value)
{
@@ -333,36 +348,40 @@ namespace data
void RouterInfo::UpdateCapsProperty ()
{
std::string caps;
if (m_Caps & eFloodfill)
{
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
if (m_Caps & eFloodfill) {
caps += CAPS_FLAG_FLOODFILL; // floodfill
}
else
{
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1;
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
caps += (m_Caps & eExtraBandwidth)
? CAPS_FLAG_EXTRA_BANDWIDTH1 // 'P'
: CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
} else {
if (m_Caps & eExtraBandwidth) {
caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
} else if (m_Caps & eHighBandwidth) {
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
} else {
caps += CAPS_FLAG_LOW_BANDWIDTH2; // 'L'
}
}
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);
@@ -391,7 +410,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 << '=';
@@ -400,7 +419,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 << '=';
@@ -412,7 +431,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 << '=';
@@ -421,7 +440,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 << '=';
@@ -463,7 +482,7 @@ namespace data
// properties
std::stringstream properties;
for (auto& p : m_Properties)
for (const auto& p : m_Properties)
{
WriteString (p.first, properties);
properties << '=';
@@ -510,19 +529,20 @@ namespace data
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);
if (f.is_open ())
f.write ((char *)m_Buffer, m_BufferLen);
else
LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath);
}
else
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)
@@ -543,46 +563,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;
for (auto it: m_Addresses) // don't insert same address twice
if (it == addr) return;
m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4;
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);
for (auto it: m_Addresses) // don't insert same address twice
if (it == addr) return;
m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eSSUV6 : 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 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)
for (auto& intro: addr->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
addr.introducers.push_back (introducer);
addr->introducers.push_back (introducer);
return true;
}
}
@@ -591,16 +612,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;
@@ -629,6 +650,14 @@ namespace data
m_Properties.erase (key);
}
std::string RouterInfo::GetProperty (const std::string& key) const
{
auto it = m_Properties.find (key);
if (it != m_Properties.end ())
return it->second;
return "";
}
bool RouterInfo::IsNTCP (bool v4only) const
{
if (v4only)
@@ -650,70 +679,86 @@ 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)
auto addresses = m_Addresses;
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,6 +5,7 @@
#include <string>
#include <map>
#include <vector>
#include <list>
#include <iostream>
#include <boost/asio.hpp>
#include "Identity.h"
@@ -24,13 +25,14 @@ namespace data
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';
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P';
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X';
/* 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';
@@ -104,9 +106,10 @@ namespace data
return !(*this == other);
}
};
typedef std::list<std::shared_ptr<Address> > Addresses;
RouterInfo ();
RouterInfo (const std::string& fullPath);
RouterInfo (): m_Buffer (nullptr) { };
RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (const uint8_t * buf, int len);
@@ -116,10 +119,10 @@ namespace data
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<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);
@@ -127,14 +130,18 @@ namespace data
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 { 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; };
@@ -157,7 +164,7 @@ 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 (); };
@@ -166,23 +173,25 @@ namespace data
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
bool IsNewer (const uint8_t * buf, size_t len) const;
/** return true if we are in a router family and the signature is valid */
bool IsFamily(const std::string & fam) const;
// implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_RouterIdentity->GetIdentHash (); };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity->GetStandardIdentity ().publicKey; };
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;
static size_t ReadString (char* str, std::istream& s);
static void WriteString (const std::string& str, std::ostream& s);
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:
@@ -192,7 +201,7 @@ namespace data
uint8_t * m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp;
std::vector<Address> m_Addresses;
std::shared_ptr<Addresses> m_Addresses;
std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_Caps;

63
SAM.cpp
View File

@@ -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 ()
@@ -369,7 +371,7 @@ namespace client
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 ();
@@ -402,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);
}
@@ -676,19 +678,20 @@ 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 (const std::string& address, int port):
@@ -716,8 +719,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)
@@ -771,7 +774,7 @@ namespace client
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;
@@ -796,8 +799,9 @@ namespace client
}
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 (eLogWarning, "SAM: Session ", id, " already exists");
return ret.first->second;
@@ -807,19 +811,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);

33
SAM.h
View File

@@ -128,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 ();
@@ -153,10 +176,10 @@ namespace client
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:
@@ -177,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:

View File

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

107
SSU.cpp
View File

@@ -10,12 +10,31 @@ 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_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_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 +54,16 @@ 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));
}
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));
}
SchedulePeerTestsCleanupTimer ();
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
@@ -215,9 +237,8 @@ namespace transport
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
{
std::shared_ptr<SSUSession> session;
for (auto it1: packets)
for (auto& packet: packets)
{
auto packet = it1;
try
{
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
@@ -231,7 +252,7 @@ namespace transport
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
(*sessions)[packet->from] = session;
LogPrint (eLogInfo, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
}
}
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
@@ -312,7 +333,7 @@ namespace transport
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
sessions[remoteEndpoint] = session;
// connect
LogPrint (eLogInfo, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect ();
}
@@ -364,10 +385,10 @@ namespace transport
}
if (introducerSession) // session found
LogPrint (eLogInfo, "SSU: Session to introducer already exists");
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
else // create new
{
LogPrint (eLogInfo, "SSU: Creating new session to introducer");
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession;
@@ -409,11 +430,11 @@ namespace transport
void SSUServer::DeleteAllSessions ()
{
for (auto it: m_Sessions)
for (auto& it: m_Sessions)
it.second->Close ();
m_Sessions.clear ();
for (auto it: m_SessionsV6)
for (auto& it: m_SessionsV6)
it.second->Close ();
m_SessionsV6.clear ();
}
@@ -422,7 +443,7 @@ namespace transport
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
{
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
for (auto s :m_Sessions)
for (const auto& s :m_Sessions)
if (filter (s.second)) filteredSessions.push_back (s.second);
if (filteredSessions.size () > 0)
{
@@ -437,8 +458,31 @@ namespace transport
return GetRandomV4Session (
[excluded](std::shared_ptr<SSUSession> session)->bool
{
return session->GetState () == eSessionStateEstablished && !session->IsV6 () &&
session != excluded;
return session->GetState () == eSessionStateEstablished && session != excluded;
}
);
}
template<typename Filter>
std::shared_ptr<SSUSession> SSUServer::GetRandomV6Session (Filter filter) // v6 only
{
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
for (const auto& s :m_SessionsV6)
if (filter (s.second)) filteredSessions.push_back (s.second);
if (filteredSessions.size () > 0)
{
auto ind = rand () % filteredSessions.size ();
return filteredSessions[ind];
}
return nullptr;
}
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded) // v6 only
{
return GetRandomV6Session (
[excluded](std::shared_ptr<SSUSession> session)->bool
{
return session->GetState () == eSessionStateEstablished && session != excluded;
}
);
}
@@ -490,7 +534,7 @@ namespace transport
std::list<boost::asio::ip::udp::endpoint> newList;
size_t numIntroducers = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it :m_Introducers)
for (const auto& it : m_Introducers)
{
auto session = FindSession (it);
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
@@ -507,23 +551,20 @@ namespace transport
{
// create new
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
if (introducers.size () > 0)
for (const auto& it1: introducers)
{
for (auto it1: introducers)
const auto& ep = it1->GetRemoteEndpoint ();
i2p::data::RouterInfo::Introducer introducer;
introducer.iHost = ep.address ();
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
if (i2p::context.AddIntroducer (introducer))
{
auto& ep = it1->GetRemoteEndpoint ();
i2p::data::RouterInfo::Introducer introducer;
introducer.iHost = ep.address ();
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
if (i2p::context.AddIntroducer (introducer))
{
newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
}
}
}
newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
}
}
}
m_Introducers = newList;
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
@@ -592,7 +633,7 @@ namespace transport
it = m_PeerTests.erase (it);
}
else
it++;
++it;
}
if (numDeleted > 0)
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");

11
SSU.h
View File

@@ -37,6 +37,7 @@ namespace transport
public:
SSUServer (int port);
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
~SSUServer ();
void Start ();
void Stop ();
@@ -47,6 +48,7 @@ namespace transport
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
void DeleteSession (std::shared_ptr<SSUSession> session);
void DeleteAllSessions ();
@@ -62,7 +64,7 @@ namespace transport
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest (uint32_t nonce);
private:
void Run ();
@@ -78,7 +80,9 @@ namespace transport
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
@@ -93,8 +97,9 @@ 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;

View File

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

View File

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

View File

@@ -1,7 +1,5 @@
#include <boost/bind.hpp>
#include <openssl/dh.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "RouterContext.h"
@@ -14,7 +12,8 @@ namespace i2p
namespace transport
{
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ): TransportSession (router),
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ):
TransportSession (router, SSU_TERMINATION_TIMEOUT),
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false),
m_RelayTag (0),m_Data (*this), m_IsDataReceived (false)
@@ -22,14 +21,14 @@ namespace transport
if (router)
{
// we are client
auto address = router->GetSSUAddress ();
auto address = router->GetSSUAddress (false);
if (address) m_IntroKey = address->key;
m_Data.AdjustPacketSize (router); // mtu
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (address) m_IntroKey = address->key;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
@@ -110,7 +109,7 @@ namespace transport
else
{
// try own intro key
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported");
@@ -265,8 +264,6 @@ namespace transport
uint16_t ourPort = bufbe16toh (payload);
s.Insert (payload, 2); // our port
payload += 2; // port
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
if (m_RemoteEndpoint.address ().is_v4 ())
s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4
else
@@ -283,11 +280,18 @@ namespace transport
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv);
m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); // TODO: non-const payload
// verify
if (!s.Verify (m_RemoteIdentity, payload))
// verify signature
if (s.Verify (m_RemoteIdentity, payload))
{
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
SendSessionConfirmed (y, ourAddress, addressSize + 2);
}
else
{
LogPrint (eLogError, "SSU: message 'created' signature verification failed");
SendSessionConfirmed (y, ourAddress, addressSize + 2);
Failed ();
}
}
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
@@ -313,11 +317,17 @@ namespace transport
paddingSize &= 0x0F; // %16
if (paddingSize > 0) paddingSize = 16 - paddingSize;
payload += paddingSize;
// verify
if (m_SignedData && !m_SignedData->Verify (m_RemoteIdentity, payload))
// verify signature
if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload))
{
m_Data.Send (CreateDeliveryStatusMsg (0));
Established ();
}
else
{
LogPrint (eLogError, "SSU message 'confirmed' signature verification failed");
m_Data.Send (CreateDeliveryStatusMsg (0));
Established ();
Failed ();
}
}
void SSUSession::SendSessionRequest ()
@@ -357,7 +367,7 @@ namespace transport
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce)
{
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported");
@@ -805,7 +815,7 @@ namespace transport
if (!ecode)
{
// timeout expired
LogPrint (eLogWarning, "SSU: session was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
LogPrint (eLogWarning, "SSU: session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
Failed ();
}
}
@@ -855,7 +865,6 @@ namespace transport
m_DHKeysPair = nullptr;
m_SignedData = nullptr;
m_Data.Start ();
m_Data.Send (CreateDatabaseStoreMsg ());
transports.PeerConnected (shared_from_this ());
if (m_IsPeerTest)
SendPeerTest ();
@@ -874,7 +883,7 @@ namespace transport
void SSUSession::ScheduleTermination ()
{
m_Timer.cancel ();
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT));
m_Timer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer,
shared_from_this (), std::placeholders::_1));
}
@@ -883,7 +892,7 @@ namespace transport
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "SSU: no activity for ", SSU_TERMINATION_TIMEOUT, " seconds");
LogPrint (eLogWarning, "SSU: no activity with ", m_RemoteEndpoint, " for ", GetTerminationTimeout (), " seconds");
Failed ();
}
}
@@ -898,7 +907,7 @@ namespace transport
{
if (m_State == eSessionStateEstablished)
{
for (auto it: msgs)
for (const auto& it: msgs)
if (it) m_Data.Send (it);
}
}
@@ -922,10 +931,10 @@ namespace transport
{
uint32_t nonce = bufbe32toh (buf); // 4 bytes
uint8_t size = buf[4]; // 1 byte
uint32_t address = (size == 4) ? buf32toh(buf + 5) : 0; // big endian, size bytes
const uint8_t * address = buf + 5; // big endian, size bytes
uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes
const uint8_t * introKey = buf + size + 7;
if (port && !address)
if (port && (size != 4) && (size != 16))
{
LogPrint (eLogWarning, "SSU: Address of ", size, " bytes not supported");
return;
@@ -946,8 +955,7 @@ namespace transport
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
i2p::context.SetStatus (eRouterStatusOK);
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey, true, false); // to Charlie
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
}
break;
}
@@ -976,8 +984,7 @@ namespace transport
case ePeerTestParticipantCharlie:
{
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Charlie");
SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey); // to Alice with her actual address
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address
m_Server.RemovePeerTest (nonce); // nonce has been used
break;
}
@@ -992,17 +999,29 @@ namespace transport
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice with her address received from Bob
boost::asio::ip::address addr; // Alice's address
if (size == 4) // v4
{
boost::asio::ip::address_v4::bytes_type bytes;
memcpy (bytes.data (), address, 4);
addr = boost::asio::ip::address_v4 (bytes);
}
else // v6
{
boost::asio::ip::address_v6::bytes_type bytes;
memcpy (bytes.data (), address, 6);
addr = boost::asio::ip::address_v6 (bytes);
}
SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob
}
else
{
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Bob");
auto session = m_Server.GetRandomEstablishedV4Session (shared_from_this ()); // Charlie, TODO: implement v6 support
auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie
if (session)
{
m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ());
session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
session->SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
}
}
}
@@ -1012,7 +1031,7 @@ namespace transport
}
}
void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port,
void SSUSession::SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port,
const uint8_t * introKey, bool toAddress, bool sendAddress)
// toAddress is true for Alice<->Chalie communications only
// sendAddress is false if message comes from Alice
@@ -1023,12 +1042,21 @@ namespace transport
htobe32buf (payload, nonce);
payload += 4; // nonce
// address and port
if (sendAddress && address)
{
*payload = 4;
payload++; // size
htobe32buf (payload, address);
payload += 4; // address
if (sendAddress)
{
if (address.is_v4 ())
{
*payload = 4;
memcpy (payload + 1, address.to_v4 ().to_bytes ().data (), 4); // our IP V4
}
else if (address.is_v6 ())
{
*payload = 6;
memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6
}
else
*payload = 0;
payload += (payload[0] + 1);
}
else
{
@@ -1056,7 +1084,7 @@ namespace transport
{
// encrypt message with specified intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey);
boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port);
boost::asio::ip::udp::endpoint e (address, port);
m_Server.Send (buf, 80, e);
}
else
@@ -1071,7 +1099,7 @@ namespace transport
{
// we are Alice
LogPrint (eLogDebug, "SSU: sending peer test");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
@@ -1082,7 +1110,7 @@ namespace transport
if (!nonce) nonce = 1;
m_IsPeerTest = false;
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1);
SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice
}
void SSUSession::SendKeepAlive ()

View File

@@ -118,7 +118,7 @@ namespace transport
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

View File

@@ -435,8 +435,13 @@ namespace crypto
std::unique_ptr<Ed25519>& GetEd25519 ()
{
if (!g_Ed25519)
g_Ed25519.reset (new 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;
}

View File

@@ -3,12 +3,10 @@
#include <inttypes.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/dsa.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include "Crypto.h"

View File

@@ -1,4 +1,4 @@
#include <openssl/rand.h>
#include "Crypto.h"
#include "Log.h"
#include "RouterInfo.h"
#include "RouterContext.h"
@@ -36,7 +36,6 @@ namespace stream
Stream::~Stream ()
{
Terminate ();
while (!m_ReceiveQueue.empty ())
{
auto packet = m_ReceiveQueue.front ();
@@ -66,6 +65,7 @@ namespace stream
m_SendHandler = nullptr;
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted));
}
m_LocalDestination.DeleteStream (shared_from_this ());
}
void Stream::HandleNextPacket (Packet * packet)
@@ -87,7 +87,7 @@ namespace stream
return;
}
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn);
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID);
if (receivedSeqn == m_LastReceivedSequenceNumber + 1)
{
// we have received next in sequence message
@@ -129,13 +129,13 @@ namespace stream
if (receivedSeqn <= m_LastReceivedSequenceNumber)
{
// we have received duplicate
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " received");
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID);
SendQuickAck (); // resend ack for previous message again
delete packet; // packet dropped
}
else
{
LogPrint (eLogWarning, "Streaming: Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
// save message and wait for missing message again
SavePacket (packet);
if (m_LastReceivedSequenceNumber >= 0)
@@ -162,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)
@@ -182,7 +183,7 @@ namespace stream
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, packet->GetOptionSize ());
optionData += m_RemoteIdentity->GetFullLen ();
if (!m_RemoteLeaseSet)
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 ());
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
}
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
@@ -200,7 +201,7 @@ namespace stream
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
{
LogPrint (eLogError, "Streaming: Signature verification failed");
LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
Close ();
flags |= PACKET_FLAG_CLOSE;
}
@@ -219,11 +220,19 @@ namespace stream
m_LastReceivedSequenceNumber = receivedSeqn;
if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET))
if (flags & PACKET_FLAG_RESET)
{
LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn);
m_Status = eStreamStatusReset;
Close ();
}
else if (flags & PACKET_FLAG_CLOSE)
{
if (m_Status != eStreamStatusClosed)
SendClose ();
m_Status = eStreamStatusClosed;
Terminate ();
}
}
void Stream::ProcessAck (Packet * packet)
@@ -231,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)
{
@@ -249,7 +263,7 @@ namespace stream
if (nacked)
{
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK");
it++;
++it;
continue;
}
}
@@ -257,7 +271,7 @@ namespace stream
uint64_t rtt = ts - sentPacket->sendTime;
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);
m_SentPackets.erase (it++);
delete sentPacket;
acknowledged = true;
@@ -288,8 +302,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)
@@ -335,9 +351,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
@@ -389,11 +405,14 @@ namespace stream
}
if (packets.size () > 0)
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
if (m_SavedPackets.empty ()) // no NACKS
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
}
bool isEmpty = m_SentPackets.empty ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: packets)
for (auto& it: packets)
{
it->sendTime = ts;
m_SentPackets.insert (it);
@@ -442,7 +461,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;
}
@@ -477,35 +496,32 @@ namespace stream
void Stream::Close ()
{
LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status);
switch (m_Status)
{
case eStreamStatusOpen:
m_Status = eStreamStatusClosing;
Close (); // recursion
if (m_Status == eStreamStatusClosing) //still closing
LogPrint (eLogInfo, "Streaming: Trying to send stream data before closing");
LogPrint (eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", m_SendStreamID);
break;
case eStreamStatusReset:
SendClose ();
Terminate ();
m_LocalDestination.DeleteStream (shared_from_this ());
// TODO: send reset
Terminate ();
break;
case eStreamStatusClosing:
if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send
{
m_Status = eStreamStatusClosed;
SendClose ();
Terminate ();
m_LocalDestination.DeleteStream (shared_from_this ());
}
break;
case eStreamStatusClosed:
// already closed
Terminate ();
m_LocalDestination.DeleteStream (shared_from_this ());
break;
default:
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status);
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID);
};
}
@@ -520,7 +536,7 @@ namespace stream
size += 4; // receiveStreamID
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
@@ -537,7 +553,7 @@ namespace stream
p->len = size;
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
LogPrint (eLogDebug, "Streaming: FIN sent");
LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID);
}
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
@@ -569,15 +585,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
@@ -591,7 +602,7 @@ namespace stream
UpdateCurrentRemoteLease ();
if (!m_RemoteLeaseSet)
{
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet");
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID);
return;
}
}
@@ -616,7 +627,7 @@ namespace stream
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel)
{
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool");
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID);
return;
}
@@ -640,7 +651,7 @@ namespace stream
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
}
else
LogPrint (eLogWarning, "Streaming: All leases are expired");
LogPrint (eLogWarning, "Streaming: All leases are expired, sSID=", m_SendStreamID);
}
@@ -659,7 +670,7 @@ namespace stream
// check for resend attempts
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
{
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate");
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
m_Status = eStreamStatusReset;
Close ();
return;
@@ -694,13 +705,13 @@ namespace stream
case 4:
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
UpdateCurrentRemoteLease (); // pick another lease
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream");
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
break;
case 3:
// pick another outbound tunnel
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream");
LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID);
break;
default: ;
}
@@ -716,7 +727,7 @@ namespace stream
{
if (m_LastReceivedSequenceNumber < 0)
{
LogPrint (eLogWarning, "Streaming: SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate");
LogPrint (eLogWarning, "Streaming: SYN has not been received after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
m_Status = eStreamStatusReset;
Close ();
return;
@@ -751,7 +762,7 @@ namespace stream
bool updated = false;
if (expired && m_CurrentRemoteLease)
{
for (auto it: leases)
for (const auto& it: leases)
if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID))
{
m_CurrentRemoteLease = it;
@@ -787,7 +798,7 @@ namespace stream
StreamingDestination::~StreamingDestination ()
{
for (auto it: m_SavedPackets)
for (auto& it: m_SavedPackets)
{
for (auto it1: it.second) delete it1;
it.second.clear ();
@@ -819,7 +830,7 @@ namespace stream
it->second->HandleNextPacket (packet);
else
{
LogPrint (eLogError, "Streaming: Unknown stream sendStreamID=", sendStreamID);
LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID);
delete packet;
}
}
@@ -835,7 +846,7 @@ namespace stream
auto it = m_SavedPackets.find (receiveStreamID);
if (it != m_SavedPackets.end ())
{
LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for receiveStreamID=", receiveStreamID);
LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for rSID=", receiveStreamID);
for (auto it1: it->second)
incomingStream->HandleNextPacket (it1);
m_SavedPackets.erase (it);
@@ -854,7 +865,7 @@ namespace stream
m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT));
m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer,
shared_from_this (), std::placeholders::_1));
LogPrint (eLogDebug, "Streaming: Pending incoming stream added");
LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID);
}
else
{
@@ -866,7 +877,7 @@ namespace stream
else // follow on packet without SYN
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
for (auto it: m_Streams)
for (auto& it: m_Streams)
if (it.second->GetSendStreamID () == receiveStreamID)
{
// found
@@ -933,7 +944,7 @@ namespace stream
m_Owner->GetService ().post([acceptor, this](void)
{
m_Acceptor = acceptor;
for (auto it: m_PendingIncomingStreams)
for (auto& it: m_PendingIncomingStreams)
if (it->GetStatus () == eStreamStatusOpen) // still open?
m_Acceptor (it);
m_PendingIncomingStreams.clear ();
@@ -952,7 +963,7 @@ namespace stream
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired");
for (auto it: m_PendingIncomingStreams)
for (auto& it: m_PendingIncomingStreams)
it->Close ();
m_PendingIncomingStreams.clear ();
}

90
Tag.h Normal file
View File

@@ -0,0 +1,90 @@
/*
* 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 TAG_H__
#define TAG_H__
#include <string.h> /* memcpy */
#include "Base.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; };
bool IsZero () const
{
for (int i = 0; i < sz/8; i++)
if (ll[i]) return false;
return true;
}
std::string ToBase64 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
std::string ToBase32 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
} // data
} // i2p
#endif /* TAG_H__ */

View File

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

View File

@@ -53,8 +53,8 @@ namespace transport
{
public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router)
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout)
{
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
@@ -70,6 +70,9 @@ namespace transport
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
int GetTerminationTimeout () const { return m_TerminationTimeout; };
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
protected:
@@ -78,6 +81,7 @@ namespace transport
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
int m_TerminationTimeout;
};
}
}

View File

@@ -1,4 +1,3 @@
#include <openssl/dh.h>
#include "Log.h"
#include "Crypto.h"
#include "RouterContext.h"
@@ -47,7 +46,7 @@ namespace transport
int num;
while ((num = m_QueueSize - m_Queue.size ()) > 0)
CreateDHKeysPairs (num);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Acquired.wait (l); // wait for element gets aquired
}
}
@@ -61,7 +60,7 @@ namespace transport
{
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
}
@@ -70,7 +69,7 @@ namespace transport
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
{
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
if (!m_Queue.empty ())
{
auto pair = m_Queue.front ();
@@ -87,14 +86,14 @@ namespace transport
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
Transports transports;
Transports::Transports ():
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
@@ -106,28 +105,45 @@ namespace transport
Stop ();
}
void Transports::Start ()
void Transports::Start (bool enableNTCP, bool enableSSU)
{
m_DHKeysPairSupplier.Start ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this));
// create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses)
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address : addresses)
{
if (!m_NTCPServer)
{
if (m_NTCPServer == nullptr && enableNTCP)
{
m_NTCPServer = new NTCPServer ();
m_NTCPServer->Start ();
if (!(m_NTCPServer->IsBoundV6() || m_NTCPServer->IsBoundV4())) {
/** failed to bind to NTCP */
LogPrint(eLogError, "Transports: failed to bind to TCP");
m_NTCPServer->Stop();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
}
if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
if (address->transportStyle == RouterInfo::eTransportSSU)
{
if (!m_SSUServer)
{
m_SSUServer = new SSUServer (address.port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address.port);
m_SSUServer->Start ();
if (m_SSUServer == nullptr && enableSSU)
{
if (address->host.is_v4())
m_SSUServer = new SSUServer (address->port);
else
m_SSUServer = new SSUServer (address->host, address->port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
try {
m_SSUServer->Start ();
} catch ( std::exception & ex ) {
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
delete m_SSUServer;
m_SSUServer = nullptr;
continue;
}
DetectExternalIP ();
}
else
@@ -200,14 +216,14 @@ namespace transport
bool Transports::IsBandwidthExceeded () const
{
if (i2p::context.GetRouterInfo ().IsExtraBandwidth ()) return false;
auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes
auto bw = std::max (m_InBandwidth, m_OutBandwidth);
return bw > (i2p::context.GetRouterInfo ().IsHighBandwidth () ? HIGH_BANDWIDTH_LIMIT : LOW_BANDWIDTH_LIMIT);
return bw > limit;
}
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
{
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
}
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
@@ -220,7 +236,7 @@ namespace transport
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
{
// we send it to ourself
for (auto it: msgs)
for (auto& it: msgs)
i2p::HandleI2NPMessage (it);
return;
}
@@ -232,7 +248,7 @@ namespace transport
{
auto r = netdb.FindRouter (ident);
{
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
i2p::util::GetSecondsSinceEpoch (), {} })).first;
}
@@ -248,8 +264,17 @@ namespace transport
it->second.sessions.front ()->SendI2NPMessages (msgs);
else
{
for (auto it1: msgs)
it->second.delayedMessages.push_back (it1);
if (it->second.delayedMessages.size () < MAX_NUM_DELAYED_MESSAGES)
{
for (auto& it1: msgs)
it->second.delayedMessages.push_back (it1);
}
else
{
LogPrint (eLogWarning, "Transports: delayed messages queue size exceeds ", MAX_NUM_DELAYED_MESSAGES);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
}
@@ -261,7 +286,7 @@ namespace transport
{
peer.numAttempts++;
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
if (address)
if (address && m_NTCPServer)
{
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
@@ -289,7 +314,7 @@ namespace transport
}
}
else
LogPrint (eLogWarning, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
}
if (peer.numAttempts == 1)// SSU
{
@@ -321,7 +346,7 @@ namespace transport
}
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
return false;
}
@@ -353,7 +378,7 @@ namespace transport
else
{
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
@@ -376,18 +401,28 @@ namespace transport
auto& peer = it1->second;
if (!ecode && peer.router)
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetNTCPAddress ();
if (addr)
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address, addr->port, s);
return;
while (it != boost::asio::ip::tcp::resolver::iterator())
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
if (address.is_v4 () || context.SupportsV6 ())
{
auto addr = peer.router->GetNTCPAddress (); // TODO: take one we requested
if (addr)
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address, addr->port, s);
return;
}
break;
}
else
LogPrint (eLogInfo, "Transports: NTCP ", address, " is not supported");
it++;
}
}
LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it1);
}
}
@@ -409,17 +444,27 @@ namespace transport
auto& peer = it1->second;
if (!ecode && peer.router)
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetSSUAddress (!context.SupportsV6 ());;
if (addr)
{
m_SSUServer->CreateSession (peer.router, address, addr->port);
return;
while (it != boost::asio::ip::tcp::resolver::iterator())
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
if (address.is_v4 () || context.SupportsV6 ())
{
auto addr = peer.router->GetSSUAddress (); // TODO: take one we requested
if (addr)
{
m_SSUServer->CreateSession (peer.router, address, addr->port);
return;
}
break;
}
else
LogPrint (eLogInfo, "Transports: SSU ", address, " is not supported");
it++;
}
}
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it1);
}
}
@@ -427,7 +472,7 @@ namespace transport
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
@@ -437,26 +482,34 @@ namespace transport
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint (eLogDebug, "Transports: SSU session closed");
}
// TODO: delete NTCP
}
auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr;
if (ntcpSession) // try deleting ntcp session too
{
ntcpSession->Terminate ();
LogPrint(eLogDebug, "Transports: NTCP session closed");
}
}
void Transports::DetectExternalIP ()
{
if (m_SSUServer)
{
#ifndef MESHNET
i2p::context.SetStatus (eRouterStatusTesting);
#endif
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
if (router && router->IsSSU (!context.SupportsV6 ()))
m_SSUServer->CreateSession (router, true); // peer test
if (router && router->IsSSU (!context.SupportsV6 ()))
m_SSUServer->CreateSession (router, true); // peer test
else
{
// if not peer test capable routers found pick any
router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU ())
m_SSUServer->CreateSession (router); // no peer test
m_SSUServer->CreateSession (router); // no peer test
}
}
}
@@ -479,7 +532,7 @@ namespace transport
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, true); // peer test
m_SSUServer->CreateSession (router, true); // peer test
}
}
}
@@ -498,20 +551,34 @@ namespace transport
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
{
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash ();
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
bool sendDatabaseStore = true;
if (it->second.delayedMessages.size () > 0)
{
// check if first message is our DatabaseStore (publishing)
auto firstMsg = it->second.delayedMessages[0];
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
sendDatabaseStore = false; // we have it in the list already
}
if (sendDatabaseStore)
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () });
else
session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds
it->second.sessions.push_back (session);
session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear ();
}
else // incoming connection
{
std::unique_lock<std::mutex> l(m_PeersMutex);
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
}
});
@@ -520,7 +587,7 @@ namespace transport
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
{
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash ();
@@ -534,7 +601,7 @@ namespace transport
ConnectToPeer (ident, it->second);
else
{
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
@@ -559,14 +626,20 @@ namespace transport
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
{
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
std::unique_lock<std::mutex> l(m_PeersMutex);
auto profile = i2p::data::GetRouterProfile(it->first);
if (profile)
{
profile->TunnelNonReplied();
profile->Save();
}
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.erase (it);
}
else
it++;
++it;
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
DetectExternalIP ();
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
@@ -575,12 +648,36 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{
if (!m_Peers.size ()) return nullptr;
if (m_Peers.empty ()) return nullptr;
std::unique_lock<std::mutex> l(m_PeersMutex);
auto it = m_Peers.begin ();
std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr;
}
void Transports::RestrictRoutes(std::vector<std::string> families)
{
std::lock_guard<std::mutex> lock(m_FamilyMutex);
m_TrustedFamilies.clear();
for ( const auto& fam : families )
m_TrustedFamilies.push_back(fam);
}
bool Transports::RoutesRestricted() const {
std::lock_guard<std::mutex> lock(m_FamilyMutex);
return m_TrustedFamilies.size() > 0;
}
/** XXX: if routes are not restricted this dies */
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const {
std::string fam;
{
std::lock_guard<std::mutex> lock(m_FamilyMutex);
// TODO: random family (?)
fam = m_TrustedFamilies[0];
}
boost::to_lower(fam);
return i2p::data::netdb.GetRandomRouterInFamily(fam);
}
}
}

View File

@@ -60,14 +60,13 @@ namespace transport
void Done ()
{
for (auto it: sessions)
for (auto& it: sessions)
it->Done ();
}
};
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
const uint32_t LOW_BANDWIDTH_LIMIT = 32*1024; // 32KBs
const uint32_t HIGH_BANDWIDTH_LIMIT = 256*1024; // 256KBs
const int MAX_NUM_DELAYED_MESSAGES = 50;
class Transports
{
public:
@@ -75,9 +74,15 @@ namespace transport
Transports ();
~Transports ();
void Start ();
void Start (bool enableNTCP=true, bool enableSSU=true);
void Stop ();
bool IsBoundNTCP() const { return m_NTCPServer != nullptr; }
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
bool IsOnline() const { return m_IsOnline; };
void SetOnline (bool online) { m_IsOnline = online; };
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
@@ -94,12 +99,19 @@ namespace transport
void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; };
uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; };
uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; };
uint32_t GetInBandwidth () const { return m_InBandwidth; }; // bytes per second
uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; // bytes per second
uint32_t GetInBandwidth () const { return m_InBandwidth; };
uint32_t GetOutBandwidth () const { return m_OutBandwidth; };
bool IsBandwidthExceeded () const;
size_t GetNumPeers () const { return m_Peers.size (); };
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
/** get a trusted first hop for restricted routes */
std::shared_ptr<const i2p::data::RouterInfo> GetRestrictedPeer() const;
/** do we want to use restricted routes? */
bool RoutesRestricted() const;
/** restrict routes to use only these router families for first hops */
void RestrictRoutes(std::vector<std::string> families);
void PeerTest ();
private:
@@ -124,7 +136,7 @@ namespace transport
private:
bool m_IsRunning;
bool m_IsOnline, m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
@@ -138,10 +150,14 @@ namespace transport
DHKeysPairSupplier m_DHKeysPairSupplier;
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes;
uint32_t m_InBandwidth, m_OutBandwidth;
uint32_t m_InBandwidth, m_OutBandwidth; // bytes per second
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
uint64_t m_LastBandwidthUpdateTime;
/** which router families to trust for first hops */
std::vector<std::string> m_TrustedFamilies;
mutable std::mutex m_FamilyMutex;
public:
// for HTTP only

View File

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

View File

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

View File

@@ -5,7 +5,6 @@
#include <sstream>
#include <vector>
#include <memory>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Identity.h"
#include "RouterContext.h"
@@ -160,6 +159,11 @@ namespace tunnel
return num;
}
bool IsEmpty () const
{
return !m_FirstHop;
}
virtual bool IsInbound () const { return m_FirstHop->isGateway; }
virtual uint32_t GetTunnelID () const
@@ -209,7 +213,7 @@ namespace tunnel
void CreatePeers (const Peers& peers)
{
TunnelHopConfig * prev = nullptr;
for (auto it: peers)
for (const auto& it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)

View File

@@ -1,6 +1,6 @@
#include "I2PEndian.h"
#include <string.h>
#include <openssl/sha.h>
#include "Crypto.h"
#include "Log.h"
#include "NetDb.h"
#include "I2NPProtocol.h"
@@ -119,7 +119,7 @@ namespace tunnel
if (ret.second)
HandleOutOfSequenceFragment (msgID, ret.first->second);
else
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, "already exists");
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
}
else
{
@@ -205,7 +205,7 @@ namespace tunnel
if (it->second.fragmentNum == msg.nextFragmentNum)
{
LogPrint (eLogWarning, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
auto size = it->second.data->GetLength ();
size_t size = it->second.data->GetLength ();
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
@@ -235,7 +235,7 @@ namespace tunnel
LogPrint (eLogInfo, "TunnelMessage: message expired");
return;
}
auto typeID = msg.data->GetTypeID ();
uint8_t typeID = msg.data->GetTypeID ();
LogPrint (eLogDebug, "TunnelMessage: handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID);
// catch RI or reply with new list of routers
if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) &&

View File

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

View File

@@ -1,5 +1,4 @@
#include <algorithm>
#include <openssl/rand.h>
#include "I2PEndian.h"
#include "Crypto.h"
#include "Tunnel.h"
@@ -50,13 +49,13 @@ namespace tunnel
{
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it: m_InboundTunnels)
for (auto& it: m_InboundTunnels)
it->SetTunnelPool (nullptr);
m_InboundTunnels.clear ();
}
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels)
for (auto& it: m_OutboundTunnels)
it->SetTunnelPool (nullptr);
m_OutboundTunnels.clear ();
}
@@ -79,7 +78,7 @@ namespace tunnel
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
for (auto& it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
@@ -102,7 +101,7 @@ namespace tunnel
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
for (auto& it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
@@ -115,7 +114,7 @@ namespace tunnel
std::vector<std::shared_ptr<InboundTunnel> > v;
int i = 0;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
for (const auto& it : m_InboundTunnels)
{
if (i >= num) break;
if (it->IsEstablished ())
@@ -145,7 +144,7 @@ namespace tunnel
if (tunnels.empty ()) return nullptr;
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
typename TTunnels::value_type tunnel = nullptr;
for (auto it: tunnels)
for (const auto& it: tunnels)
{
if (it->IsEstablished () && it != excluded)
{
@@ -165,7 +164,7 @@ namespace tunnel
if (old)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels)
for (const auto& it: m_OutboundTunnels)
if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
{
tunnel = it;
@@ -183,7 +182,7 @@ namespace tunnel
int num = 0;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumInboundTunnels; i++)
@@ -192,7 +191,7 @@ namespace tunnel
num = 0;
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it : m_OutboundTunnels)
for (const auto& it : m_OutboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumOutboundTunnels; i++)
@@ -204,11 +203,10 @@ namespace tunnel
decltype(m_Tests) tests;
{
std::unique_lock<std::mutex> l(m_TestsMutex);
tests = m_Tests;
m_Tests.clear ();
tests.swap(m_Tests);
}
for (auto it: tests)
for (auto& it: tests)
{
LogPrint (eLogWarning, "Tunnels: test of tunnel ", it.first, " failed");
// if test failed again with another tunnel we consider it failed
@@ -249,12 +247,12 @@ namespace tunnel
if ((*it1)->IsFailed ())
{
failed = true;
it1++;
++it1;
}
if ((*it2)->IsFailed ())
{
failed = true;
it2++;
++it2;
}
if (!failed)
{
@@ -266,7 +264,7 @@ namespace tunnel
}
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
CreateDeliveryStatusMsg (msgID));
it1++; it2++;
++it1; ++it2;
}
}
}
@@ -323,16 +321,25 @@ namespace tunnel
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
if (!hop || hop->GetProfile ()->IsBad ())
hop = i2p::data::netdb.GetRandomRouter ();
hop = i2p::data::netdb.GetRandomRouter (prevHop);
return hop;
}
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
auto prevHop = i2p::context.GetSharedRouterInfo ();
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
if (i2p::transport::transports.GetNumPeers () > 25)
if (numHops <= 0) return true; // peers is empty
auto prevHop = i2p::context.GetSharedRouterInfo ();
if(i2p::transport::transports.RoutesRestricted())
{
/** if routes are restricted prepend trusted first hop */
auto hop = i2p::transport::transports.GetRestrictedPeer();
if(!hop) return false;
peers.push_back(hop->GetRouterIdentity());
prevHop = hop;
}
else if (i2p::transport::transports.GetNumPeers () > 25)
{
auto r = i2p::transport::transports.GetRandomPeer ();
if (r && !r->GetProfile ()->IsBad ())
@@ -343,7 +350,7 @@ namespace tunnel
}
}
for (int i = 0; i < numHops; i++)
for(int i = 0; i < numHops; i++ )
{
auto hop = SelectNextHop (prevHop);
if (!hop)
@@ -353,7 +360,7 @@ namespace tunnel
}
prevHop = hop;
peers.push_back (hop->GetRouterIdentity ());
}
}
return true;
}
@@ -390,9 +397,16 @@ namespace tunnel
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (peers, true))
{
std::reverse (peers.begin (), peers.end ());
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig> (peers), outboundTunnel);
std::shared_ptr<TunnelConfig> config;
if (m_NumInboundHops > 0)
{
std::reverse (peers.begin (), peers.end ());
config = std::make_shared<TunnelConfig> (peers);
}
auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
}
else
LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available");
@@ -404,8 +418,12 @@ namespace tunnel
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(tunnel->GetPeers ()), outboundTunnel);
std::shared_ptr<TunnelConfig> config;
if (m_NumInboundHops > 0) config = std::make_shared<TunnelConfig>(tunnel->GetPeers ());
auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
newTunnel->SetTunnelPool (shared_from_this());
if (newTunnel->IsEstablished ()) // zero hops
TunnelCreated (newTunnel);
}
void TunnelPool::CreateOutboundTunnel ()
@@ -419,9 +437,13 @@ namespace tunnel
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (peers, false))
{
auto tunnel = tunnels.CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
std::shared_ptr<TunnelConfig> config;
if (m_NumOutboundHops > 0)
config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
auto tunnel = tunnels.CreateOutboundTunnel (config);
tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
}
else
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
@@ -438,10 +460,13 @@ namespace tunnel
if (inboundTunnel)
{
LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (tunnel->GetPeers (),
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
std::shared_ptr<TunnelConfig> config;
if (m_NumOutboundHops > 0)
config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
auto newTunnel = tunnels.CreateOutboundTunnel (config);
newTunnel->SetTunnelPool (shared_from_this ());
if (newTunnel->IsEstablished ()) // zero hops
TunnelCreated (newTunnel);
}
else
LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found");
@@ -450,7 +475,7 @@ namespace tunnel
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
{
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
}

291
UPnP.cpp
View File

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

54
UPnP.h
View File

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

View File

@@ -53,7 +53,8 @@ END
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
MAINICON ICON "ictoopie.ico"
IDI_ICON1 ICON "ictoopie_16.ico"
//MAINICON ICON "anke.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////

View File

@@ -1,16 +1,28 @@
#include <string.h>
#include <windows.h>
#include <shellapi.h>
//#include "../Daemon.h"
#include "../Config.h"
#include "../RouterContext.h"
#include "../version.h"
#include "resource.h"
#include "Win32App.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define ID_ABOUT 2000
#define ID_EXIT 2001
#define ID_CONSOLE 2002
#define ID_APP 2003
#define ID_GRACEFUL_SHUTDOWN 2004
#define ID_TRAY_ICON 2050
#define WM_TRAYICON (WM_USER + 1)
#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
namespace i2p
{
namespace win32
@@ -18,10 +30,13 @@ namespace win32
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
{
HMENU hPopup = CreatePopupMenu();
InsertMenu (hPopup, 0, MF_BYPOSITION | MF_STRING, ID_ABOUT, "About...");
InsertMenu (hPopup, 1, MF_BYPOSITION | MF_STRING, ID_EXIT , "Exit");
SetMenuDefaultItem (hPopup, ID_ABOUT, FALSE);
SetFocus (hWnd);
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL);
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);
POINT p;
@@ -44,10 +59,11 @@ namespace win32
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = ID_TRAY_ICON;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
nid.uCallbackMessage = WM_TRAYICON;
nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (IDI_ICON1));
nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON));
strcpy (nid.szTip, "i2pd");
strcpy (nid.szInfo, "i2pd is running");
Shell_NotifyIcon(NIM_ADD, &nid );
}
@@ -71,6 +87,7 @@ namespace win32
case WM_CLOSE:
{
RemoveTrayIcon (hWnd);
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
PostQuitMessage (0);
break;
}
@@ -80,7 +97,9 @@ namespace win32
{
case ID_ABOUT:
{
MessageBox( hWnd, TEXT("i2pd"), TEXT("About"), MB_ICONINFORMATION | MB_OK );
std::stringstream text;
text << "Version: " << I2PD_VERSION << " " << CODENAME;
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
return 0;
}
case ID_EXIT:
@@ -88,14 +107,68 @@ namespace win32
PostMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
case ID_GRACEFUL_SHUTDOWN:
{
i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
return 0;
}
case ID_CONSOLE:
{
char buf[30];
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
return 0;
}
case ID_APP:
{
ShowWindow(hWnd, SW_SHOW);
return 0;
}
}
break;
}
case WM_SYSCOMMAND:
{
switch (wParam)
{
case SC_MINIMIZE:
{
ShowWindow(hWnd, SW_HIDE);
return 0;
}
case SC_CLOSE:
{
std::string close; i2p::config::GetOption("close", close);
if (0 == close.compare("ask"))
switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?"
" You can add 'close' configuration option. Valid values are: ask, minimize, exit.",
"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
{
case IDYES: close = "minimize"; break;
case IDNO: close = "exit"; break;
default: return 0;
}
if (0 == close.compare("minimize"))
{
ShowWindow(hWnd, SW_HIDE);
return 0;
}
if (0 != close.compare("exit"))
{
::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
return 0;
}
}
}
}
case WM_TRAYICON:
{
SetForegroundWindow (hWnd);
switch (lParam)
{
case WM_LBUTTONUP:
case WM_RBUTTONUP:
{
SetForegroundWindow (hWnd);
@@ -106,6 +179,15 @@ namespace win32
}
break;
}
case WM_TIMER:
{
if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
{
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
return 0;
}
break;
}
}
return DefWindowProc( hWnd, uMsg, wParam, lParam);
}
@@ -127,15 +209,14 @@ namespace win32
wclx.cbClsExtra = 0;
wclx.cbWndExtra = 0;
wclx.hInstance = hInst;
wclx.hIcon = LoadIcon (hInst, IDI_APPLICATION);
wclx.hIconSm = LoadIcon (hInst, IDI_APPLICATION);
wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON));
wclx.hCursor = LoadCursor (NULL, IDC_ARROW);
wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wclx.lpszMenuName = NULL;
wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
RegisterClassEx (&wclx);
// create new window
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 250, 150, NULL, NULL, hInst, NULL))
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW, 100, 100, 549, 738, NULL, NULL, hInst, NULL))
{
MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
return false;
@@ -158,5 +239,13 @@ namespace win32
{
UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
}
bool GracefulShutdown ()
{
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
if (hWnd)
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
return hWnd;
}
}
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -2,7 +2,7 @@
// Microsoft Visual C++ generated include file.
// Used by Resource.rc
//
#define IDI_ICON1 101
#define MAINICON 101
// Next default values for new objects
//

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