Compare commits

..

159 Commits

Author SHA1 Message Date
EinMByte
45d27f8ddc Merge pull request #281 from anonimal/master
Catch exception in SSUServer that would dump core.
2015-10-14 11:05:43 +02:00
anonimal
6ae5f20ec1 Catch exception in SSUServer that would dump core. 2015-10-13 20:26:32 +00:00
EinMByte
e4f19716dd Merge pull request #278 from anonimal/master
Minor enhancements.
2015-10-05 16:40:02 +02:00
anonimal
dd88e13b95 English corrections. 2015-10-05 11:44:48 +00:00
anonimal
7c924e13d9 Add Arch Linux support to documentation. 2015-10-05 10:33:30 +00:00
anonimal
65b5a6773e Add Netbeans-related to .gitignore 2015-10-05 10:24:22 +00:00
EinMByte
9da8f44761 Merge pull request #275 from majestrate/master
add datagram forwarding to sam
2015-09-30 22:03:01 +02:00
jeff
2248d2ad82 get rid of compiler warnings, add logging 2015-09-30 09:18:30 -04:00
jeff
569bd45f10 remove unused import 2015-09-30 07:58:53 -04:00
jeff
06a7612e2e Merge remote-tracking branch 'purple/master' 2015-09-30 07:55:11 -04:00
jeff
39cd25bb44 initial sam udp forward 2015-09-29 13:31:04 -04:00
EinMByte
d05ad68aa4 Merge pull request #273 from EinMByte/master
Fix #272 and #274.
2015-09-28 21:26:29 +02:00
EinMByte
4a69a0a831 Fix #274 (hack). 2015-09-28 09:46:29 +02:00
EinMByte
68cf69fabc Fix #272. 2015-09-27 22:41:37 +02:00
EinMByte
af66f335c9 Merge pull request #271 from EinMByte/master
Benchmarking, webui and windows build
2015-09-27 19:49:45 +02:00
EinMByte
b9e25f2c96 Remove orignal certificate. 2015-09-27 17:10:23 +02:00
EinMByte
73725774dc Minor bugfixes for windows. 2015-09-27 16:55:39 +02:00
EinMByte
c9080f9f72 Changes to static build (related to #270). 2015-09-27 14:45:24 +02:00
EinMByte
65252790e6 Fix #270. 2015-09-27 13:43:02 +02:00
EinMByte
36c77080b6 Fixes needed for windows build (issue #270). 2015-09-24 22:47:44 +02:00
EinMByte
99a4be498a Properly escape backslash in CMakeLists (windows). 2015-09-21 17:25:26 +02:00
EinMByte
633f71c145 Display outbound tunnels in webui. 2015-09-21 15:58:36 +02:00
EinMByte
0c2830b9a5 Fix webui tunnel status column. 2015-09-18 23:12:08 +02:00
EinMByte
f04f556b75 Add basic inbound tunnel information to webui. 2015-09-18 21:37:21 +02:00
EinMByte
19557a0908 Update doc/ files to include --install. 2015-09-18 14:26:05 +02:00
EinMByte
3ef89718a1 Added --install flag. 2015-09-18 14:19:06 +02:00
EinMByte
c741382fc9 Added 404 page to the webui. 2015-09-18 11:52:09 +02:00
EinMByte
dbade8b569 (Graceful) restart and reseed buttons in webui. 2015-09-17 23:18:31 +02:00
EinMByte
fbea1ea142 Remove obsolete includes and constants in HTTPServer 2015-09-17 17:18:36 +02:00
EinMByte
f05419845b Move part of BUILD_NOTES.md to BUILDING.md 2015-09-17 16:59:39 +02:00
EinMByte
9890bf98e4 Add more information to webui. 2015-09-17 16:49:55 +02:00
EinMByte
7e066f7f69 webui: reauthenticate when token expires 2015-09-17 16:21:11 +02:00
EinMByte
30798f835e Fixes to the webui CSS 2015-09-17 14:02:40 +02:00
EinMByte
221e350228 Move MIME type detection to util/HTTP.cpp 2015-09-17 11:59:04 +02:00
EinMByte
17cd149e7f Several improvements to HTTPServer. 2015-09-17 11:47:16 +02:00
EinMByte
1ec31125b0 Change WIN32 to _WIN32 in util.cpp. 2015-09-13 16:54:55 +02:00
EinMByte
0c376117b0 Simplify CSS for webui. 2015-09-12 14:43:30 +02:00
EinMByte
d3cede7995 Fix operating system detection in util.cpp. 2015-09-12 14:19:12 +02:00
EinMByte
98d8dd7224 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-09-11 18:58:40 +02:00
Mikal Villa
8ff01be841 Cleanup in README.md 2015-09-10 15:00:39 +08:00
Mikal Villa
01c11d5fa5 Sorted out the documentation 2015-09-10 02:09:56 +08:00
EinMByte
4d6d032115 Add more information to webui. 2015-09-07 13:41:48 +02:00
EinMByte
719bfbc89b Added basic webui (layout from l-n-s). 2015-09-07 12:31:57 +02:00
EinMByte
e7350a3af4 Started rewrite of HTTPServer. 2015-09-06 20:34:50 +02:00
EinMByte
3d30b4bbbc Add Response type to util::http. 2015-09-06 18:51:19 +02:00
EinMByte
e3b891de41 Add util::GetWebuiDataDir(), HTTP parsing utilities and tests. 2015-09-06 16:15:46 +02:00
EinMByte
6857dcb911 Make cmake copy webui files to the i2pd data folder. 2015-09-05 21:02:44 +02:00
EinMByte
c42d76bca7 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-09-05 11:41:03 +02:00
EinMByte
07adb8ff64 Merge pull request #265 from at676/master
Support OpenBSD.
2015-09-05 11:40:28 +02:00
at676
91c1643ffe Support OpenBSD. 2015-09-05 01:32:50 -07:00
EinMByte
0b1d6b3c01 Fix typos. 2015-08-30 19:07:26 +02:00
EinMByte
670f369659 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-08-30 19:04:28 +02:00
EinMByte
97e06f3f2b Add benchmarking code and optimization option. 2015-08-30 19:00:26 +02:00
EinMByte
ed96e5ede1 Merge pull request #263 from EinMByte/master
EdDSASigner/Verifier support, fix warnings
2015-08-30 14:16:15 +02:00
EinMByte
ec02a27a39 Update README.md 2015-08-30 13:34:30 +02:00
EinMByte
6dd36b8289 Fix more warnings (#260). 2015-08-30 12:33:09 +02:00
EinMByte
ee0d603a81 Fix remaining warnings (fixes #260). 2015-08-30 12:10:33 +02:00
EinMByte
48c66a1d08 Fix all unused parameter warnings (issue #260) 2015-08-30 12:02:09 +02:00
EinMByte
828ac404eb Do not include Signature.h in Identity.h. 2015-08-30 11:30:28 +02:00
EinMByte
a24d99d678 Use size_t and uint64_t instead of long long in ed25519/. 2015-08-30 10:18:29 +02:00
EinMByte
22f9380d49 Add constructor from keypair for EDDSA25519Signer. 2015-08-30 09:58:07 +02:00
EinMByte
eabf434a48 Add tests for EdDSA25519, fix Verifier. 2015-08-29 23:21:32 +02:00
EinMByte
99b2ca860d Add interface for edddsa key generation. 2015-08-29 21:53:04 +02:00
EinMByte
93d60152d5 Add eddsa from ref10 implementation (with some modifications). 2015-08-29 19:32:50 +02:00
EinMByte
2fcc91a755 Merge pull request #258 from EinMByte/master
Fix #253.
2015-08-26 22:23:58 +02:00
EinMByte
ab3b100402 Fix #253. 2015-08-26 22:17:10 +02:00
EinMByte
f0aa7b55be Merge pull request #257 from EinMByte/master
New build system
2015-08-25 20:42:42 +02:00
EinMByte
0979f0d2f2 Update README and BUILD_NOTES. 2015-08-21 23:35:26 +02:00
EinMByte
5d00ad6473 Fix building tests, move binaries to different output directory. 2015-08-21 23:27:25 +02:00
EinMByte
103b6db717 Better hardening flags. 2015-08-21 22:50:19 +02:00
EinMByte
8ac9520dfd Implement #243, separate core/client (PCH support dropped for now) 2015-08-21 22:29:33 +02:00
EinMByte
5db0448c6b Merge pull request #256 from EinMByte/master
Update .gitignore, fix typo in CMakeLists.txt.
2015-08-18 22:45:30 +02:00
EinMByte
bdaf2c16aa Update .gitignore, fix typo in CMakeLists.txt. 2015-08-18 22:17:28 +02:00
EinMByte
9236c7b391 Merge pull request #255 from apprb/dev
CMake: tests support
2015-08-18 21:53:52 +02:00
EinMByte
ff466b7aaa Merge pull request #254 from EinMByte/master
Fix #252.
2015-08-18 21:51:59 +02:00
apprb
e285cd034b CMake: tests support 2015-08-19 00:58:07 +06:00
EinMByte
ca84c71c39 Fix #252. 2015-08-18 10:58:24 +02:00
EinMByte
2ab1cf0a89 Merge pull request #251 from EinMByte/master
Changes to HTTPServer
2015-08-18 10:48:04 +02:00
EinMByte
485e2237e0 Update README in accordance with #245. 2015-08-18 10:45:40 +02:00
EinMByte
a2e6acbd44 Do not include iomanip in HTTPServer.cpp, fix typo. 2015-08-17 21:35:47 +02:00
EinMByte
64d3aa9c56 Remove broken link from web UI. 2015-08-17 12:23:21 +02:00
EinMByte
3922a203b7 Merge https://github.com/majestrate/i2pd into web-ui 2015-08-16 23:39:36 +02:00
EinMByte
3cc95c0df2 Merge branch 'master' into web-ui 2015-08-16 23:38:20 +02:00
EinMByte
823ae63308 Merge pull request #250 from edwtjo/make-tunnelscfg-configurable
Make tunnels.cfg configurable
2015-08-16 22:03:16 +02:00
EinMByte
6de4ea2dce Merge pull request #248 from tayler1/master
Returning default mtu.
2015-08-16 19:19:00 +02:00
Edward Tjörnhammar
3d489a2996 Make tunnels.cfg configurable 2015-08-11 23:19:25 +02:00
jeff
413d1b4960 make http server http/1.1 compliant 2015-08-11 16:55:55 -04:00
tayler1
e57b6294ae Returning default mtu.
Also mtu function for osx.
2015-08-11 22:36:35 +03:00
Riccardo Spagni
14c5753261 Merge pull request #247 from edwtjo/address-insteadof-host
Use address instead of host in code and README.md
2015-08-11 21:08:35 +02:00
Edward Tjörnhammar
56cb3004a7 Use address instead of host in code and README.md 2015-08-11 18:27:26 +02:00
EinMByte
86b83ca614 Merge pull request #242 from EinMByte/master
Tests for AES + Improvements to i2pcontrol
2015-08-03 09:25:30 +02:00
EinMByte
2db3fe36d1 Fix building with CMake (typo in commit 12dd8a3) 2015-08-02 23:22:13 +02:00
EinMByte
12dd8a394d Fix building without AESNI. 2015-08-02 23:18:55 +02:00
EinMByte
6ed48ab876 Get rid of old HTTPServer proxy functionality. 2015-08-02 23:04:52 +02:00
EinMByte
c3685927d2 Modfiy indention and spaces in I2PControlServer.h/cpp 2015-08-02 22:01:09 +02:00
EinMByte
999001733d Fix I2PControl invalid password error message. 2015-08-02 20:42:01 +02:00
EinMByte
79768cf783 Mention I2PControl password option in README. 2015-08-02 20:39:10 +02:00
EinMByte
843abb60dd Make i2pcontrol password configurable. 2015-08-02 19:59:16 +02:00
EinMByte
8434863beb Fixed typos (thx zzz) 2015-08-02 17:14:36 +02:00
EinMByte
c10d0fdb7e Make i2pcontrol error codes negative. 2015-08-02 17:02:53 +02:00
EinMByte
833a606b13 Add a constant for the I2PControl token size. 2015-08-02 16:38:46 +02:00
EinMByte
69d93146f2 Expire tokens, use std::shared_ptr for I2PControlSession. 2015-08-02 16:32:54 +02:00
EinMByte
109127a39e Generate random I2PControl tokens. 2015-08-02 14:46:39 +02:00
EinMByte
c7f4a79b2c Destroy I2PControlSession before io_service, cancel waiting operations. 2015-08-02 12:57:16 +02:00
EinMByte
9cca01d159 Error handling and authentication for I2PControl. 2015-08-01 23:10:10 +02:00
EinMByte
996c36b93d Fixes for I2PControl. 2015-08-01 19:19:09 +02:00
EinMByte
1f90b08445 Began separation of i2pcontrol (untested). 2015-08-01 16:48:35 +02:00
EinMByte
af4719d665 Move AESNIMacros.h to crypto/ 2015-07-31 13:11:47 +02:00
EinMByte
5d0b24d696 Add tests for AES CBC (mainly important for custom AESNI implementation). 2015-07-31 13:01:49 +02:00
EinMByte
59e081f41e Fix typo "Chipher" -> "Cipher" 2015-07-30 22:06:42 +02:00
Riccardo Spagni
d9dc406d18 Merge pull request #239 from EinMByte/master
Split source code in directories
2015-07-30 18:21:26 +02:00
EinMByte
9597917183 Split tunnel-specific crypto from aes.h/cpp. 2015-07-30 17:27:47 +02:00
EinMByte
d9bb09780f Created tunnel/ 2015-07-30 16:25:43 +02:00
EinMByte
c8190d0020 Move Log.h/cpp to util/ 2015-07-30 15:54:02 +02:00
EinMByte
6268e297e4 Fix building with cmake. 2015-07-29 18:37:15 +02:00
EinMByte
13e8945666 Moved I2PEndian to util/ 2015-07-28 22:48:38 +02:00
EinMByte
b9aebadd46 Add crypto/ directory. 2015-07-28 22:03:39 +02:00
EinMByte
9e5c33bce7 Fixed tests after introducing util/. 2015-07-28 19:30:44 +02:00
EinMByte
89d3d17b28 Move base64 to util/ 2015-07-28 19:27:27 +02:00
EinMByte
3668c27db6 Moved some files to util/ 2015-07-28 18:37:06 +02:00
EinMByte
581c59a195 Merge branch 'master' of https://github.com/PurpleI2P/i2pd 2015-07-27 22:24:27 +02:00
EinMByte
9876a2a019 Merge pull request #238 from notorca/master
Build with clang on linux fixed
2015-07-27 22:23:57 +02:00
EinMByte
18ca8b9b97 Move SSU and NTCP to transport/. 2015-07-27 22:17:31 +02:00
Cyril Lashkevich
25b8a18224 Build with clang on linux fixed 2015-07-27 22:31:25 +03:00
EinMByte
e90b0dd54f Update TODO. 2015-07-27 21:28:50 +02:00
EinMByte
3f3103572a Merge pull request #237 from EinMByte/master
Fixes for #235 and #233.
Also tested by psi, and seems to be working fine.
2015-07-27 18:46:15 +02:00
EinMByte
9c03be08da Do not build unit tests by default with make (#233). 2015-07-27 14:50:28 +02:00
EinMByte
e70f0d999f Include address option for tunnels in README.md. 2015-07-27 14:07:28 +02:00
EinMByte
524a02cb0d Make address for proxies, BOB, SAM, ... configurable (#235). 2015-07-27 13:40:35 +02:00
EinMByte
a7da2423ce Do not bind HTTPServer to the "any" address (#235). 2015-07-27 12:17:16 +02:00
Riccardo Spagni
668235c697 Merge pull request #230 from EinMByte/master
Base32/64 tests and documentation + GetMTU changes
2015-07-24 15:55:24 +02:00
EinMByte
7d38b1a9b9 Base64/32 tests for #229. 2015-07-24 15:32:47 +02:00
EinMByte
c612d21639 Fix #229 by returning zero instead of -1. 2015-07-24 15:13:09 +02:00
EinMByte
2bde6fc13b Fixes to GetMTUWindows and GetMTUWindowsIpv6 (thanks mlt). 2015-07-24 14:43:51 +02:00
EinMByte
c3ec75756d Also check the returned size in base64 tests. 2015-07-23 21:54:13 +02:00
EinMByte
469981cce5 Tests and documentation for base32. 2015-07-23 14:46:35 +02:00
EinMByte
28a4f4173d Tests and documentation for base64. 2015-07-22 16:43:11 +02:00
Meeh
53053105c9 Merge pull request #169 from guanqun/fix-binary-name
the binary should be i2pd
2015-07-22 15:03:51 +02:00
Riccardo Spagni
85b1505e51 Merge pull request #228 from EinMByte/master
Various changes
2015-07-22 14:34:29 +02:00
EinMByte
d0b2a0da14 Fix #227: Transports::GetRandomPeer() crashes if m_Peers is empty 2015-07-22 12:11:21 +02:00
EinMByte
28969bfeab Update todo. 2015-07-21 12:10:15 +02:00
EinMByte
6f569f5334 Merge remote-tracking branch 'github/master'
Conflicts:
	Log.cpp
	Log.h
2015-07-21 11:31:36 +02:00
Mikal Villa
5f644b1b04 Fixed build issue with Mac OSX, which don't support std::chrono::monotonic_clock. 2015-07-20 18:08:55 +02:00
EinMByte
eb4109187a Remove unused include (boost foreach). 2015-07-19 18:11:38 +02:00
EinMByte
79e3f669f0 Cleanup util, add documentation. 2015-07-19 17:52:28 +02:00
EinMByte
66754aac5a Tests and documentation for util::url. 2015-07-19 15:51:02 +02:00
EinMByte
4412dd198d Tabs -> spaces (yes this breaks compatiblity with upstream) 2015-07-16 23:29:52 +02:00
EinMByte
5d78e2f5e4 Setup unit test environment. 2015-07-16 22:28:57 +02:00
EinMByte
4ce7b0e9ef Minor changes based on cppcheck output. 2015-07-16 18:02:19 +02:00
EinMByte
62ac53563c Ensure zero-inialization, add TODO update gitignore. 2015-07-16 17:39:24 +02:00
orignal
c939dec26a check if current remote lease is not set 2015-07-10 09:25:12 -04:00
orignal
6e54714c0e use shared_ptr for garlic cloves 2015-07-07 11:08:36 -04:00
orignal
6851644922 shared_ptr for DatabaseStore, DatabaseLookup and DatabaseSearchReply 2015-07-07 10:55:58 -04:00
orignal
8f40167fe1 rollback of previous change. port must be accesses from a remote host 2015-07-06 18:57:56 -04:00
orignal
de6e73c2eb Merge pull request #224 from majestrate/master
bind I2PService and HTTPServer to loopback
2015-07-06 18:40:58 -04:00
jeff
855f3833d2 bind to loopback 2015-07-06 18:03:16 -04:00
jeff
37a5c10c66 Merge branch 'upstream-master' into http-bind-local 2015-07-06 17:45:17 -04:00
jeff
23275a5949 have http server bind to loopback 2015-07-06 17:45:12 -04:00
guanqun
d2b30b5d11 the binary should be i2pd 2015-03-27 11:19:04 +08:00
368 changed files with 33431 additions and 30685 deletions

26
.gitignore vendored
View File

@@ -1,10 +1,26 @@
# i2pd
obj/*.o
router.info
router.keys
i2p
libi2pd.so
netDb
tunnels.cfg
tests/tests
# Build files
CMakeCache.txt
build/CMakeFiles/*
build/tests
build/client
build/core
build/benchmark
build/i2pd
build/i2pd-tests
build/i2pd-benchmark
*.cmake
*.a
*.o
# Autotools
autom4te.cache
@@ -47,6 +63,14 @@ local.properties
# PDT-specific
.buildpath
#################
## Netbeans
#################
# CMake: if "Project with Existing Sources" is properly setup, then only the following needs to be ignored:
# (e.g., don't select "Automatic" but instead select "Run Configure Script in Subfolder:" into ./build/)
build/compile_commands.json
nbproject/
#################
## Visual Studio

View File

@@ -1,602 +0,0 @@
#include <string.h>
#include <inttypes.h>
#include <string>
#include <map>
#include <fstream>
#include <chrono>
#include <condition_variable>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
#include "Log.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
namespace i2p
{
namespace client
{
class AddressBookFilesystemStorage: public AddressBookStorage
{
public:
AddressBookFilesystemStorage ();
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const;
void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void RemoveAddress (const i2p::data::IdentHash& ident);
int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
private:
boost::filesystem::path GetPath () const
{
return i2p::util::filesystem::GetDefaultDataDir() / "addressbook";
}
boost::filesystem::path GetAddressPath (const i2p::data::IdentHash& ident) const
{
auto b32 = ident.ToBase32();
return GetPath () / (std::string ("b") + b32[0]) / (b32 + ".b32");
}
};
AddressBookFilesystemStorage::AddressBookFilesystemStorage ()
{
auto path = GetPath ();
if (!boost::filesystem::exists (path))
{
// Create directory is necessary
if (!boost::filesystem::create_directory (path))
LogPrint (eLogError, "Failed to create addressbook directory");
}
}
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
{
auto filename = GetAddressPath (ident);
if (!boost::filesystem::exists (filename))
{
boost::filesystem::create_directory (filename.parent_path ());
// try to find in main folder
auto filename1 = GetPath () / (ident.ToBase32 () + ".b32");
if (!boost::filesystem::exists (filename1))
{
boost::system::error_code ec;
boost::filesystem::rename (filename1, filename, ec);
if (ec)
LogPrint (eLogError, "Couldn't move file ", ec.message ());
}
else
return nullptr; // address doesn't exist
}
std::ifstream f(filename.string (), std::ifstream::binary);
if (f.is_open ())
{
f.seekg (0,std::ios::end);
size_t len = f.tellg ();
if (len < i2p::data::DEFAULT_IDENTITY_SIZE)
{
LogPrint (eLogError, "File ", filename, " is too short. ", len);
return nullptr;
}
f.seekg(0, std::ios::beg);
uint8_t * buf = new uint8_t[len];
f.read((char *)buf, len);
auto address = std::make_shared<i2p::data::IdentityEx>(buf, len);
delete[] buf;
return address;
}
else
return nullptr;
}
void AddressBookFilesystemStorage::AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{
auto filename = GetAddressPath (address->GetIdentHash ());
std::ofstream f (filename.string (), std::ofstream::binary | std::ofstream::out);
if (!f.is_open ())
{
// create subdirectory
if (boost::filesystem::create_directory (filename.parent_path ()))
f.open (filename.string (), std::ofstream::binary | std::ofstream::out); // and try to open again
}
if (f.is_open ())
{
size_t len = address->GetFullLen ();
uint8_t * buf = new uint8_t[len];
address->ToBuffer (buf, len);
f.write ((char *)buf, len);
delete[] buf;
}
else
LogPrint (eLogError, "Can't open file ", filename);
}
void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident)
{
auto filename = GetAddressPath (ident);
if (boost::filesystem::exists (filename))
boost::filesystem::remove (filename);
}
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = 0;
auto filename = GetPath () / "addresses.csv";
std::ifstream f (filename.string (), std::ofstream::in); // in text mode
if (f.is_open ())
{
addresses.clear ();
while (!f.eof ())
{
std::string s;
getline(f, s);
if (!s.length())
continue; // skip empty line
size_t pos = s.find(',');
if (pos != std::string::npos)
{
std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos);
i2p::data::IdentHash ident;
ident.FromBase32 (addr);
addresses[name] = ident;
num++;
}
}
LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded");
}
else
LogPrint (eLogWarning, "Addressbook: ", filename, " not found");
return num;
}
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = 0;
auto filename = GetPath () / "addresses.csv";
std::ofstream f (filename.string (), std::ofstream::out); // in text mode
if (f.is_open ())
{
for (auto it: addresses)
{
f << it.first << "," << it.second.ToBase32 () << std::endl;
num++;
}
LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved");
}
else
LogPrint (eLogError, "Can't open file ", filename);
return num;
}
//---------------------------------------------------------------------
AddressBook::AddressBook (): m_Storage (nullptr), m_IsLoaded (false), m_IsDownloading (false),
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{
}
AddressBook::~AddressBook ()
{
Stop ();
}
void AddressBook::Start ()
{
StartSubscriptions ();
}
void AddressBook::Stop ()
{
StopSubscriptions ();
if (m_SubscriptionsUpdateTimer)
{
delete m_SubscriptionsUpdateTimer;
m_SubscriptionsUpdateTimer = nullptr;
}
if (m_IsDownloading)
{
LogPrint (eLogInfo, "Subscription is downloading. Waiting for temination...");
for (int i = 0; i < 30; i++)
{
if (!m_IsDownloading)
{
LogPrint (eLogInfo, "Subscription download complete");
break;
}
std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds
}
LogPrint (eLogError, "Subscription download hangs");
m_IsDownloading = false;
}
if (m_Storage)
{
m_Storage->Save (m_Addresses);
delete m_Storage;
m_Storage = nullptr;
}
if (m_DefaultSubscription)
{
delete m_DefaultSubscription;
m_DefaultSubscription = nullptr;
}
for (auto it: m_Subscriptions)
delete it;
m_Subscriptions.clear ();
}
AddressBookStorage * AddressBook::CreateStorage ()
{
return new AddressBookFilesystemStorage ();
}
bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident)
{
auto pos = address.find(".b32.i2p");
if (pos != std::string::npos)
{
Base32ToByteStream (address.c_str(), pos, ident, 32);
return true;
}
else
{
pos = address.find (".i2p");
if (pos != std::string::npos)
{
auto identHash = FindAddress (address);
if (identHash)
{
ident = *identHash;
return true;
}
else
return false;
}
}
// if not .b32 we assume full base64 address
i2p::data::IdentityEx dest;
if (!dest.FromBase64 (address))
return false;
ident = dest.GetIdentHash ();
return true;
}
const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address)
{
if (!m_IsLoaded)
LoadHosts ();
if (m_IsLoaded)
{
auto it = m_Addresses.find (address);
if (it != m_Addresses.end ())
return &it->second;
}
return nullptr;
}
void AddressBook::InsertAddress (const std::string& address, const std::string& base64)
{
auto ident = std::make_shared<i2p::data::IdentityEx>();
ident->FromBase64 (base64);
if (!m_Storage)
m_Storage = CreateStorage ();
m_Storage->AddAddress (ident);
m_Addresses[address] = ident->GetIdentHash ();
LogPrint (eLogInfo, "Addressbook: ", address,"->", ToAddress(ident->GetIdentHash ()), " added");
}
void AddressBook::InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{
if (!m_Storage)
m_Storage = CreateStorage ();
m_Storage->AddAddress (address);
}
std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetAddress (const std::string& address)
{
if (!m_Storage)
m_Storage = CreateStorage ();
i2p::data::IdentHash ident;
if (!GetIdentHash (address, ident)) return nullptr;
return m_Storage->GetAddress (ident);
}
void AddressBook::LoadHosts ()
{
if (!m_Storage)
m_Storage = CreateStorage ();
if (m_Storage->Load (m_Addresses) > 0)
{
m_IsLoaded = true;
return;
}
// try hosts.txt first
std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ofstream::in); // in text mode
if (f.is_open ())
{
LoadHostsFromStream (f);
m_IsLoaded = true;
}
else
{
// if not found download it from http://i2p-projekt.i2p/hosts.txt
LogPrint (eLogInfo, "hosts.txt not found. Try to download it from default subscription...");
if (!m_IsDownloading)
{
m_IsDownloading = true;
if (!m_DefaultSubscription)
m_DefaultSubscription = new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS);
m_DefaultSubscription->CheckSubscription ();
}
}
}
void AddressBook::LoadHostsFromStream (std::istream& f)
{
std::unique_lock<std::mutex> l(m_AddressBookMutex);
int numAddresses = 0;
std::string s;
while (!f.eof ())
{
getline(f, s);
if (!s.length())
continue; // skip empty line
size_t pos = s.find('=');
if (pos != std::string::npos)
{
std::string name = s.substr(0, pos++);
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, "Malformed address ", addr, " for ", name);
}
}
LogPrint (eLogInfo, numAddresses, " addresses processed");
if (numAddresses > 0)
{
m_IsLoaded = true;
m_Storage->Save (m_Addresses);
}
}
void AddressBook::LoadSubscriptions ()
{
if (!m_Subscriptions.size ())
{
std::ifstream f (i2p::util::filesystem::GetFullPath ("subscriptions.txt").c_str (), std::ofstream::in); // in text mode
if (f.is_open ())
{
std::string s;
while (!f.eof ())
{
getline(f, s);
if (!s.length()) continue; // skip empty line
m_Subscriptions.push_back (new AddressBookSubscription (*this, s));
}
LogPrint (eLogInfo, m_Subscriptions.size (), " subscriptions loaded");
}
else
LogPrint (eLogWarning, "subscriptions.txt not found");
}
else
LogPrint (eLogError, "Subscriptions already loaded");
}
void AddressBook::DownloadComplete (bool success)
{
m_IsDownloading = false;
if (m_SubscriptionsUpdateTimer)
{
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(
success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
}
void AddressBook::StartSubscriptions ()
{
LoadSubscriptions ();
if (!m_Subscriptions.size ()) return;
auto dest = i2p::client::context.GetSharedLocalDestination ();
if (dest)
{
m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ());
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
else
LogPrint (eLogError, "Can't start subscriptions: missing shared local destination");
}
void AddressBook::StopSubscriptions ()
{
if (m_SubscriptionsUpdateTimer)
m_SubscriptionsUpdateTimer->cancel ();
}
void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto dest = i2p::client::context.GetSharedLocalDestination ();
if (!dest) return;
if (m_IsLoaded && !m_IsDownloading && dest->IsReady () && !m_Subscriptions.empty ())
{
// pick random subscription
auto ind = rand () % m_Subscriptions.size();
m_IsDownloading = true;
m_Subscriptions[ind]->CheckSubscription ();
}
else
{
if (!m_IsLoaded)
LoadHosts ();
// try it again later
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT));
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
this, std::placeholders::_1));
}
}
}
AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link):
m_Book (book), m_Link (link)
{
}
void AddressBookSubscription::CheckSubscription ()
{
std::thread load_hosts(&AddressBookSubscription::Request, this);
load_hosts.detach(); // TODO: use join
}
void AddressBookSubscription::Request ()
{
// must be run in separate thread
LogPrint (eLogInfo, "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, "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\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\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, "Subscription 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;
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);
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");
}
}
LogPrint (eLogInfo, m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
if (!response.eof ())
{
success = true;
if (!isChunked)
m_Book.LoadHostsFromStream (response);
else
{
// merge chunks
std::stringstream merged;
i2p::util::http::MergeChunkedResponse (response, merged);
m_Book.LoadHostsFromStream (merged);
}
}
}
else if (status == 304)
{
success = true;
LogPrint (eLogInfo, "No updates from ", m_Link);
}
else
LogPrint (eLogWarning, "Adressbook HTTP response ", status);
}
else
LogPrint (eLogError, "Address ", u.host_, " not found");
}
else
LogPrint (eLogError, "Can't resolve ", u.host_);
if (!success)
LogPrint (eLogError, "Addressbook download failed");
m_Book.DownloadComplete (success);
}
}
}

View File

@@ -1,106 +0,0 @@
#ifndef ADDRESS_BOOK_H__
#define ADDRESS_BOOK_H__
#include <string.h>
#include <string>
#include <map>
#include <vector>
#include <iostream>
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
#include "Log.h"
namespace i2p
{
namespace client
{
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt";
const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes
const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours)
const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes
const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
class AddressBookStorage // interface for storage
{
public:
virtual ~AddressBookStorage () {};
virtual std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const = 0;
virtual void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address) = 0;
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
};
class AddressBookSubscription;
class AddressBook
{
public:
AddressBook ();
~AddressBook ();
void Start ();
void Stop ();
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address);
const i2p::data::IdentHash * FindAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success);
//This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); }
private:
void StartSubscriptions ();
void StopSubscriptions ();
AddressBookStorage * CreateStorage ();
void LoadHosts ();
void LoadSubscriptions ();
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
private:
std::mutex m_AddressBookMutex;
std::map<std::string, i2p::data::IdentHash> m_Addresses;
AddressBookStorage * m_Storage;
volatile bool m_IsLoaded, m_IsDownloading;
std::vector<AddressBookSubscription *> m_Subscriptions;
AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
};
class AddressBookSubscription
{
public:
AddressBookSubscription (AddressBook& book, const std::string& link);
void CheckSubscription ();
private:
void Request ();
private:
AddressBook& m_Book;
std::string m_Link, m_Etag, m_LastModified;
};
}
}
#endif

643
BOB.cpp
View File

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

237
BOB.h
View File

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

333
Base.cpp
View File

@@ -1,333 +0,0 @@
#include <stdlib.h>
#include "Log.h"
#include "Base.h"
namespace i2p
{
namespace data
{
static void iT64Build(void);
/*
*
* BASE64 Substitution Table
* -------------------------
*
* Direct Substitution Table
*/
static char T64[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '-', '~'
};
const char * GetBase64SubstitutionTable ()
{
return T64;
}
/*
* Reverse Substitution Table (built in run time)
*/
static char iT64[256];
static int isFirstTime = 1;
/*
* Padding
*/
static char P64 = '=';
/*
*
* ByteStreamToBase64
* ------------------
*
* Converts binary encoded data to BASE64 format.
*
*/
size_t /* Number of bytes in the encoded buffer */
ByteStreamToBase64 (
const uint8_t * InBuffer, /* Input buffer, binary data */
size_t InCount, /* Number of bytes in the input buffer */
char * OutBuffer, /* output buffer */
size_t len /* length of output buffer */
)
{
unsigned char * ps;
unsigned char * pd;
unsigned char acc_1;
unsigned char acc_2;
int i;
int n;
int m;
size_t outCount;
ps = (unsigned char *)InBuffer;
n = InCount/3;
m = InCount%3;
if (!m)
outCount = 4*n;
else
outCount = 4*(n+1);
if (outCount > len) return 0;
pd = (unsigned char *)OutBuffer;
for ( i = 0; i<n; i++ ){
acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x30;
acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1];
acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
*pd++ = T64[acc_2];
acc_1 &= 0x0f;
acc_1 <<=2;
acc_2 = *ps++;
acc_1 |= acc_2>>6; /* base64 digit #3 */
*pd++ = T64[acc_1];
acc_2 &= 0x3f; /* base64 digit #4 */
*pd++ = T64[acc_2];
}
if ( m == 1 ){
acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */
acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1];
*pd++ = T64[acc_2];
*pd++ = P64;
*pd++ = P64;
}
else if ( m == 2 ){
acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x3f;
acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1];
acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
*pd++ = T64[acc_2];
acc_1 &= 0x0f;
acc_1 <<=2; /* base64 digit #3 */
*pd++ = T64[acc_1];
*pd++ = P64;
}
return outCount;
}
/*
*
* Base64ToByteStream
* ------------------
*
* Converts BASE64 encoded data to binary format. If input buffer is
* not properly padded, buffer of negative length is returned
*
*/
size_t /* Number of output bytes */
Base64ToByteStream (
const char * InBuffer, /* BASE64 encoded buffer */
size_t InCount, /* Number of input bytes */
uint8_t * OutBuffer, /* output buffer length */
size_t len /* length of output buffer */
)
{
unsigned char * ps;
unsigned char * pd;
unsigned char acc_1;
unsigned char acc_2;
int i;
int n;
int m;
size_t outCount;
if (isFirstTime) iT64Build();
n = InCount/4;
m = InCount%4;
if (InCount && !m)
outCount = 3*n;
else {
outCount = 0;
return 0;
}
ps = (unsigned char *)(InBuffer + InCount - 1);
while ( *ps-- == P64 ) outCount--;
ps = (unsigned char *)InBuffer;
if (outCount > len) return -1;
pd = OutBuffer;
auto endOfOutBuffer = OutBuffer + outCount;
for ( i = 0; i < n; i++ ){
acc_1 = iT64[*ps++];
acc_2 = iT64[*ps++];
acc_1 <<= 2;
acc_1 |= acc_2>>4;
*pd++ = acc_1;
if (pd >= endOfOutBuffer) break;
acc_2 <<= 4;
acc_1 = iT64[*ps++];
acc_2 |= acc_1 >> 2;
*pd++ = acc_2;
if (pd >= endOfOutBuffer) break;
acc_2 = iT64[*ps++];
acc_2 |= acc_1 << 6;
*pd++ = acc_2;
}
return outCount;
}
/*
*
* iT64
* ----
* Reverse table builder. P64 character is replaced with 0
*
*
*/
static void iT64Build()
{
int i;
isFirstTime = 0;
for ( i=0; i<256; i++ ) iT64[i] = -1;
for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i;
iT64[(int)P64] = 0;
}
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen)
{
int tmp = 0, bits = 0;
size_t ret = 0;
for (size_t i = 0; i < len; i++)
{
char ch = inBuf[i];
if (ch >= '2' && ch <= '7') // digit
ch = (ch - '2') + 26; // 26 means a-z
else if (ch >= 'a' && ch <= 'z')
ch = ch - 'a'; // a = 0
else
return 0; // unexpected character
tmp |= ch;
bits += 5;
if (bits >= 8)
{
if (ret >= outLen) return ret;
outBuf[ret] = tmp >> (bits - 8);
bits -= 8;
ret++;
}
tmp <<= 5;
}
return ret;
}
size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen)
{
size_t ret = 0, pos = 1;
int bits = 8, tmp = inBuf[0];
while (ret < outLen && (bits > 0 || pos < len))
{
if (bits < 5)
{
if (pos < len)
{
tmp <<= 8;
tmp |= inBuf[pos] & 0xFF;
pos++;
bits += 8;
}
else // last byte
{
tmp <<= (5 - bits);
bits = 5;
}
}
bits -= 5;
int ind = (tmp >> bits) & 0x1F;
outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2');
ret++;
}
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;
}
}
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;
}
}
}
}

123
Base.h
View File

@@ -1,123 +0,0 @@
#ifndef BASE_H__
#define BASE_H__
#include <inttypes.h>
#include <string.h>
#include <string>
#include <zlib.h>
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 * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
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];
};
};
class GzipInflator
{
public:
GzipInflator ();
~GzipInflator ();
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
private:
z_stream m_Inflator;
bool m_IsDirty;
};
class GzipDeflator
{
public:
GzipDeflator ();
~GzipDeflator ();
void SetCompressionLevel (int level);
size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
private:
z_stream m_Deflator;
bool m_IsDirty;
};
}
}
#endif

195
CMakeLists.txt Normal file
View File

@@ -0,0 +1,195 @@
cmake_minimum_required ( VERSION 2.8.12 )
project ( "i2pd" )
# configurale options
option(WITH_AESNI "Use AES-NI instructions set" OFF)
option(WITH_HARDENING "Use hardening compiler flags" OFF)
option(WITH_LIBRARY "Build library" ON)
option(WITH_BINARY "Build binary" ON)
option(WITH_STATIC "Static build" OFF)
option(WITH_UPNP "Include support for UPnP client" OFF)
option(WITH_TESTS "Build unit tests" OFF)
option(WITH_BENCHMARK "Build benchmarking code" OFF)
option(WITH_OPTIMIZE "Optimization flags" OFF)
option(I2PD_DATA_PATH "The path to the i2pd data folder")
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake_modules")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# Default build is Debug
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
# compiler flags customization (by vendor)
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Winvalid-pch")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -pedantic")
# TODO: The following is incompatible with static build and enabled hardening
# for OpenWRT.
# Multiple definitions of __stack_chk_fail (libssp & libc)
set(
CMAKE_CXX_FLAGS_MINSIZEREL
"${CMAKE_CXX_FLAGS_MINSIZEREL} -flto -s -ffunction-sections -fdata-sections"
)
# -flto is added from above
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections")
endif()
# Check for c++11 support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" CXX0X_SUPPORTED)
if(CXX11_SUPPORTED)
add_definitions("-std=c++11")
elseif(CXX0X_SUPPORTED) # gcc 4.6
add_definitions("-std=c++0x")
elseif(NOT MSVC)
message(SEND_ERROR "C++11 standard not supported by compiler. Version too old?")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(WITH_HARDENING)
add_definitions( "-D_FORTIFY_SOURCE=2" )
set(
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wformat -Wformat-security -Werror=format-security"
)
set(
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -fstack-protector-strong -fPIE --param ssp-buffer-size=4 -z relro -z now"
)
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# TODO: Clang-specific flags
endif()
# Compiler flags customization (by system)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
# "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8
add_definitions( "-D_GLIBCXX_USE_NANOSLEEP=1" )
endif ()
if(WITH_UPNP)
add_definitions(-DUSE_UPNP)
if(NOT MSVC)
set(DL_LIB ${CMAKE_DL_LIBS})
endif()
endif()
if(WITH_AESNI)
add_definitions( "-maes -DAESNI")
endif()
if(WITH_OPTIMIZE AND (NOT MSVC))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()
# Libraries
# TODO: once CMake 3.1+ becomes mainstream, see e.g.
# http://stackoverflow.com/a/29871891/673826 use imported Threads::Threads
# instead
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package (Threads REQUIRED)
if(THREADS_HAVE_PTHREAD_ARG) # compile time flag
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
if(WITH_STATIC)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME OFF)
if(NOT WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
endif()
set(BUILD_SHARED_LIBS OFF)
if(${CMAKE_CXX_COMPILER} MATCHES ".*-openwrt-.*")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(
CMAKE_THREAD_LIBS_INIT
"gcc_eh -Wl,-u,pthread_create,-u,pthread_once,-u,pthread_mutex_lock,-u,pthread_mutex_unlock,-u,pthread_join,-u,pthread_equal,-u,pthread_detach,-u,pthread_cond_wait,-u,pthread_cond_signal,-u,pthread_cond_destroy,-u,pthread_cond_broadcast,-u,pthread_cancel"
)
endif()
elseif(NOT WIN32)
# TODO: Consider separate compilation for COMMON_SRC for library.
# No need in -fPIC overhead for binary if not interested in library
# HINT: revert c266cff CMakeLists.txt: compilation speed up
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
else() # Not a static build
add_definitions(-DBOOST_ALL_DYN_LINK)
endif()
find_package(
Boost COMPONENTS
system filesystem regex program_options date_time thread chrono REQUIRED
)
if(NOT DEFINED Boost_INCLUDE_DIRS)
message(SEND_ERROR "Boost not found, or version below 1.46. Please download Boost!")
endif()
find_package(CryptoPP REQUIRED)
if(NOT DEFINED CRYPTO++_INCLUDE_DIR)
message(SEND_ERROR "Could not find Crypto++. Please download and install it first!")
endif()
find_package(MiniUPnPc)
if(NOT ${MINIUPNPC_FOUND})
set(WITH_UPNP OFF)
endif()
# Load includes
include_directories(
${CMAKE_SOURCE_DIR} ${Boost_INCLUDE_DIRS} ${CRYPTO++_INCLUDE_DIR}
"core/"
)
if(I2PD_DATA_PATH)
set(I2PD_DATA_DIR ${I2PD_DATA_PATH})
# Using custom path, make sure the code knows about this
add_definitions(-DI2PD_CUSTOM_DATA_PATH="${I2PD_DATA_PATH}")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(I2PD_DATA_DIR "$ENV{APPDATA}\\i2pd")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(I2PD_DATA_DIR "$ENV{HOME}/Library/Application Support/i2pd")
else()
set(I2PD_DATA_DIR "$ENV{HOME}/.i2pd")
endif()
# Show summary
message(STATUS "---------------------------------------")
message(STATUS "Build type : ${CMAKE_BUILD_TYPE}")
message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}")
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
message(STATUS "I2PD data directory: ${I2PD_DATA_DIR}")
message(STATUS "Options:")
message(STATUS " AESNI : ${WITH_AESNI}")
message(STATUS " HARDENING : ${WITH_HARDENING}")
message(STATUS " LIBRARY : ${WITH_LIBRARY}")
message(STATUS " BINARY : ${WITH_BINARY}")
message(STATUS " STATIC BUILD : ${WITH_STATIC}")
message(STATUS " UPnP : ${WITH_UPNP}")
message(STATUS " TESTS : ${WITH_TESTS}")
message(STATUS " BENCHMARKING : ${WITH_BENCHMARK}")
message(STATUS " OPTIMIZATION : ${WITH_OPTIMIZE}")
message(STATUS "---------------------------------------")
# Handle paths nicely
include(GNUInstallDirs)
set(CORE_NAME "${PROJECT_NAME}-core")
set(CLIENT_NAME "${PROJECT_NAME}-client")
set(TESTS_NAME "${PROJECT_NAME}-tests")
set(BENCHMARK_NAME "${PROJECT_NAME}-benchmark")
add_subdirectory(core)
add_subdirectory(client)
add_subdirectory(tests)
add_subdirectory(benchmark)
if(WITH_BINARY)
file(MAKE_DIRECTORY "${I2PD_DATA_DIR}/webui")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/webui" DESTINATION "${I2PD_DATA_DIR}")
endif()

View File

@@ -1,318 +0,0 @@
#include <fstream>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "util.h"
#include "Log.h"
#include "Identity.h"
#include "ClientContext.h"
namespace i2p
{
namespace client
{
ClientContext context;
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
m_BOBCommandChannel (nullptr)
{
}
ClientContext::~ClientContext ()
{
delete m_HttpProxy;
delete m_SocksProxy;
delete m_SamBridge;
delete m_BOBCommandChannel;
}
void ClientContext::Start ()
{
if (!m_SharedLocalDestination)
{
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination;
m_SharedLocalDestination->Start ();
}
std::shared_ptr<ClientDestination> localDestination;
// proxies
std::string proxyKeys = i2p::util::config::GetArg("-proxykeys", "");
if (proxyKeys.length () > 0)
localDestination = LoadLocalDestination (proxyKeys, false);
LogPrint(eLogInfo, "Clients: starting HTTP Proxy");
m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyaddress", "127.0.0.1"), i2p::util::config::GetArg("-httpproxyport", 4446), localDestination);
m_HttpProxy->Start();
LogPrint(eLogInfo, "Clients: starting SOCKS Proxy");
m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyaddress", "127.0.0.1"), i2p::util::config::GetArg("-socksproxyport", 4447), localDestination);
m_SocksProxy->Start();
// I2P tunnels
ReadTunnels ();
// SAM
int samPort = i2p::util::config::GetArg("-samport", 0);
if (samPort)
{
LogPrint(eLogInfo, "Clients: starting SAM bridge");
m_SamBridge = new SAMBridge (i2p::util::config::GetArg("-samaddress", "127.0.0.1"), samPort);
m_SamBridge->Start ();
}
// BOB
int bobPort = i2p::util::config::GetArg("-bobport", 0);
if (bobPort)
{
LogPrint(eLogInfo, "Clients: starting BOB command channel");
m_BOBCommandChannel = new BOBCommandChannel (i2p::util::config::GetArg("-bobaddress", "127.0.0.1"), bobPort);
m_BOBCommandChannel->Start ();
}
m_AddressBook.Start ();
}
void ClientContext::Stop ()
{
LogPrint(eLogInfo, "Clients: stopping HTTP Proxy");
m_HttpProxy->Stop();
delete m_HttpProxy;
m_HttpProxy = nullptr;
LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy");
m_SocksProxy->Stop();
delete m_SocksProxy;
m_SocksProxy = nullptr;
for (auto& it: m_ClientTunnels)
{
LogPrint(eLogInfo, "Clients: stopping I2P client tunnel on port ", it.first);
it.second->Stop ();
}
m_ClientTunnels.clear ();
for (auto& it: m_ServerTunnels)
{
LogPrint(eLogInfo, "Clients: stopping I2P server tunnel");
it.second->Stop ();
}
m_ServerTunnels.clear ();
if (m_SamBridge)
{
LogPrint(eLogInfo, "Clients: stopping SAM bridge");
m_SamBridge->Stop ();
delete m_SamBridge;
m_SamBridge = nullptr;
}
if (m_BOBCommandChannel)
{
LogPrint(eLogInfo, "Clients: stopping BOB command channel");
m_BOBCommandChannel->Stop ();
delete m_BOBCommandChannel;
m_BOBCommandChannel = nullptr;
}
LogPrint(eLogInfo, "Clients: stopping AddressBook");
m_AddressBook.Stop ();
for (auto it: m_Destinations)
it.second->Stop ();
m_Destinations.clear ();
m_SharedLocalDestination = nullptr;
}
std::shared_ptr<ClientDestination> ClientContext::LoadLocalDestination (const std::string& filename,
bool isPublic, i2p::data::SigningKeyType sigType)
{
i2p::data::PrivateKeys keys;
std::string fullPath = i2p::util::filesystem::GetFullPath (filename);
std::ifstream s(fullPath.c_str (), std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0, std::ios::end);
size_t len = s.tellg();
s.seekg (0, std::ios::beg);
uint8_t * buf = new uint8_t[len];
s.read ((char *)buf, len);
keys.FromBuffer (buf, len);
delete[] buf;
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
}
else
{
LogPrint (eLogError, "Clients: can't open file ", fullPath, " Creating new one with signature type ", sigType);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
size_t len = keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
len = keys.ToBuffer (buf, len);
f.write ((char *)buf, len);
delete[] buf;
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
}
std::shared_ptr<ClientDestination> localDestination = nullptr;
std::unique_lock<std::mutex> l(m_DestinationsMutex);
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " already exists");
localDestination = it->second;
}
else
{
localDestination = std::make_shared<ClientDestination> (keys, isPublic);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
}
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
}
void ClientContext::DeleteLocalDestination (std::shared_ptr<ClientDestination> destination)
{
if (!destination) return;
auto it = m_Destinations.find (destination->GetIdentHash ());
if (it != m_Destinations.end ())
{
auto d = it->second;
{
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations.erase (it);
}
d->Stop ();
}
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params)
{
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
if (!it->second->IsRunning ())
{
it->second->Start ();
return it->second;
}
return nullptr;
}
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const
{
auto it = m_Destinations.find (destination);
if (it != m_Destinations.end ())
return it->second;
return nullptr;
}
// should be moved in i2p::utils::fs
void ClientContext::ReadTunnels ()
{
boost::property_tree::ptree pt;
std::string pathTunnelsConfigFile = i2p::util::filesystem::GetTunnelsConfigFile().string();
try
{
boost::property_tree::read_ini (pathTunnelsConfigFile, pt);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "Clients: Can't read ", pathTunnelsConfigFile, ": ", ex.what ());
return;
}
int numClientTunnels = 0, numServerTunnels = 0;
for (auto& section: pt)
{
std::string name = section.first;
try
{
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT)
{
// mandatory params
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT);
// optional params
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1");
int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0);
i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (keys.length () > 0)
localDestination = LoadLocalDestination (keys, false, sigType);
auto clientTunnel = new I2PClientTunnel (dest, address, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
clientTunnel->Start ();
else
LogPrint (eLogError, "Clients: I2P client tunnel with port ", port, " already exists");
numClientTunnels++;
}
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP)
{
// mandatory params
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
int port = section.second.get<int> (I2P_SERVER_TUNNEL_PORT);
std::string keys = section.second.get<std::string> (I2P_SERVER_TUNNEL_KEYS);
// optional params
int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0);
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
auto localDestination = LoadLocalDestination (keys, true, sigType);
I2PServerTunnel * serverTunnel = (type == I2P_TUNNELS_SECTION_TYPE_HTTP) ? new I2PServerTunnelHTTP (host, port, localDestination, inPort) : new I2PServerTunnel (host, port, localDestination, inPort);
if (accessList.length () > 0)
{
std::set<i2p::data::IdentHash> idents;
size_t pos = 0, comma;
do
{
comma = accessList.find (',', pos);
i2p::data::IdentHash ident;
ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
idents.insert (ident);
pos = comma + 1;
}
while (comma != std::string::npos);
serverTunnel->SetAccessList (idents);
}
if (m_ServerTunnels.insert (std::make_pair (localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
serverTunnel->Start ();
else
LogPrint (eLogError, "Clients: I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
numServerTunnels++;
}
else
LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", pathTunnelsConfigFile);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ());
}
}
LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created");
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
}
}
}

View File

@@ -1,87 +0,0 @@
#ifndef CLIENT_CONTEXT_H__
#define CLIENT_CONTEXT_H__
#include <map>
#include <mutex>
#include <memory>
#include "Destination.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
#include "I2PTunnel.h"
#include "SAM.h"
#include "BOB.h"
#include "AddressBook.h"
namespace i2p
{
namespace client
{
const char I2P_TUNNELS_SECTION_TYPE[] = "type";
const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client";
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
const char I2P_CLIENT_TUNNEL_PORT[] = "port";
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
const char I2P_CLIENT_TUNNEL_KEYS[] = "keys";
const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
const char I2P_SERVER_TUNNEL_HOST[] = "host";
const char I2P_SERVER_TUNNEL_PORT[] = "port";
const char I2P_SERVER_TUNNEL_KEYS[] = "keys";
const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
const char I2P_SERVER_TUNNEL_INPORT[] = "inport";
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
class ClientContext
{
public:
ClientContext ();
~ClientContext ();
void Start ();
void Stop ();
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
std::shared_ptr<ClientDestination> CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
const std::map<std::string, std::string> * params = nullptr); // transient
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
const std::map<std::string, std::string> * params = nullptr);
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
std::shared_ptr<ClientDestination> LoadLocalDestination (const std::string& filename, bool isPublic,
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
AddressBook& GetAddressBook () { return m_AddressBook; };
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
private:
void ReadTunnels ();
private:
std::mutex m_DestinationsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<ClientDestination> > m_Destinations;
std::shared_ptr<ClientDestination> m_SharedLocalDestination;
AddressBook m_AddressBook;
i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy;
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel
SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel;
public:
// for HTTP
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
};
extern ClientContext context;
}
}
#endif

View File

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

281
Crypto.h
View File

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

View File

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

View File

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

View File

@@ -1,184 +0,0 @@
#include <thread>
#include <memory>
#include "Daemon.h"
#include "Log.h"
#include "Base.h"
#include "version.h"
#include "Transports.h"
#include "NTCPSession.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "NetDb.h"
#include "Garlic.h"
#include "util.h"
#include "Streaming.h"
#include "Destination.h"
#include "HTTPServer.h"
#include "I2PControl.h"
#include "ClientContext.h"
#include "Crypto.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
namespace i2p
{
namespace util
{
class Daemon_Singleton::Daemon_Singleton_Private
{
public:
Daemon_Singleton_Private() {};
~Daemon_Singleton_Private() {};
std::unique_ptr<i2p::util::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
#ifdef USE_UPNP
i2p::transport::UPnP m_UPnP;
#endif
};
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
Daemon_Singleton::~Daemon_Singleton() {
delete &d;
};
bool Daemon_Singleton::IsService () const
{
#ifndef _WIN32
return i2p::util::config::GetArg("-service", 0);
#else
return false;
#endif
}
bool Daemon_Singleton::init(int argc, char* argv[])
{
i2p::crypto::InitCrypto ();
i2p::util::config::OptionParser(argc, argv);
i2p::context.Init ();
LogPrint(eLogInfo, "\n\n\n\ni2pd v", VERSION, " starting\n");
LogPrint(eLogDebug, "data directory: ", i2p::util::filesystem::GetDataDir().string());
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
isDaemon = i2p::util::config::GetArg("-daemon", 0);
isLogging = i2p::util::config::GetArg("-log", 1);
int port = i2p::util::config::GetArg("-port", 0);
if (port)
i2p::context.UpdatePort (port);
std::string host = i2p::util::config::GetArg("-host", "");
if (host != "")
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0));
i2p::context.SetAcceptsTunnels (!i2p::util::config::GetArg("-notransit", 0));
bool isFloodfill = i2p::util::config::GetArg("-floodfill", 0);
i2p::context.SetFloodfill (isFloodfill);
auto bandwidth = i2p::util::config::GetArg("-bandwidth", "");
if (bandwidth.length () > 0)
{
if (bandwidth[0] > 'O')
i2p::context.SetExtraBandwidth ();
else if (bandwidth[0] > 'L')
i2p::context.SetHighBandwidth ();
else
i2p::context.SetLowBandwidth ();
}
else if (isFloodfill)
i2p::context.SetExtraBandwidth ();
LogPrint(eLogDebug, "Daemon: CMD parameters:");
for (int i = 0; i < argc; ++i)
LogPrint(eLogDebug, i, ": ", argv[i]);
return true;
}
bool Daemon_Singleton::start()
{
// initialize log
if (isLogging)
{
if (isDaemon)
{
std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string();
#ifndef _WIN32
logfile_path.append("/i2pd.log");
#else
logfile_path.append("\\i2pd.log");
#endif
StartLog (logfile_path);
} else {
StartLog (""); // write to stdout
}
g_Log->SetLogLevel(i2p::util::config::GetArg("-loglevel", "info"));
}
LogPrint(eLogInfo, "Daemon: staring HTTP Server");
d.httpServer = std::unique_ptr<i2p::util::HTTPServer>(new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpaddress", "127.0.0.1"), i2p::util::config::GetArg("-httpport", 7070)));
d.httpServer->Start();
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: starting UPnP");
d.m_UPnP.Start ();
#endif
LogPrint(eLogInfo, "Daemon: starting Transports");
i2p::transport::transports.Start();
LogPrint(eLogInfo, "Daemon: starting Tunnels");
i2p::tunnel::tunnels.Start();
LogPrint(eLogInfo, "Daemon: starting Client");
i2p::client::context.Start ();
// I2P Control
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
if (i2pcontrolPort)
{
LogPrint(eLogInfo, "Daemon: starting I2PControl");
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2p::util::config::GetArg("-i2pcontroladdress", "127.0.0.1"), i2pcontrolPort));
d.m_I2PControlService->Start ();
}
return true;
}
bool Daemon_Singleton::stop()
{
LogPrint(eLogInfo, "Daemon: shutting down");
LogPrint(eLogInfo, "Daemon: stopping Client");
i2p::client::context.Stop();
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
i2p::tunnel::tunnels.Stop();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: stopping UPnP");
d.m_UPnP.Stop ();
#endif
LogPrint(eLogInfo, "Daemon: stopping Transports");
i2p::transport::transports.Stop();
LogPrint(eLogInfo, "Daemon: stopping NetDB");
i2p::data::netdb.Stop();
LogPrint(eLogInfo, "Daemon: stopping HTTP Server");
d.httpServer->Stop();
d.httpServer = nullptr;
if (d.m_I2PControlService)
{
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
d.m_I2PControlService->Stop ();
d.m_I2PControlService = nullptr;
}
i2p::crypto::TerminateCrypto ();
StopLog ();
return true;
}
}
}

View File

@@ -1,71 +0,0 @@
#pragma once
#include <string>
#ifdef _WIN32
#define Daemon i2p::util::DaemonWin32::Instance()
#else
#define Daemon i2p::util::DaemonLinux::Instance()
#endif
namespace i2p
{
namespace util
{
class Daemon_Singleton_Private;
class Daemon_Singleton
{
public:
virtual bool init(int argc, char* argv[]);
virtual bool start();
virtual bool stop();
int isLogging;
int isDaemon;
int running;
protected:
Daemon_Singleton();
virtual ~Daemon_Singleton();
bool IsService () const;
// d-pointer for httpServer, httpProxy, etc.
class Daemon_Singleton_Private;
Daemon_Singleton_Private &d;
};
#ifdef _WIN32
class DaemonWin32 : public Daemon_Singleton
{
public:
static DaemonWin32& Instance()
{
static DaemonWin32 instance;
return instance;
}
virtual bool init(int argc, char* argv[]);
virtual bool start();
virtual bool stop();
};
#else
class DaemonLinux : public Daemon_Singleton
{
public:
static DaemonLinux& Instance()
{
static DaemonLinux instance;
return instance;
}
virtual bool start();
virtual bool stop();
private:
std::string pidfile;
int pidFH;
};
#endif
}
}

View File

@@ -1,123 +0,0 @@
#include "Daemon.h"
#ifndef _WIN32
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "Log.h"
#include "util.h"
void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
LogPrint(eLogInfo, "Daemon: Got SIGHUP, doing nothing");
// TODO:
break;
case SIGABRT:
case SIGTERM:
case SIGINT:
Daemon.running = 0; // Exit loop
break;
}
}
namespace i2p
{
namespace util
{
bool DaemonLinux::start()
{
if (isDaemon == 1)
{
pid_t pid;
pid = fork();
if (pid > 0) // parent
::exit (EXIT_SUCCESS);
if (pid < 0) // error
{
LogPrint(eLogError, "Daemon: could not fork: ", strerror(errno));
return false;
}
// child
umask(S_IWGRP | S_IRWXO); // 0027
int sid = setsid();
if (sid < 0)
{
LogPrint(eLogError, "Daemon: could not create process group.");
return false;
}
std::string d(i2p::util::filesystem::GetDataDir().string ()); // make a copy
if (chdir(d.c_str()) != 0)
{
LogPrint(eLogError, "Daemon: could not chdir: ", strerror(errno));
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);
}
// Pidfile
// this code is c-styled and a bit ugly, but we need fd for locking pidfile
pidfile = i2p::util::config::GetArg("-pidfile", "");
if (pidfile != "") {
pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
if (pidFH < 0)
{
LogPrint(eLogError, "Daemon: could not create pid file ", pidfile, ": ", strerror(errno));
return false;
}
if (lockf(pidFH, F_TLOCK, 0) != 0)
{
LogPrint(eLogError, "Daemon: could not lock pid file ", pidfile, ": ", strerror(errno));
return false;
}
char pid[10];
sprintf(pid, "%d\n", getpid());
ftruncate(pidFH, 0);
if (write(pidFH, pid, strlen(pid)) < 0)
{
LogPrint(eLogError, "Daemon: could not write pidfile: ", strerror(errno));
return false;
}
}
// Signal handler
struct sigaction sa;
sa.sa_handler = handle_signal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGHUP, &sa, 0);
sigaction(SIGABRT, &sa, 0);
sigaction(SIGTERM, &sa, 0);
sigaction(SIGINT, &sa, 0);
return Daemon_Singleton::start();
}
bool DaemonLinux::stop()
{
unlink(pidfile.c_str());
return Daemon_Singleton::stop();
}
}
}
#endif

View File

@@ -1,83 +0,0 @@
#include "Daemon.h"
#include "util.h"
#include "Log.h"
#ifdef _WIN32
#include "./Win32/Win32Service.h"
namespace i2p
{
namespace util
{
bool DaemonWin32::init(int argc, char* argv[])
{
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
if (!Daemon_Singleton::init(argc, argv)) return false;
if (I2PService::isService())
isDaemon = 1;
else
isDaemon = 0;
std::string serviceControl = i2p::util::config::GetArg("-service", "none");
if (serviceControl == "install")
{
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
exit(0);
}
else if (serviceControl == "remove")
{
UninstallService(SERVICE_NAME);
exit(0);
}
else if (serviceControl != "none")
{
printf(" --service=install to install the service.\n");
printf(" --service=remove to remove the service.\n");
}
if (isDaemon == 1)
{
LogPrint(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());
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else
LogPrint(eLogDebug, "Daemon: running as user");
return true;
}
bool DaemonWin32::start()
{
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
return Daemon_Singleton::start();
}
bool DaemonWin32::stop()
{
return Daemon_Singleton::stop();
}
}
}
#endif

View File

@@ -1,144 +0,0 @@
#include <string.h>
#include <vector>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "Log.h"
#include "TunnelBase.h"
#include "RouterContext.h"
#include "Destination.h"
#include "Datagram.h"
namespace i2p
{
namespace datagram
{
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner), m_Receiver (nullptr)
{
}
DatagramDestination::~DatagramDestination ()
{
}
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort)
{
uint8_t buf[MAX_DATAGRAM_SIZE];
auto identityLen = m_Owner->GetIdentity ()->ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen;
auto signatureLen = m_Owner->GetIdentity ()->GetSignatureLen ();
uint8_t * buf1 = signature + signatureLen;
size_t headerLen = identityLen + signatureLen;
memcpy (buf1, payload, len);
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(buf1, len, hash);
m_Owner->Sign (hash, 32, signature);
}
else
m_Owner->Sign (buf1, len, signature);
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto remote = m_Owner->FindLeaseSet (ident);
if (remote)
m_Owner->GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote));
else
m_Owner->RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg));
}
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, std::shared_ptr<I2NPMessage> msg)
{
if (remote)
SendMsg (msg, remote);
}
void DatagramDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto outboundTunnel = m_Owner->GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty () && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
uint32_t i = rand () % leases.size ();
auto garlic = m_Owner->WrapMessage (remote, msg, true);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
leases[i].tunnelGateway, leases[i].tunnelID,
garlic
});
outboundTunnel->SendTunnelDataMsg (msgs);
}
else
{
if (outboundTunnel)
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
else
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
}
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
i2p::data::IdentityEx identity;
size_t identityLen = identity.FromBuffer (buf, len);
const uint8_t * signature = buf + identityLen;
size_t headerLen = identityLen + identity.GetSignatureLen ();
bool verified = false;
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(buf + headerLen, len - headerLen, hash);
verified = identity.Verify (hash, 32, signature);
}
else
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
if (verified)
{
auto it = m_ReceiversByPorts.find (toPort);
if (it != m_ReceiversByPorts.end ())
it->second (identity, fromPort, toPort, buf + headerLen, len -headerLen);
else if (m_Receiver != nullptr)
m_Receiver (identity, fromPort, toPort, buf + headerLen, len -headerLen);
else
LogPrint (eLogWarning, "Receiver for datagram is not set");
}
else
LogPrint (eLogWarning, "Datagram signature verification failed");
}
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
// unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen)
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{
auto msg = NewI2NPMessage ();
uint8_t * buf = msg->GetPayload ();
buf += 4; // reserve for length
size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
if (size)
{
htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, fromPort); // source port
htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData);
}
else
msg = nullptr;
return msg;
}
}
}

View File

@@ -1,61 +0,0 @@
#ifndef DATAGRAM_H__
#define DATAGRAM_H__
#include <inttypes.h>
#include <memory>
#include <functional>
#include <map>
#include "Base.h"
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
namespace i2p
{
namespace client
{
class ClientDestination;
}
namespace datagram
{
const size_t MAX_DATAGRAM_SIZE = 32768;
class DatagramDestination
{
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
public:
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port) { m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { m_ReceiversByPorts.erase (port); };
private:
void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default
std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
};
}
}
#endif

View File

@@ -1,705 +0,0 @@
#include <algorithm>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <openssl/rand.h>
#include "Log.h"
#include "util.h"
#include "Crypto.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "Destination.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), 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;
int outboundTunnelsQuantity = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
if (params)
{
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
if (len > 0)
{
inboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", len);
}
}
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = boost::lexical_cast<int>(it->second);
if (len > 0)
{
outboundTunnelLen = len;
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", len);
}
}
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = boost::lexical_cast<int>(it->second);
if (quantity > 0)
{
inboundTunnelsQuantity = quantity;
LogPrint (eLogInfo, "Destination: Inbound tunnels quantity set to ", quantity);
}
}
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = boost::lexical_cast<int>(it->second);
if (quantity > 0)
{
outboundTunnelsQuantity = quantity;
LogPrint (eLogInfo, "Destination: Outbound tunnels quantity set to ", quantity);
}
}
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
if (it != params->end ())
{
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
std::stringstream ss(it->second);
std::string b64;
while (std::getline (ss, b64, ','))
{
i2p::data::IdentHash ident;
ident.FromBase64 (b64);
explicitPeers->push_back (ident);
}
LogPrint (eLogInfo, "Destination: Explicit peers set to ", it->second);
}
}
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 ()
{
if (m_IsRunning)
Stop ();
for (auto it: m_LeaseSetRequests)
if (it.second->requestComplete) it.second->requestComplete (nullptr);
m_LeaseSetRequests.clear ();
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
if (m_DatagramDestination)
delete m_DatagramDestination;
}
void ClientDestination::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Destination: runtime exception: ", ex.what ());
}
}
}
void ClientDestination::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, this));
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this ()); // TODO:
m_StreamingDestination->Start ();
for (auto it: m_StreamingDestinationsByPorts)
it.second->Start ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
this, std::placeholders::_1));
}
}
void ClientDestination::Stop ()
{
if (m_IsRunning)
{
m_CleanupTimer.cancel ();
m_IsRunning = false;
m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto it: m_StreamingDestinationsByPorts)
it.second->Stop ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
if (m_Pool)
{
m_Pool->SetLocalDestination (nullptr);
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
}
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
}
}
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
{
if (it->second->HasNonExpiredLeases ())
return it->second;
else
LogPrint (eLogWarning, "Destination: All leases of remote LeaseSet expired");
}
else
{
auto ls = i2p::data::netdb.FindLeaseSet (ident);
if (ls)
{
m_RemoteLeaseSets[ident] = ls;
return ls;
}
}
return nullptr;
}
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::GetLeaseSet ()
{
if (!m_Pool) return nullptr;
if (!m_LeaseSet)
UpdateLeaseSet ();
return m_LeaseSet;
}
void ClientDestination::UpdateLeaseSet ()
{
m_LeaseSet.reset (new i2p::data::LeaseSet (m_Pool));
}
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{
struct
{
uint8_t k[32], t[32];
} data;
memcpy (data.k, key, 32);
memcpy (data.t, tag, 32);
m_Service.post ([this,data](void)
{
this->AddSessionKey (data.k, data.t);
});
return true;
}
void ClientDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, this, msg));
}
void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg));
}
void ClientDestination::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)
{
case eI2NPData:
HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
case eI2NPDeliveryStatus:
// we assume tunnel tests non-encrypted
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
break;
case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
default:
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
}
}
void ClientDestination::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;
if (replyToken)
{
LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
offset += 36;
}
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
{
LogPrint (eLogDebug, "Remote LeaseSet");
auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET);
if (it != m_RemoteLeaseSets.end ())
{
leaseSet = it->second;
leaseSet->Update (buf + offset, len - offset);
if (leaseSet->IsValid ())
LogPrint (eLogDebug, "Remote LeaseSet updated");
else
{
LogPrint (eLogDebug, "Remote LeaseSet update failed");
m_RemoteLeaseSets.erase (it);
leaseSet = nullptr;
}
}
else
{
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
if (leaseSet->IsValid ())
{
LogPrint (eLogDebug, "New remote LeaseSet added");
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet;
}
else
{
LogPrint (eLogError, "New remote LeaseSet verification failed");
leaseSet = nullptr;
}
}
}
else
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET);
if (it1 != m_LeaseSetRequests.end ())
{
it1->second->requestTimeoutTimer.cancel ();
if (it1->second->requestComplete) it1->second->requestComplete (leaseSet);
m_LeaseSetRequests.erase (it1);
}
}
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
{
i2p::data::IdentHash key (buf);
int num = buf[32]; // num
LogPrint (eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64 (), " num=", num);
auto it = m_LeaseSetRequests.find (key);
if (it != m_LeaseSetRequests.end ())
{
auto request = it->second;
bool found = false;
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
{
for (int i = 0; i < num; i++)
{
i2p::data::IdentHash peerHash (buf + 33 + i*32);
auto floodfill = i2p::data::netdb.FindRouter (peerHash);
if (floodfill)
{
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
else
{
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
i2p::data::netdb.RequestDestination (peerHash);
}
}
if (!found)
LogPrint (eLogError, "Destination: Suggested floodfills are not presented in netDb");
}
else
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
if (!found)
{
if (request->requestComplete) request->requestComplete (nullptr);
m_LeaseSetRequests.erase (key);
}
}
else
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
}
void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed");
m_ExcludedFloodfills.clear ();
m_PublishReplyToken = 0;
}
else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
}
void ClientDestination::SetLeaseSetUpdated ()
{
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
UpdateLeaseSet ();
if (m_IsPublic)
Publish ();
}
void ClientDestination::Publish ()
{
if (!m_LeaseSet || !m_Pool)
{
LogPrint (eLogError, "Destination: Can't publish non-existing LeaseSet");
return;
}
if (m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending");
return;
}
auto outbound = m_Pool->GetNextOutboundTunnel ();
if (!outbound)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
return;
}
std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
if (!floodfill)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
m_ExcludedFloodfills.clear ();
return;
}
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
this, std::placeholders::_1));
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
}
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_PublishReplyToken)
{
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again");
m_PublishReplyToken = 0;
Publish ();
}
}
}
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = bufbe32toh (buf);
buf += 4;
// we assume I2CP payload
uint16_t fromPort = bufbe16toh (buf + 4), // source
toPort = bufbe16toh (buf + 6); // destination
switch (buf[9])
{
case PROTOCOL_TYPE_STREAMING:
{
// streaming protocol
auto dest = GetStreamingDestination (toPort);
if (dest)
dest->HandleDataMessagePayload (buf, length);
else
LogPrint (eLogError, "Destination: Missing streaming destination");
}
break;
case PROTOCOL_TYPE_DATAGRAM:
// datagram protocol
if (m_DatagramDestination)
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
else
LogPrint (eLogError, "Destination: Missing datagram destination");
break;
default:
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) {
assert(streamRequestComplete);
auto leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (leaseSet, port));
else
{
RequestDestination (dest,
[this, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
streamRequestComplete(CreateStream (ls, port));
else
streamRequestComplete (nullptr);
});
}
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
if (m_StreamingDestination)
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
else
return nullptr;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const
{
if (port)
{
auto it = m_StreamingDestinationsByPorts.find (port);
if (it != m_StreamingDestinationsByPorts.end ())
return it->second;
}
// if port is zero or not found, use default destination
return m_StreamingDestination;
}
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
{
if (m_StreamingDestination)
m_StreamingDestination->SetAcceptor (acceptor);
}
void ClientDestination::StopAcceptingStreams ()
{
if (m_StreamingDestination)
m_StreamingDestination->ResetAcceptor ();
}
bool ClientDestination::IsAcceptingStreams () const
{
if (m_StreamingDestination)
return m_StreamingDestination->IsAcceptorSet ();
return false;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port)
{
auto dest = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this (), port);
if (port)
m_StreamingDestinationsByPorts[port] = dest;
else // update default
m_StreamingDestination = dest;
return dest;
}
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{
if (!m_DatagramDestination)
m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
return m_DatagramDestination;
}
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
if (!m_Pool || !IsReady ())
{
if (requestComplete) requestComplete (nullptr);
return false;
}
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true;
}
void ClientDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
{
auto s = shared_from_this ();
m_Service.post ([dest, s](void)
{
auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ())
s->m_LeaseSetRequests.erase (it);
});
}
void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill)
{
auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestComplete = requestComplete;
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
if (ret.second) // inserted
{
if (!SendLeaseSetRequest (dest, floodfill, request))
{
// request failed
if (request->requestComplete) request->requestComplete (nullptr);
m_LeaseSetRequests.erase (dest);
}
}
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");
}
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
{
auto replyTunnel = m_Pool->GetNextInboundTunnel ();
if (!replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
auto outboundTunnel = m_Pool->GetNextOutboundTunnel ();
if (!outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
if (replyTunnel && 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,
replyTunnel.get (), replyKey, replyTag));
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,
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)
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)
{
if (it->second->requestComplete) it->second->requestComplete (nullptr);
m_LeaseSetRequests.erase (it);
}
}
}
}
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
CleanupRoutingSessions ();
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 ()
{
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
{
if (!it->second->HasNonExpiredLeases ()) // all leases expired
{
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_RemoteLeaseSets.erase (it);
}
else
it++;
}
}
void ClientDestination::PersistTemporaryKeys ()
{
auto path = i2p::util::filesystem::GetDefaultDataDir() / "destinations";
auto filename = path / (GetIdentHash ().ToBase32 () + ".dat");
std::ifstream f(filename.string (), std::ifstream::binary);
if (f)
{
f.read ((char *)m_EncryptionPublicKey, 256);
f.read ((char *)m_EncryptionPrivateKey, 256);
}
if (!f)
{
LogPrint (eLogInfo, "Creating new temporary keys for address ", GetIdentHash ().ToBase32 ());
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
if (!boost::filesystem::exists (path))
{
if (!boost::filesystem::create_directory (path))
LogPrint (eLogError, "Failed to create destinations directory");
}
std::ofstream f1 (filename.string (), std::ofstream::binary | std::ofstream::out);
if (f1)
{
f1.write ((char *)m_EncryptionPublicKey, 256);
f1.write ((char *)m_EncryptionPrivateKey, 256);
}
}
}
}
}

View File

@@ -1,159 +0,0 @@
#ifndef DESTINATION_H__
#define DESTINATION_H__
#include <thread>
#include <mutex>
#include <memory>
#include <map>
#include <set>
#include <string>
#include <functional>
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
#include "Crypto.h"
#include "LeaseSet.h"
#include "Garlic.h"
#include "NetDb.h"
#include "Streaming.h"
#include "Datagram.h"
namespace i2p
{
namespace client
{
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18;
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
const int DESTINATION_CLEANUP_TIMEOUT = 20; // in minutes
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
// I2CP
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class ClientDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<ClientDestination>
{
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found
struct LeaseSetRequest
{
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
RequestComplete requestComplete;
};
public:
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
virtual void Start ();
virtual void Stop ();
bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases () && 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); // 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 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<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated ();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
private:
void Run ();
void UpdateLeaseSet ();
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets ();
void PersistTemporaryKeys ();
private:
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;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination;
boost::asio::deadline_timer m_PublishConfirmationTimer, m_CleanupTimer;
public:
// for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
};
}
}
#endif

View File

@@ -1,627 +0,0 @@
#include <inttypes.h>
#include "I2PEndian.h"
#include <map>
#include <string>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Timestamp.h"
#include "Log.h"
#include "Garlic.h"
namespace i2p
{
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
{
// create new session tags and session key
RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
}
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
m_SessionTags.push_back (sessionTag);
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
}
GarlicRoutingSession::~GarlicRoutingSession ()
{
for (auto it: m_UnconfirmedTagsMsgs)
delete it.second;
m_UnconfirmedTagsMsgs.clear ();
}
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
{
auto tags = new UnconfirmedTags (m_NumTags);
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++)
{
RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
}
return tags;
}
void GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
{
TagsConfirmed (msgID);
if (msgID == m_LeaseSetUpdateMsgID)
{
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
CleanupExpiredTags ();
}
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
{
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
UnconfirmedTags * tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
m_UnconfirmedTagsMsgs.erase (it);
delete tags;
}
}
bool GarlicRoutingSession::CleanupExpiredTags ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
it++;
}
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveCreatedSession (it->first);
delete it->second;
it = m_UnconfirmedTagsMsgs.erase (it);
}
else
it++;
}
return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty ();
}
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
size_t len = 0;
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
// find non-expired tag
bool tagFound = false;
SessionTag tag;
if (m_NumTags > 0)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
while (!m_SessionTags.empty ())
{
if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
tag = m_SessionTags.front ();
m_SessionTags.pop_front (); // use same tag only once
tagFound = true;
break;
}
else
m_SessionTags.pop_front (); // remove expired tag
}
}
// create message
if (!tagFound) // new session
{
LogPrint (eLogWarning, "Garlic: No tags available. Use ElGamal");
if (!m_Destination)
{
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
return nullptr;
}
// create ElGamal block
ElGamalBlock elGamal;
memcpy (elGamal.sessionKey, m_SessionKey, 32);
RAND_bytes (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv);
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Encryption.SetIV (iv);
buf += 514;
len += 514;
}
else // existing session
{
// session tag
memcpy (buf, tag, 32);
uint8_t iv[32]; // IV is first 16 bytes
SHA256(tag, 32, iv);
m_Encryption.SetIV (iv);
buf += 32;
len += 32;
}
// AES block
len += CreateAESBlock (buf, msg);
htobe32buf (m->GetPayload (), len);
m->len += len + 4;
m->FillI2NPMessageHeader (eI2NPGarlic);
return m;
}
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
{
size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
blockSize += 2;
if (newTags) // session tags recreated
{
for (int i = 0; i < newTags->numTags; i++)
{
memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags
blockSize += 32;
}
}
uint32_t * payloadSize = (uint32_t *)(buf + blockSize);
blockSize += 4;
uint8_t * payloadHash = buf + blockSize;
blockSize += 32;
buf[blockSize] = 0; // flag
blockSize++;
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
htobe32buf (payloadSize, len);
SHA256(buf + blockSize, len, payloadHash);
blockSize += len;
size_t rem = blockSize % 16;
if (rem)
blockSize += (16-rem); //padding
m_Encryption.Encrypt(buf, blockSize, buf);
return blockSize;
}
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
size_t size = 0;
uint8_t * numCloves = payload + size;
*numCloves = 0;
size++;
if (m_Owner)
{
// resubmit non-confirmed LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted &&
i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
// attach DeviveryStatus if necessary
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
{
// clove is DeliveryStatus
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
if (cloveSize > 0) // successive?
{
size += cloveSize;
(*numCloves)++;
if (newTags) // new tags created
m_UnconfirmedTagsMsgs[msgID] = newTags;
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
}
else
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
}
// attach LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
{
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
m_LeaseSetUpdateMsgID = msgID;
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
// clove if our leaseSet must be attached
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
size += CreateGarlicClove (payload + size, leaseSet, false);
(*numCloves)++;
}
}
if (msg) // clove message ifself if presented
{
size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false);
(*numCloves)++;
}
memset (payload + size, 0, 3); // certificate of message
size += 3;
htobe32buf (payload + size, msgID); // MessageID
size += 4;
htobe64buf (payload + size, ts); // Expiration of message
size += 8;
return size;
}
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
size_t size = 0;
if (isDestination && m_Destination)
{
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
size++;
memcpy (buf + size, m_Destination->GetIdentHash (), 32);
size += 32;
}
else
{
buf[size] = 0;// delivery instructions flag local
size++;
}
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4;
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
memset (buf + size, 0, 3); // certificate of clove
size += 3;
return size;
}
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
{
size_t size = 0;
if (m_Owner)
{
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
if (inboundTunnel)
{
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
size++;
// hash and tunnelID sequence is reversed for Garlic
memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash
size += 32;
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
size += 4;
// create msg
auto msg = CreateDeliveryStatusMsg (msgID);
if (m_Owner)
{
//encrypt
uint8_t key[32], tag[32];
RAND_bytes (key, 32); // random session key
RAND_bytes (tag, 32); // random session tag
m_Owner->SubmitSessionKey (key, tag);
GarlicRoutingSession garlic (key, tag);
msg = garlic.WrapSingleMessage (msg);
}
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
// fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4;
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
memset (buf + size, 0, 3); // certificate of clove
size += 3;
}
else
LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus");
}
else
LogPrint (eLogWarning, "Garlic: Missing local LeaseSet");
return size;
}
GarlicDestination::~GarlicDestination ()
{
}
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
{
if (key)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
decryption->SetKey (key);
m_Tags[SessionTag(tag, ts)] = decryption;
}
}
bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{
AddSessionKey (key, tag);
return true;
}
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
uint8_t * buf = msg->GetPayload ();
uint32_t length = bufbe32toh (buf);
if (length > msg->GetLength ())
{
LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ());
return;
}
buf += 4; // length
auto it = m_Tags.find (SessionTag(buf));
if (it != m_Tags.end ())
{
// tag found. Use AES
if (length >= 32)
{
uint8_t iv[32]; // IV is first 16 bytes
SHA256(buf, 32, iv);
it->second->SetIV (iv);
it->second->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
}
else
LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes");
m_Tags.erase (it); // tag might be used only once
}
else
{
// tag not found. Use ElGamal
ElGamalBlock elGamal;
if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
{
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
decryption->SetKey (elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv);
decryption->SetIV (iv);
decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
}
else
LogPrint (eLogError, "Garlic: Failed to decrypt message");
}
// cleanup expired tags
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
if (m_LastTagsCleanupTime)
{
int numExpiredTags = 0;
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
{
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
numExpiredTags++;
it = m_Tags.erase (it);
}
else
it++;
}
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
}
m_LastTagsCleanupTime = ts;
}
}
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
uint16_t tagCount = bufbe16toh (buf);
buf += 2; len -= 2;
if (tagCount > 0)
{
if (tagCount*32 > len)
{
LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len);
return ;
}
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < tagCount; i++)
m_Tags[SessionTag(buf + i*32, ts)] = decryption;
}
buf += tagCount*32;
len -= tagCount*32;
uint32_t payloadSize = bufbe32toh (buf);
if (payloadSize > len)
{
LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize);
return;
}
buf += 4;
uint8_t * payloadHash = buf;
buf += 32;// payload hash.
if (*buf) // session key?
buf += 32; // new session key
buf++; // flag
// payload
uint8_t digest[32];
SHA256 (buf, payloadSize, digest);
if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match
{
LogPrint (eLogError, "Garlic: wrong payload hash");
return;
}
HandleGarlicPayload (buf, payloadSize, from);
}
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
const uint8_t * buf1 = buf;
int numCloves = buf[0];
LogPrint (eLogDebug, "Garlic: ", numCloves," cloves");
buf++;
for (int i = 0; i < numCloves; i++)
{
// delivery instructions
uint8_t flag = buf[0];
buf++; // flag
if (flag & 0x80) // encrypted?
{
// TODO: implement
LogPrint (eLogWarning, "Garlic: clove encrypted");
buf += 32;
}
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
switch (deliveryType)
{
case eGarlicDeliveryTypeLocal:
LogPrint (eLogDebug, "Garlic: type local");
HandleI2NPMessage (buf, len, from);
break;
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination");
buf += 32; // destination. check it later or for multiple destinations
HandleI2NPMessage (buf, len, from);
break;
case eGarlicDeliveryTypeTunnel:
{
LogPrint (eLogDebug, "Garlic: type tunnel");
// gwHash and gwTunnel sequence is reverted
uint8_t * gwHash = buf;
buf += 32;
uint32_t gwTunnel = bufbe32toh (buf);
buf += 4;
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from && from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel) // we have send it through an outbound tunnel
{
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
}
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
break;
}
case eGarlicDeliveryTypeRouter:
LogPrint (eLogWarning, "Garlic: type router not supported");
buf += 32;
break;
default:
LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
}
buf += GetI2NPMessageLength (buf); // I2NP
buf += 4; // CloveID
buf += 8; // Date
buf += 3; // Certificate
if (buf - buf1 > (int)len)
{
LogPrint (eLogError, "Garlic: clove is too long");
break;
}
}
}
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
{
auto session = GetRoutingSession (destination, attachLeaseSet); // 32 tags by default
return session->WrapSingleMessage (msg);
}
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{
auto it = m_Sessions.find (destination->GetIdentHash ());
std::shared_ptr<GarlicRoutingSession> session;
if (it != m_Sessions.end ())
session = it->second;
if (!session)
{
session = std::make_shared<GarlicRoutingSession> (this, destination,
attachLeaseSet ? 40 : 4, attachLeaseSet); // 40 tags for connections and 4 for LS requests
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
}
void GarlicDestination::CleanupRoutingSessions ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
{
if (!it->second->CleanupExpiredTags ())
{
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
it = m_Sessions.erase (it);
}
else
it++;
}
}
void GarlicDestination::RemoveCreatedSession (uint32_t msgID)
{
m_CreatedSessions.erase (msgID);
}
void GarlicDestination::DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID)
{
m_CreatedSessions[msgID] = session;
}
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload ());
{
auto it = m_CreatedSessions.find (msgID);
if (it != m_CreatedSessions.end ())
{
it->second->MessageConfirmed (msgID);
m_CreatedSessions.erase (it);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
}
}
void GarlicDestination::SetLeaseSetUpdated ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it: m_Sessions)
it.second->SetLeaseSetUpdated ();
}
void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleGarlicMessage (msg);
}
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleDeliveryStatusMessage (msg);
}
}
}

168
Garlic.h
View File

@@ -1,168 +0,0 @@
#ifndef GARLIC_H__
#define GARLIC_H__
#include <inttypes.h>
#include <map>
#include <list>
#include <string>
#include <thread>
#include <mutex>
#include <memory>
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "LeaseSet.h"
#include "Queue.h"
#include "Identity.h"
namespace i2p
{
namespace garlic
{
enum GarlicDeliveryType
{
eGarlicDeliveryTypeLocal = 0,
eGarlicDeliveryTypeDestination = 1,
eGarlicDeliveryTypeRouter = 2,
eGarlicDeliveryTypeTunnel = 3
};
#pragma pack(1)
struct ElGamalBlock
{
uint8_t sessionKey[32];
uint8_t preIV[32];
uint8_t padding[158];
};
#pragma pack()
const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
struct SessionTag: public i2p::data::Tag<32>
{
SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {};
SessionTag () = default;
SessionTag (const SessionTag& ) = default;
SessionTag& operator= (const SessionTag& ) = default;
#ifndef _WIN32
SessionTag (SessionTag&& ) = default;
SessionTag& operator= (SessionTag&& ) = default;
#endif
uint32_t creationTime; // seconds since epoch
};
class GarlicDestination;
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
{
enum LeaseSetUpdateStatus
{
eLeaseSetUpToDate = 0,
eLeaseSetUpdated,
eLeaseSetSubmitted,
eLeaseSetDoNotSend
};
struct UnconfirmedTags
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags () { delete[] sessionTags; };
int numTags;
SessionTag * sessionTags;
uint32_t tagsCreationTime;
};
public:
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
int numTags, bool attachLeaseSet);
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~GarlicRoutingSession ();
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
void SetLeaseSetUpdated ()
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
void TagsConfirmed (uint32_t msgID);
UnconfirmedTags * GenerateSessionTags ();
private:
GarlicDestination * m_Owner;
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags;
int m_NumTags;
std::map<uint32_t, UnconfirmedTags *> m_UnconfirmedTagsMsgs;
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
i2p::crypto::CBCEncryption m_Encryption;
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
};
class GarlicDestination: public i2p::data::LocalDestination
{
public:
GarlicDestination (): m_LastTagsCleanupTime (0) {};
~GarlicDestination ();
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupRoutingSessions ();
void RemoveCreatedSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID);
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
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<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
protected:
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
private:
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private:
// outgoing sessions
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<GarlicRoutingSession> > m_Sessions;
// incoming
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
uint32_t m_LastTagsCleanupTime;
// DeliveryStatus
std::map<uint32_t, std::shared_ptr<GarlicRoutingSession> > m_CreatedSessions; // msgID -> session
};
}
}
#endif

View File

@@ -1,299 +0,0 @@
#include <cstring>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <string>
#include <atomic>
#include "HTTPProxy.h"
#include "util.h"
#include "Identity.h"
#include "Streaming.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include "I2PTunnel.h"
namespace i2p
{
namespace proxy
{
static const size_t http_buffer_size = 8192;
class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler>
{
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);
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void AsyncSockRead();
void HTTPRequestFailed(/*std::string message*/);
void ExtractRequest();
bool ValidateHTTPRequest();
void HandleJumpServices();
bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
void SentHTTPFailed(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
uint8_t m_http_buff[http_buffer_size];
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::string m_request; //Data left to be sent
std::string m_url; //URL
std::string m_method; //Method
std::string m_version; //HTTP version
std::string m_address; //Address
std::string m_path; //Path
int m_port; //Port
state m_state;//Parsing state
public:
HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock)
{ EnterState(GET_METHOD); }
~HTTPProxyHandler() { Terminate(); }
void Handle () { AsyncSockRead(); }
};
void HTTPProxyHandler::AsyncSockRead()
{
LogPrint(eLogDebug,"--- HTTP Proxy async sock read");
if(m_sock) {
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint(eLogError,"--- HTTP Proxy no socket for read");
}
}
void HTTPProxyHandler::Terminate() {
if (Kill()) return;
if (m_sock)
{
LogPrint(eLogDebug,"--- HTTP Proxy close sock");
m_sock->close();
m_sock = nullptr;
}
Done(shared_from_this());
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
{
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate)
{
m_state = nstate;
}
void HTTPProxyHandler::ExtractRequest()
{
LogPrint(eLogDebug,"--- HTTP Proxy method is: ", m_method, "\nRequest is: ", m_url);
std::string server="";
std::string port="80";
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
boost::smatch m;
std::string path;
if(boost::regex_search(m_url, m, rHTTP, boost::match_extra))
{
server=m[1].str();
if (m[2].str() != "") port=m[3].str();
path=m[4].str();
}
LogPrint(eLogDebug,"--- HTTP Proxy server is: ",server, " port is: ", port, "\n path is: ",path);
m_address = server;
m_port = boost::lexical_cast<int>(port);
m_path = path;
}
bool HTTPProxyHandler::ValidateHTTPRequest()
{
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
{
LogPrint(eLogError,"--- HTTP Proxy unsupported version: ", m_version);
HTTPRequestFailed(); //TODO: send right stuff
return false;
}
return true;
}
void HTTPProxyHandler::HandleJumpServices()
{
static const char * helpermark1 = "?i2paddresshelper=";
static const char * helpermark2 = "&i2paddresshelper=";
size_t addressHelperPos1 = m_path.rfind (helpermark1);
size_t addressHelperPos2 = m_path.rfind (helpermark2);
size_t addressHelperPos;
if (addressHelperPos1 == std::string::npos)
{
if (addressHelperPos2 == std::string::npos)
return; //Not a jump service
else
addressHelperPos = addressHelperPos2;
}
else
{
if (addressHelperPos2 == std::string::npos)
addressHelperPos = addressHelperPos1;
else if ( addressHelperPos1 > addressHelperPos2 )
addressHelperPos = addressHelperPos1;
else
addressHelperPos = addressHelperPos2;
}
auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1));
base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded
LogPrint (eLogDebug,"Jump service for ", m_address, " found at ", base64, ". Inserting to address book");
//TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/
//TODO: we could redirect the user again to avoid dirtiness in the browser
i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64);
m_path.erase(addressHelperPos);
}
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
{
ExtractRequest(); //TODO: parse earlier
if (!ValidateHTTPRequest()) return false;
HandleJumpServices();
m_request = m_method;
m_request.push_back(' ');
m_request += m_path;
m_request.push_back(' ');
m_request += m_version;
m_request.push_back('\r');
m_request.push_back('\n');
m_request.append("Connection: close\r\n");
m_request.append(reinterpret_cast<const char *>(http_buff),len);
return true;
}
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
{
assert(len); // This should always be called with a least a byte left to parse
while (len > 0)
{
//TODO: fallback to finding HOst: header if needed
switch (m_state)
{
case GET_METHOD:
switch (*http_buff)
{
case ' ': EnterState(GET_HOSTNAME); break;
default: m_method.push_back(*http_buff); break;
}
break;
case GET_HOSTNAME:
switch (*http_buff)
{
case ' ': EnterState(GET_HTTPV); break;
default: m_url.push_back(*http_buff); break;
}
break;
case GET_HTTPV:
switch (*http_buff)
{
case '\r': EnterState(GET_HTTPVNL); break;
default: m_version.push_back(*http_buff); break;
}
break;
case GET_HTTPVNL:
switch (*http_buff)
{
case '\n': EnterState(DONE); break;
default:
LogPrint(eLogError,"--- HTTP Proxy rejected invalid request ending with: ", ((int)*http_buff));
HTTPRequestFailed(); //TODO: add correct code
return false;
}
break;
default:
LogPrint(eLogError,"--- HTTP Proxy invalid state: ", m_state);
HTTPRequestFailed(); //TODO: add correct code 500
return false;
}
http_buff++;
len--;
if (m_state == DONE)
return CreateHTTPRequest(http_buff,len);
}
return true;
}
void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
LogPrint(eLogDebug,"--- HTTP Proxy sock recv: ", len);
if(ecode)
{
LogPrint(eLogWarning," --- HTTP Proxy sock recv got error: ", ecode);
Terminate();
return;
}
if (HandleData(m_http_buff, len))
{
if (m_state == DONE)
{
LogPrint(eLogInfo,"--- HTTP Proxy requested: ", m_url);
GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address, m_port);
}
else
AsyncSockRead();
}
}
void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
{
if (!ecode)
Terminate();
else
{
LogPrint (eLogError,"--- HTTP Proxy Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
}
void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (Kill()) return;
LogPrint (eLogInfo,"--- HTTP Proxy New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
Done(shared_from_this());
}
else
{
LogPrint (eLogError,"--- HTTP Proxy Issue when creating the stream, check the previous warnings for more info.");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
}
}
HTTPProxyServer::HTTPProxyServer(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)
{
return std::make_shared<HTTPProxyHandler> (this, socket);
}
}
}

View File

@@ -1,32 +0,0 @@
#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
{
public:
HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
~HTTPProxyServer() {};
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;
}
}
#endif

View File

@@ -1,852 +0,0 @@
#include <ctime>
#include <iomanip>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "Base.h"
#include "Log.h"
#include "Tunnel.h"
#include "TransitTunnel.h"
#include "Transports.h"
#include "NetDb.h"
#include "I2PEndian.h"
#include "Streaming.h"
#include "Destination.h"
#include "RouterContext.h"
#include "ClientContext.h"
#include "HTTPServer.h"
// For image and info
#include "version.h"
namespace i2p
{
namespace util
{
const std::string HTTPConnection::itoopieImage =
"<img alt=\"ICToopie Icon\" src=\"data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXM"
"AAA3XAAAN1wFCKJt4AAAAB3RJTUUH3ggRChYFXVBoSgAAIABJREFUeNrtnXl8VOX1/9/PvXcmewiQBB"
"J2CKsKihQXkCJuiD8VKyptXejXaikWbe1C1dqi0lpr7UvrgihV64ZCXaqCUBEUQVBAAZUl7EtYEkLIP"
"pmZ+zy/P+6dySx3JgESkpAcX/MiznLvc8/5POc55zznnAfaqFWTaIXPnAt0AzoBqYAB1AAVwAFgF3Ck"
"DQCnBvUHxgEXAMOBLsfw22+BT4ElwGKgrE1ftAwaBswEygFlv+QJvALX2AH8BchrY3HzpF8A+xtA4PU"
"BwxZgUhvLmwf9AfA1suBjgaEK+GWbDdA0dAswC0iwhVEvSk5A9smFThmIjFSUroPHC0cr0HYexNxTiH"
"aMfBFAiT2e99sA0PiUBXwMnFEfwZ/ZB3n1eTDmTDh3IMKdgoYZoi8CXBCABhioA/uRn3+H+OgreGcFq"
"vAoWj15udQ2Oj1tAGgcmgS8WJfgczsif3sd3D4OkZyCZnqPQUWEkKaBlgDbd2PO+gDx5H/B462TZwq4"
"zPYc2gDQgPQmcH084Z/eE/nkHYjRw9H8VQ17c02A5ka99j/kb59DHDgSl3cC+BswrQ0AJ04GsB4YFEv"
"47VJQr/8eNW4kuv8kKF8jEfXSfOSUf6JVe+PydhEwtg0Ax0/Jtv+dHesLU65EPn0Xmt/XJM+ibn0M8+"
"XF6HH4+xVwdhsAjp0Sgb1AB6dxCoH67B+oEaeh+80mVE8GLP0a8+LfI6R05KcA1gFntQHg2GgX0N3pg"
"87tkd/NRktPbj7jr/SghkxG7j7k6DEI23O5uLkxWWumwl8WS/i9OmPueQ3RnIQPkJKI2PUq+jkDgs5l"
"pGdwEfDPNgDUTQ9hbd5EUfds5PZ/owvRPIHr98Oqp9EvHBITBFOBa9qWgNg0FFjrZO1npKIOvgm61my"
"1Vq1d4IbhP0euzo9pE3TAih62ASCCioH2TrNn72vQuUPzF34QBDoqdyLywBHHMa+zwd62BITQX+yZEU"
"X/uR+V04TCN9ygFOaafNTbyzHnLsNc9g3S60ca7hjLgYlY9yxajNjFWcBNbRqgltKBUidmTRiFnPcnd"
"L9DwEUI0JNgz17k5xuRBYfRvX7I6YD8/mBEr+5o/uoTEHwC6vn3UE+9h9qwwxmAw/oh/3or4qJhaE5j"
"fGcF5vUzHH/rtV3dNgBgxfdvcfL1a+YjhIgep6Ej/zYX+eg8tMOlzs/RLQv52M/gujHo/pr6D0bXYG0"
"+5iX3II5W1I9Hlw/HnP8Qhimjtce432N+uDoKBAJ4AJje2gHQDjjqNPtn34265ZJwxmkarMnHvOi3iA"
"pP/cY/5izkx4/UL2CkaTBvGf6Jfw6L7gXus/aCCy4YcujQoZL8/HzdXrKC4x7UHfXdbLTI+1TXINPHO"
"/JbNLUMmoMNMN1J+DkdkLdeGc4cXYO3l+M/ZypaiPAFsHvMmDFFl1122ZoxY8Zsyc7OLgxl7JKv0YZM"
"RhquugezJh8zQvjmpEmT9hUWFuYrpc5etmyZsWXLliylVOLs2bPXCyFKA/fauAcxfjr+SLsgORHtjz+"
"OuYl1F62c/Dhk3My5F7/vQ1Toa8XjmIHPhRAK2L1w4cIDSimPiqCCgoJdI0aM2EtIptAtl+BTH4VfM/"
"SlPkalJ9feIyEhQa5fv36Nik/Fffv2LbHHIwH5v4ejx24uQkLttUNe+1uz8K/CIZUrIxVTLUWGMXAhM"
"tFdK/y8vLzNSimzDuGo++67b37oPdY8HS2cwOuZqWECqtm0adNaVT86AhQEftuvK361NAIAC1G/uc4R"
"AAo4s7UuAT9xUv+/uQ5l1tSqcE3A/f9GeWwru127dnu2bt3auz7jnzFjxriJEyeuEkIIgDufRjm5boY"
"bZn4QHIuYPn367gEDBtTXV2+/atWqI4GlIH8f2uYdhFkCUsG06x1/q2jCBNOmNgKVEwDK/otKctcK10"
"hEuS5G+U3LaNq5c2dhz549s4/hPj4hxFEgE6BoHmSkhj+7pmHqlwXvWaaUcmFtR9ebMjMzNxcXF/cHm"
"DEJNe2GcIAabjhnCuaXW6KAexCrYKVVaQDH2TW8PzItNXxcK9cjbeGTnZ295xiFD+CaMmWKPwD4uZ9G"
"g+7bnbX3vP766w8fq/ABpk2bFrTqV26ytorDjB0v3Oi8H5hje0OtCgCOrJh4ocWoUFqxsXac11xzzXG"
"Nefz48cGrLvsWZUSkcBwuq00RHTNmzHFlGFx55ZU5gb93HUQ6cffakTG17oWtDQDnO6n/K8+JUs1s3x"
"9cT8WgQYNkHdfdiVUVFEaDBw/2Bf7eVgCROTyGXntfl8t1XBmFOTk5e4O+vxflJOrcXLTUxKjdQgWc0"
"9oAcKZT5C+vdzjbBODzhwfqnC722Wef7cnMzNwthOglhEjMzMxct2HDhj1BARtG8CpHK6OF0yWz9u/8"
"/PxOAEoppJSlU6ZM2dipU6cCIcSXEyZM2KaUKncaQ3l5eXrQHkhHd/T8vTDydEctcEZrA0CPyDfOykP"
"hD2eOlJCdEXxPff7551FFmgsWLDg4atSorsXFxd3t2WQUFxcPGTJkSJeFCxceBti2bVtwoyk1CREpnD"
"7dEQGj9IknnvABFBcXl+u6rs+cOXNQYWFhLvC9t956K0/TtIMQvee/fPny4FUHdEcqf/RDmyYM6VN/m"
"+hUBUCa05uDutuhkgjdOLRvSFRvyZLIHcODV1xxRaxqHu3yyy/XgKqXXnopKI7enR3EZyLGnGnBwuPx"
"dP/666935+Xl7QNSIpYqJYToO3Xq1PWRN3vooYeqA98dOwzNdFislILeOTENwVYDAEeXp1uWNUOi7IJ"
"za4VbVFTUafXq1RtCZr+POFnDQIfbb7/962effbZdQDgjT7eyd8IsdB9MqQ09q6FDh3rKysoGOvquSq"
"mnnnoqzGpftGjRVxs3buwf+MrE0bFd7JwOxLJjcloLABz3/TukoTktmwkuxPgRwVmohg8fHtQg+/btK"
"60r1vD888+PCHXrbr7YWTjXjkHLzggKp59SKl5BUW9gD8CKFSu2jh07tm8AYPdMRCkVGwDtU2Omkbca"
"ACThLGhHhvtNeGZqqLEoemVnZx+srKwsGjhwYHo9A04A/L9zUZkZzs/t98D8GfUPjuXn538+ZsyYb0e"
"OHNkXq9sInTKQf/kpuowDHU3EvEdGawGA476cz4zN/OwMtNl3WxaCUkoVFRV1Sk1NTZg5c+aeY4k8vv"
"w7hN8f+wvD+qH9YzL1iQPI/v37T1y6dOnpAYClJKK+eQ7N74v/Q1PGXAJcrQUAjiyqjJO9oxTcOg7jr"
"7eGCSdtzpw5I6ln7eeqf0JaUvwZ7jfhVxMwnrmTuuINQa8By1CVB96AjLS6NUhI0CkKG60FAJVOb+4p"
"wtTjjMjvg2k3YCx6GJmUEK3eY1G3LGT+i6hhfev3vH4f/OwK9J2voEYPiS+UIX2Q707HXDsLPSkBrT7"
"rx/7imOOoONmCMJoIAMWOAChEF5qThx0+Q8eciV71PuqRNzGffg+xtyiaoalJyAuHwE8vR1w1yioaPZ"
"YScSmhayba0sfQjpYhF3yJ2rwXUVqJmdkO47QeyEuGItLSrHzF+qacCQFbC1Ax3NZDJ1sQTbUbmGxrg"
"TCZdEzHPPweRn0TOYUAPQHwYe4uRPj8kJwAudmAjoYv2t07YYYJazk67hnngot+g1yyzjE9zDjZy0BT"
"bgc7bgXXLEBqIqab1OLJSIbkSzCrvVFayw+4W4sNAFbxZxR9/DWnNB04gHQQPlhl5LQmAKx3evO9ldY"
"O4KlK76+KaYqsbG0AWO20BL35CWiJp6bwDRe8sTTmUvxxawOAIytKKtBWf4N5KgLA40EuXR+T5/NbGw"
"A+j/XB0/+1agBONZr5flxtqFobAMBqohRF//4IzedvGoY0mvpPRP15Tkz1/3JTjaupAfCvWK7oA68it"
"VOol/m8j5HFZTHd7tlNNa7mwOJYcT9VMx+haS2/pb2RiOr8A9ShEsdnWYjVXbRVagCAR2IAUdz+BKbR"
"wkNCQsATc5ExhC+AGU06vmbAowSs3rqOa/6GWaiB3WmxJmGlB5lxTUxeb8U61ILWrAFqgEdjgfHSe1C"
"Gq2UK30hAjbsvpvAF8KumHmNzmVnTsGLhUXTwCNqND+NvaSDQNXj4VczPN8bUspuABU0+zmbEs93EaK"
"H2zU60HlmYZ+WhqRbiHK74DnnTIzEnmMCqjDrU1ONsbhb2GuLkxy97DHX+ac0fBNv2Yw68NW73D59t+"
"zQ5NTfjamw8UI76NWLtVqRoxo7hzoP4T7utztYvbqyDrZp+qWpm/KvCSrUeH+sLsz9EDO+PHNANTTYj"
"TaAJWL8D84zb0eKlhIfQ97CaSnzVBoBwWgecS5zj2V5fitAE8sJhCGk2/TJmuOHVxcjL7zvm84ausgG"
"/rs0GAObOhQ8+QLz8Msp2D+Pa/qMGIz/8M8JtNGETSRfqhzMw3/jkuCeTAO4B/tpmBAJCMFIpXsc63r"
"VOJa8J1CvTUD+67OScFhI665evx3/FH9DKqsL4qM7nbDqSIQ9QqK3hm/rwWQBPY5192GoB4BaCuUpxN"
"cexNTq0L2r5P8DVyNrAcMGuA6jJT6AWrQnn37WMlT/kKg2UkCh0NHR01vKt+ojP1CrW1XXO0HvA1a0R"
"AFcC79ZzPMECzsgPrj4P+e4DDX+CSKAl7RfrMR94BSK7fmbTUT3Ar0QmGULGwK6Ojh+/eoV31XyWiDj"
"PtpwY7fJPVQC8BfxACOKWYuaQLccx2ncOZ/o6kam2sUu7h0dTvCFFRmf0Qm6Y7dxXONCvxzTrl9ZtGJ"
"anvnkr5pyl8NwCKyoZ7beOkrfzQ91H/fLPNTQKOCin8VdR41wgJbDyA88/1QEwGPiEOgoiu5Erf8r1n"
"rMY5K+mJmy8bzI/4W0WBlOp774W+eht4YWZhhtmvYf8cDVKSkSfXNSg7ojeOaiMVLT0ZJQmrPMAj1bC"
"7kPIrQVoq7cgF64BUzovKSkkq3uYrAaSp/uPI4Otkmp1O/fidwaOAOZhHZN3SgLgfuDBgBp3KrZIJkl"
"N4UbPBXzP54kQfIDms9T9Mm8HI2oFc1DZIZW/moCH30D+4aWGe84cstRVXMJYRmlefCd0rU1sM6fzRL"
"xw8R3AM41q05xkwacDn2L1BwqKPEL4YjyXem7mB14fPmIJX0Own0NB5o0dhszNQg+tzFWg/vDSiQ+6P"
"e3UBQzjIkbQk66ahxpOVPgAQxio96OXmc9OJxAo2zN4HauZdosHwDXA20RUBIXO/q50lvcztaoD7ZSv"
"DgYnkKDW8m1w/HeOR0SWZb++JLwGbzTnmns5oO2hAB9+R2AlkyS70ln0opsaSB8xmAGiI+21GrwoFB5"
"qGowhXnxcw2XiEZ6N9RUFPAXc2JIB4Lbdm8siLfcQ4Ysfc7XnOsZ5a/Ai6+EF7qZAL6E0cCKHuvz88A"
"JNw4B5n9UCII8e8lf8n2EiMdCRSFVOpfTiFQJBAm6VTpoukbqJiR8TZY+jIYUeSd9jcF3L049bMgBGA"
"EvsiJ5ygncG6eoh7q7sRKaswVtvS/o9/ucOXHPCBSj8EZE4F+r9lbWz/xauFQFB2tpFuHHp7pBgYxXV"
"nGwy0EV72vlLKNXrMJg3NMb9tUYE1hu2T+uKYeKIUWqY/wUeqcimo1THEPvREHzE58HrTr4SEen7L15"
"VO/s7k6UGM6BZppVJJNl0rCuMvKElaYAJwNxYwZoA/VbdVnkeQ81o/1nV6Zx8wJKg8NOTURcNR4SWlB"
"s6vLAo1Pi4tFHV+ImQAlzxxfBhS/IC/g3cHE/wncmSM/h1VRop6niEn0Sieo/FQd//l9egTE+EJtNRc"
"2oLz9TFjBD+ZlptJoA4QSQBvNqY929ItTizLuFfxAjfs8yoSCNF1RWW0NAQCAo4qCXgVoHzIrexWy/m"
"aFBl3j0hOkPovyHG32jORaKaLOCVSALVeKQ7Rum/hkYhxfH6Ec1pCRqgHzA5nvCvZaz3x4yvqcErnFW"
"hItA9TUPjOV5P/IgVLstZEGoU3/MNYZD5DouCxt+lZyPbpYX7/oYBL1rHs+gAlzASWWe/p8aY2YJt7J"
"YzeFJU4RG96Sb/zr1a5GzX0JTtzcRS/6olAOD78f1AF5OY4KmiWsRaCQPCr6BK/IoHU8qoDNn0UXzKl"
"65P+TLMoPzNhGjfH5D/XWmpiySS1Bn016rxnHQAHKRI3sujwefdwV7xPkvkWEaFCXtP7CODBPBcY4+z"
"oZaA5+NFq3T0uDo4FOJT+VOo8IO92CLzANuloi45L9pgeGtZ7VoymnOaxPhLJIFHmBX1/qesUu4Ip2g"
"jW+PN8HdbCgAgTkJnNR7xBesNZ+FLBAINwYv8J6EKjwgLFMW42S+uQpkR5wYaBrywqPYnFzAM1QRFxl"
"vZJQs4GMWLQooJPftaR+drNsYa4OsnY6wNCYAvgHtjgeBv4tmk6Li+InASvBu3WslaV9jMV+ERw9DWM"
"VOvRkQaf6YfteDL4DOp0+jXJMbfmhhueyQYXRis5CvRVOq/MQJBD2PFrsMPfRDgVT5xFw+mxArzSqRI"
"I1XhgCClrGtI25Yb0A3ZKSt67M8tqLX2hjMkZry/MUlHZyf7HD9zYYQ9/Vd8J2NMGA/WplmLA4C1jMP"
"fIx9MAUcpE1P5U6qJiSL02RVevNzFT6rDIgKiFkChdONF0Y0ZjUR44t3ae57DmcJsAt9fR6OcCkfg+U"
"JOw9DR+JgVsS7zwskab2OFR39rxwQEhG/3HqZETOa+1AqqRKTW60GuvIfJ1YrwXUKlwq8xfkT0rFm3G"
"XPL3tr3z2+CAzgkUr3CO3IHex0/r6Raq8KjAEykWs6aWNb/yy0dAACvAGdBtBleQZW4nftSN7FN1yNS"
"6Rdbvn/Y+h+6lAC8+jGyqgYZ6B1gGPDQa7UXGckw5cI4qeq/iCPyRu7mbRaJeJ7HS8yTblx8yCexwp5"
"+2546aZHIBiUFbGCwGMIGFfSKrAcaDCgNEbrdKy5hpHcyP/J48XMXD6QWUiycMoSc3ptwAfLBW6wzhT"
"In1D7L37mHbuSeTACom7hbefE5tX+NMnrGcaFawRpKKXca4zzghhYLgOD6Hf32UwLuUIE0sJDvJuKmM"
"1nmLgr0+gg/8v9Tk5CV1bWnjbzPbGIHnRo+4vcOi8w5vB+qTcsmZVDR1UXKp5Uc+ayKHKxDMlQ95HEX"
"8M8WuQTMJe52zi90xA9DPw58twYvuynQNa3W4g8FqF1rJ2JpglDhA5RSftKcfxcGK1gbVhiyrS/mUzl"
"0mZZJxv960rtyIPLGduyq54Q7cjKXrgYFwAgeZ26Mh7yXnoYf9YaAoQJEQPjBYI/t5gUEnKzhfzKHzS"
"t7oeZ2Y98vO7K/h5viyMJLJx37AUuUOEn5rjp6WDh3eBKHurnoEBiTX4GElOe70PPlLmyvBwgOt0gAf"
"AK8wi/FDaDmhrw/i1xm00esQ8kXEDxiFUL2Ddh0gRkf+i8gHu7EnkkZDDg9Ee3yVLo+lE3u9jwyN+Wx"
"9/I0CoK/dxjLG7wvKqk6KVogAmji0lQSvA539iuY0I4+d3TgmzpAcLBFAmA01llw07GS2QOa4Gfs51v"
"2iwXsls+QIbrSTaym1zYXYriyNUGE8EFAoog+W7BaQVcX3d7uRtdNeRR1dVEYg5ni1/xZSRq/lYSIsK"
"U6GbHz2kwFT+YwECiLc8k9LQ4AS4EPQNwMarptC1xvT843gMeplgB3YfIj9sov0LTpZH/lFlo7oCBU+"
"EKgBKhfH8SbJJz3cf0WELJ29aP9be2d1eoRSsXPuFcVU6Ias9XgTvbJiLHFTe8yFUaqFiNQ0FJtgPsB"
"RY9gHlhoOcvEoFrOEjdRpv5Cd93Axz5d4+IJsqJHD/KASiHANgeEUlCp6DpsJ4UaURGjIFVJ3E/m0Gd"
"GNt85gaCMCjGFP/Im800dXWkNpPAEgkQS1Lfkq9/zSJgDtNWLHg9ufiitkPSOiaeTTKIhZr+HjqKAYv"
"XTGN+5kgzxfxxVW+ijJZPAdo6I6jFKZp93iKLDaLNmcbEQLITa+kBbKwig9I4O+G/MgGGJVBjCPnNYw"
"EEfe5ZXoS2qQH+9FFUl4x68qC5mBOczlNPoRwJuzY9JfcPFOjoJuNjJPrmElfyPzwKuZlixaprGgbKB"
"5FZE6C6XgKMmBefuIHGXz/ngTKz0r5tbFAAA3gHtGpCRLuB0+/U4XfTVpMvz2MFWMrTNJJs3vbJTlJa"
"h3XGHJQEhKFSKzIALGOYOKstWsOko1rk6qdQ2WjrmtT6T9rIX3UQvutGJTNWJTC2NFBJJUAKBDz8VVI"
"rDlMj9HBJb2ckGtigPNYHQZTndkPTAoJCj5NMl4Nnel8XWGdlk+hUFm2vouaSSqldL8a6uJjcOz4WtP"
"OfRUmgW8G8QHzJAADzChVHfeYw8A+AfZGiv0V+MI1sD+N3vLH1805AgQ2YLgRTWul/7r9VLuKlfgWqm"
"EvpRwpWUcCc1/ALFFBQ/Zq/9eeT3Q1/1ucdJpxNKCfsZMJfB2uVsMDeBWMnSsIe4mk5iMO3Mn5OijaC"
"repAj2gIKzUsvRf/7v5A/vxS9x3pLA2ga+UohlLKqdYMbQfFiqvG0mosictERwC4U0LGelxAYlNIZHT"
"DRqKELKXTFSy7J+ElAEd7WsiNdSeMA5XQ+Xo1kz6eTTie0BCwgV4xjv3qZwdzMhmBk7zqgEz3FU+xSk"
"8gWP6VQ/RGrRChAd16A/s/PLOHfMQV95rPcISVPaAIlVVDgIiLCHP85UijhdLycQRIppAeXdwMvGyhm"
"KZmouKAXdOMw15KGP6SPX31ySqup4UU7sh0+VlHP8adgdUlrORpgHPvVJ8BoOwNGBE3Z03Czhz/QWXx"
"qFWKJj6nNzX7sJsQXr1hsnTYNo8SDlJJUzT40Mij8qzmAi1QOotjHUUpIohQFpNm3KyWLJLpSzun4aU"
"+P4MwMTRb14mYAOfSljH/hxU/HGI8kGUcy3uNo4phEAj+nmq8o5BAmAkEqCWThZxUGVTH7IAis+r+qF"
"qcBAjQfxBUhCJ8IooLBKoES8RZ7w5B/xyC0nhmoHpeiCtpBUhJi8mSUYTBL+cVtZuhEuRZBp5CRavYr"
"dE5Jju2oRZMynicZ6eCvp1PCJDpwoodNaiGawwCeZDvK0fUTWI2yf9dUdtwJO8ZzgSsi1NsboJLYpv0"
"nQvgPno22dyOqqBi1Efjr47D4BWsM0i8GmPG0pLIF7QO89svHsZ+zqZPO2BgRxA54G6SEQIYsG5Y6i3"
"XE/RtNKfwGAYBTD5Nr6KLNo0q+ZP//tN7wu3SE2o4amoc6+n2YPh2uGop+9W0BnqlBUbPDy+5Geeq+5"
"JLqcH5xSj3X+2PncCz137WpPbkGzwi6jjOEQZW6DvgJML0DHDyI0HOgSqCOjIO1WxFTf4Lr7AtRN90W"
"nMOZUVngnkaK4fqAc0iI0AKCdNo3+L0q2E3shpcjTzkAzOMbBTkqGM0YiOjTGfHwFtTi3jBnPaJfGVp"
"7N77Jd1rzzdDEwGCMNSzWGzNiduLUz8Ho6tgIVSRVIaDSHTeKup5SALBAsLE2GrgC9ccdlqAPZSB67E"
"XMWYt5ur3lcUMvhKlUXiD6F7bqF1HdaPs4brIhYonJaoQOEV5Sgi5gF6yMuHA6+5QDQPDJIh6tfwGs2"
"YGcPhqu3w6fPoo41AuhFJmOFziA0WjtrCXQJWLvwN0oRYQq5C+N9ChLt+8pC4C1ayE3t/b/P95sPfz0"
"T+BWgbjvPUR5KZLo42Ks0Gg57fFQ0iiDU4BOedh7+2PGB04k0lITtDUUGon4IxzZLqcsAAD2xyh+XeN"
"DLP8MuXYtAEVhnnqot7++Eas7wqOCimWUNnjLjEi7xkVCRFQw7ZQGQCxav8FeC28HYEuYpx66ibKaZF"
"z17B51rCGw0ohedKV0Ib+Bc/IOBw1LgUGNXa4sGjoY1+IAEGIkQWgihAjODs1eDJJZFzeF6vhIx0MZq"
"VE6YSGJeBvIGHRhssIOBen4cJFIDUaEBiht3QB4KfjXUlsEwlacHpKosVVzCnoDLwV7KMHauCECfCm8"
"SPkJc0YDlnGASjIAQXYwLhCph3a0bgDU0pwwdahIJBMdDRNFEkspaDBlqQFrHXoXdgFSUZhk8zrF6Mf"
"ZD1YDNnOIr+kKKFLxkYKLcnwOu5Gr2wBg0b+i1PFhBN0QgORbulLaQD1ziznM7qDraYbxIweNZHwcoS"
"MfUnbMRqEBrGIbi+kEKNz46GTnJRwOb5Nr0xdtAKh1/cJBUI2BH0V7u5Z8Dj70E8ycEVQx116HXUhyQ"
"7Zt/HiQQC4GBpJtdGQ1+49B81TxNkWsIc/WYT664wI0SvDhj2oV9kJTM725nRmUjXWapgpzC/uisxMT"
"PwbZ7OaH9Dgu5awo5jUSKSMZ8NMHHZBstwHREUmGHXoyMdll8+cHFNOZrLjTaC+FfEA6pp0QkoGfLFx"
"IwIdkDypiwgmgE1DYlAxvbsfGVWIdFnVWGHtr8JGDzlEklbSngqP0JbHeO3cGUEARr5OMh2QAeqAF/y"
"ulxj7ixyTN5omGhgs/lRhsQqMPB0iinQMHJYso5nOysGoC/HRB0Q6XvYUt7YBzpPDvp5G7gLZEDRAAZ"
"U0UwzrjRaFxyF6VsyjiCjTS6Ri2/05YGOko24EVlFFK96Bm6YYXt531I4B9gMcWVx4ayr63AA7hpxwd"
"8HIhRxlMeyRuNLx8w2E+IR1JKtauv4+sEDXvR7Eb6SD8X2CdBUAbAJzpOmqLjWupD4rDVFMa3GARJLC"
"fXAyS8JBCd2oopgwfJeiU0t6e/9Z33fjJBfQQ004g2YZJID0uG5O0kM814ACSimCF8mEySeEwEiuDAF"
"z46IwgwW4CJIBKajgQteYLrJPS/9ZcGN2MT+HlQ6wzBmopGS9dSKAUH4WIei5hVgQuE500jChNcRBJO"
"aEF6X76YKAIL1IvwUsxRths1jDJQpJur/UBQB3G5Kij/yBsO6eouTDZaMYAqHJ4x025zfAUFEe/Nz35"
"AAABiUlEQVTwUoHAjJppVk5vMpJ0dNwkhC0TGlCJj8OANyIeoDA4iEnnkJZe1sEGbtojqcCHHz8JGCT"
"jQqIH+13VYHIAiT8uX4cAi9s0QHxKBKqDccGIM4VIwkMSbhLwY+BGpxrwIzAwcKHZwgv9XQ1evAiq0C"
"hH2QEZFZMvafjojIGsg0cC6+yXIkyqo1LCnWgHcc5Fbn0AOA34zjEqeEM9x69C/lVYuwuh28surGNr6"
"pOfH6kffWQCabijMv1N/FQgKMVPTdQOX11jfgbrRLBWTgMdATia+pVSncyyMB8JmCQiSUQFtdOJXfMn"
"bRrAmcqD1vWpTQLoBexqykE0t3N0noCoLdpTlRQnsSFkS9AABlbCtqL1kKDVJ4TU0sWtzAISWAdptmk"
"Am9phNX9QTcwD1cg8K8HqBLYO+FEbAMIpF3gc+AGNv1G1GPgSqzYgkKeTBmTar2ygg22TGHZgqgBYb/"
"+mHGvzKrRS0R/yqsZq++6BRshpPMUDQcfzHFrIsqZHhWqasAtHc6b/D3cbSAuGcmWdAAAAAElFTkSuQmCC\" />";
const std::string HTTPConnection::itoopieFavicon =
"data:image/vnd.microsoft.icon;base64,"
"";
const char HTTP_COMMAND_TUNNELS[] = "tunnels";
const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels";
const char HTTP_COMMAND_TRANSPORTS[] = "transports";
const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels";
const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels";
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations";
const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination";
const char HTTP_PARAM_BASE32_ADDRESS[] = "b32";
const char HTTP_COMMAND_SAM_SESSIONS[] = "sam_sessions";
const char HTTP_COMMAND_SAM_SESSION[] = "sam_session";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
namespace misc_strings
{
const char name_value_separator[] = { ':', ' ' };
const char crlf[] = { '\r', '\n' };
} // namespace misc_strings
std::vector<boost::asio::const_buffer> HTTPConnection::reply::to_buffers(int status)
{
std::vector<boost::asio::const_buffer> buffers;
if (headers.size () > 0)
{
status_string = "HTTP/1.1 ";
status_string += std::to_string (status);
status_string += " ";
switch (status)
{
case 105: status_string += "Name Not Resolved"; break;
case 200: status_string += "OK"; break;
case 400: status_string += "Bad Request"; break;
case 404: status_string += "Not Found"; break;
case 408: status_string += "Request Timeout"; break;
case 500: status_string += "Internal Server Error"; break;
case 502: status_string += "Bad Gateway"; break;
case 503: status_string += "Not Implemented"; break;
case 504: status_string += "Gateway Timeout"; break;
default: status_string += "WTF";
}
buffers.push_back(boost::asio::buffer(status_string, status_string.size()));
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
for (std::size_t i = 0; i < headers.size(); ++i)
{
header& h = headers[i];
buffers.push_back(boost::asio::buffer(h.name));
buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
buffers.push_back(boost::asio::buffer(h.value));
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
}
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
}
buffers.push_back(boost::asio::buffer(content));
return buffers;
}
void HTTPConnection::Terminate ()
{
if (!m_Stream) return;
m_Stream->Close ();
m_Stream = nullptr;
m_Socket->close ();
}
void HTTPConnection::Receive ()
{
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE),
std::bind(&HTTPConnection::HandleReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (!ecode)
{
if (!m_Stream) // new request
{
m_Buffer[bytes_transferred] = 0;
m_BufferLen = bytes_transferred;
RunRequest();
}
else // follow-on
m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred);
Receive ();
}
else if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
void HTTPConnection::RunRequest ()
{
auto address = ExtractAddress ();
if (address.length () > 1 && address[1] != '?') // not just '/' or '/?'
{
std::string uri ("/"), b32;
size_t pos = address.find ('/', 1);
if (pos == std::string::npos)
b32 = address.substr (1); // excluding leading '/' to end of line
else
{
b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/'
uri = address.substr (pos); // rest of line
}
HandleDestinationRequest (b32, uri);
}
else
HandleRequest (address);
}
std::string HTTPConnection::ExtractAddress ()
{
char * get = strstr (m_Buffer, "GET");
if (get)
{
char * http = strstr (get, "HTTP");
if (http)
return std::string (get + 4, http - get - 5);
}
return "";
}
void HTTPConnection::ExtractParams (const std::string& str, std::map<std::string, std::string>& params)
{
if (str[0] != '&') return;
size_t pos = 1, end;
do
{
end = str.find ('&', pos);
std::string param = str.substr (pos, end - pos);
LogPrint (eLogDebug, "HTTPServer: extracted parameters: ", param);
size_t e = param.find ('=');
if (e != std::string::npos)
params[param.substr(0, e)] = param.substr(e+1);
pos = end + 1;
}
while (end != std::string::npos);
}
void HTTPConnection::HandleWriteReply (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
boost::system::error_code ignored_ec;
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
Terminate ();
}
}
void HTTPConnection::HandleWrite (const boost::system::error_code& ecode)
{
if (ecode || (m_Stream && !m_Stream->IsOpen ()))
{
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else // data keeps coming
AsyncStreamReceive ();
}
void HTTPConnection::HandleRequest (const std::string& address)
{
std::stringstream s;
// Html5 head start
s << "<!DOCTYPE html>\n<html lang=\"en\">"; // TODO: Add support for locale.
s << "<head><meta charset=\"utf-8\">"; // TODO: Find something to parse html/template system. This is horrible.
// s << "<link rel='shortcut icon' href='";
// s << itoopieFavicon;
// s << "' />";
s << "<title>Purple I2P " << VERSION " Webconsole</title>";
s << "<style>";
s << "body {font: 100%/1.5em sans-serif; margin: 0; padding: 0; background: #FAFAFA;}";
s << "a {text-decoration: none;}";
s << "body, a {color: #103456}";
s << ".header {font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84;}";
s << ".wrapper {margin: 0 auto; padding: 1em; max-width: 48em;}";
s << ".left {width: 28em; float:left;}";
s << "</style>";
s << "</head>";
// Head end
if (address.length () > 1)
HandleCommand (address.substr (2), s);
else
FillContent (s);
s << "</html>";
SendReply (s.str ());
}
void HTTPConnection::FillContent (std::stringstream& s)
{
s << "<div class=header><b>i2pd </b>webconsole</div>";
s << "<div class=wrapper>";
s << "<div class=left>";
s << "<b>Uptime:</b> " << boost::posix_time::to_simple_string (
boost::posix_time::time_duration (boost::posix_time::seconds (
i2p::context.GetUptime ()))) << "<br>";
s << "<b>Status:</b> ";
switch (i2p::context.GetStatus ())
{
case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Testing"; break;
case eRouterStatusFirewalled: s << "Firewalled"; break;
default: s << "Unknown";
}
s << "<br>";
s << "<b>Tunnel creation success rate:</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>";
s << "<b>Received:</b> " << i2p::transport::transports.GetTotalReceivedBytes ()/1000 << "K";
s << " (" << i2p::transport::transports.GetInBandwidth () <<" Bps)<br>";
s << "<b>Sent:</b> " << i2p::transport::transports.GetTotalSentBytes ()/1000 << "K";
s << " (" << i2p::transport::transports.GetOutBandwidth () <<" Bps)<br>";
s << "<b>Data path:</b> " << i2p::util::filesystem::GetDataDir().string() << "<br><br>";
s << "<b>Our external address:</b>" << "<br>" ;
for (auto& address : i2p::context.GetRouterInfo().GetAddresses())
{
switch (address.transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
if (address.host.is_v6 ())
s << "NTCP6&nbsp;&nbsp;";
else
s << "NTCP&nbsp;&nbsp;";
break;
case i2p::data::RouterInfo::eTransportSSU:
if (address.host.is_v6 ())
s << "SSU6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
else
s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
break;
default:
s << "Unknown&nbsp;&nbsp;";
}
s << address.host.to_string() << ":" << address.port << "<br>";
}
s << "<br><b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>";
s << "</div><div>";
s << "[ <a href=/?" << HTTP_COMMAND_LOCAL_DESTINATIONS << ">Local destinations</a> ]<br>";
s << "[ <a href=/?" << HTTP_COMMAND_TUNNELS << ">Tunnels</a> ]<br>";
s << "[ <a href=/?" << HTTP_COMMAND_TRANSIT_TUNNELS << ">Transit tunnels</a> ]<br>";
s << "[ <a href=/?" << HTTP_COMMAND_TRANSPORTS << ">Transports</a> ]<br>";
if (i2p::client::context.GetSAMBridge ())
s << "[ <a href=/?" << HTTP_COMMAND_SAM_SESSIONS << ">SAM sessions</a> ]<br><br>";
if (i2p::context.AcceptsTunnels ())
s << "[ <a href=/?" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a> ]<br><br>";
else
s << "[ <a href=/?" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << ">Start accepting tunnels</a> ]<br><br>";
s << "[ <a href=/?" << HTTP_COMMAND_RUN_PEER_TEST << ">Run peer test</a> ]<br><br>";
s << "</div></div></body>";
}
void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s)
{
size_t paramsPos = command.find('&');
std::string cmd = command.substr (0, paramsPos);
if (cmd == HTTP_COMMAND_TRANSPORTS)
ShowTransports (s);
else if (cmd == HTTP_COMMAND_TUNNELS)
ShowTunnels (s);
else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS)
ShowTransitTunnels (s);
else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS)
StartAcceptingTunnels (s);
else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS)
StopAcceptingTunnels (s);
else if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
RunPeerTest (s);
else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS)
ShowLocalDestinations (s);
else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION)
{
std::map<std::string, std::string> params;
ExtractParams (command.substr (paramsPos), params);
auto b32 = params[HTTP_PARAM_BASE32_ADDRESS];
ShowLocalDestination (b32, s);
}
else if (cmd == HTTP_COMMAND_SAM_SESSIONS)
ShowSAMSessions (s);
else if (cmd == HTTP_COMMAND_SAM_SESSION)
{
std::map<std::string, std::string> params;
ExtractParams (command.substr (paramsPos), params);
auto id = params[HTTP_PARAM_SAM_SESSION_ID];
ShowSAMSession (id, s);
}
}
void HTTPConnection::ShowTransports (std::stringstream& s)
{
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
if (ntcpServer)
{
s << "NTCP<br>";
for (auto it: ntcpServer->GetNTCPSessions ())
{
if (it.second && it.second->IsEstablished ())
{
// incoming connection doesn't have remote RI
if (it.second->IsOutgoing ()) s << "-->";
s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!it.second->IsOutgoing ()) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>";
}
s << std::endl;
}
}
auto ssuServer = i2p::transport::transports.GetSSUServer ();
if (ssuServer)
{
s << "<br>SSU<br>";
for (auto it: ssuServer->GetSessions ())
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << "-->";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>" << std::endl;
}
s << "<br>SSU6<br>";
for (auto it: ssuServer->GetSessionsV6 ())
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << "-->";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>" << std::endl;
}
}
}
void HTTPConnection::ShowTunnels (std::stringstream& s)
{
s << "Queue size:" << i2p::tunnel::tunnels.GetQueueSize () << "<br>";
for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ())
{
it->Print (s);
auto state = it->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed";
else if (state == i2p::tunnel::eTunnelStateExpiring)
s << " " << "Exp";
s << " " << (int)it->GetNumSentBytes () << "<br>";
s << std::endl;
}
for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ())
{
it.second->Print (s);
auto state = it.second->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed";
else if (state == i2p::tunnel::eTunnelStateExpiring)
s << " " << "Exp";
s << " " << (int)it.second->GetNumReceivedBytes () << "<br>";
s << std::endl;
}
}
void HTTPConnection::ShowTransitTunnels (std::stringstream& s)
{
for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ())
{
if (dynamic_cast<i2p::tunnel::TransitTunnelGateway *>(it.second))
s << it.second->GetTunnelID () << "-->";
else if (dynamic_cast<i2p::tunnel::TransitTunnelEndpoint *>(it.second))
s << "-->" << it.second->GetTunnelID ();
else
s << "-->" << it.second->GetTunnelID () << "-->";
s << " " << it.second->GetNumTransmittedBytes () << "<br>";
}
}
void HTTPConnection::ShowLocalDestinations (std::stringstream& s)
{
for (auto& it: i2p::client::context.GetDestinations ())
{
auto ident = it.second->GetIdentHash ();;
s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION;
s << "&" << HTTP_PARAM_BASE32_ADDRESS << "=" << ident.ToBase32 () << ">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>" << std::endl;
}
}
void HTTPConnection::ShowLocalDestination (const std::string& b32, std::stringstream& s)
{
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
if (dest)
{
s << "<b>Base64:</b><br>" << dest->GetIdentity ()->ToBase64 () << "<br><br>";
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>";
auto pool = dest->GetTunnelPool ();
if (pool)
{
s << "<b>Tunnels:</b><br>";
for (auto it: pool->GetOutboundTunnels ())
{
it->Print (s);
auto state = it->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed";
else if (state == i2p::tunnel::eTunnelStateExpiring)
s << " " << "Exp";
s << "<br>" << std::endl;
}
for (auto it: pool->GetInboundTunnels ())
{
it->Print (s);
auto state = it->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed";
else if (state == i2p::tunnel::eTunnelStateExpiring)
s << " " << "Exp";
s << "<br>" << std::endl;
}
}
s << "<br><b>Streams:</b><br>";
for (auto it: dest->GetStreamingDestination ()->GetStreams ())
{
s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
s << "[buf:" << it.second->GetSendBufferSize () << "]";
s << "[RTT:" << it.second->GetRTT () << "]";
s << "[Window:" << it.second->GetWindowSize () << "]";
s << "[Status:" << (int)it.second->GetStatus () << "]";
s << "<br>"<< std::endl;
}
}
}
void HTTPConnection::ShowSAMSessions (std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (sam)
{
for (auto& it: sam->GetSessions ())
{
s << "<a href=/?" << HTTP_COMMAND_SAM_SESSION;
s << "&" << HTTP_PARAM_SAM_SESSION_ID << "=" << it.first << ">";
s << it.first << "</a><br>" << std::endl;
}
}
}
void HTTPConnection::ShowSAMSession (const std::string& id, std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (sam)
{
auto session = sam->FindSession (id);
if (session)
{
auto& ident = session->localDestination->GetIdentHash();
s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION;
s << "&" << HTTP_PARAM_BASE32_ADDRESS << "=" << ident.ToBase32 () << ">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>" << std::endl;
s << "<b>Streams:</b><br>";
for (auto it: session->sockets)
{
switch (it->GetSocketType ())
{
case i2p::client::eSAMSocketTypeSession:
s << "session";
break;
case i2p::client::eSAMSocketTypeStream:
s << "stream";
break;
case i2p::client::eSAMSocketTypeAcceptor:
s << "acceptor";
break;
default:
s << "unknown";
}
s << " [" << it->GetSocket ().remote_endpoint() << "]";
s << "<br>" << std::endl;
}
}
}
}
void HTTPConnection::StartAcceptingTunnels (std::stringstream& s)
{
i2p::context.SetAcceptsTunnels (true);
s << "Accepting tunnels started" << std::endl;
}
void HTTPConnection::StopAcceptingTunnels (std::stringstream& s)
{
i2p::context.SetAcceptsTunnels (false);
s << "Accepting tunnels stopped" << std::endl;
}
void HTTPConnection::RunPeerTest (std::stringstream& s)
{
i2p::transport::transports.PeerTest ();
s << "Peer test" << std::endl;
}
void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri)
{
std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n\r\n";
LogPrint(eLogDebug, "HTTPServer: client request: ", request);
SendToAddress (address, 80, request.c_str (), request.size ());
}
void HTTPConnection::SendToAddress (const std::string& address, int port, const char * buf, size_t len)
{
i2p::data::IdentHash destination;
if (!i2p::client::context.GetAddressBook ().GetIdentHash (address, destination))
{
LogPrint (eLogWarning, "HTTPServer: Unknown address ", address);
SendReply ("<html>" + itoopieImage + "<br>Unknown address " + address + "</html>", 404);
return;
}
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination);
if (leaseSet && leaseSet->HasNonExpiredLeases ())
SendToDestination (leaseSet, port, buf, len);
else
{
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (destination);
m_Timer.expires_from_now (boost::posix_time::seconds(HTTP_DESTINATION_REQUEST_TIMEOUT));
m_Timer.async_wait (std::bind (&HTTPConnection::HandleDestinationRequestTimeout,
shared_from_this (), std::placeholders::_1, destination, port, m_Buffer, m_BufferLen));
}
}
void HTTPConnection::HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
i2p::data::IdentHash destination, int port, const char * buf, size_t len)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination);
if (leaseSet && leaseSet->HasNonExpiredLeases ())
SendToDestination (leaseSet, port, buf, len);
else
// still no LeaseSet
SendReply (leaseSet ? "<html>" + itoopieImage + "<br>Leases expired</html>" : "<html>" + itoopieImage + "LeaseSet not found</html>", 504);
}
}
void HTTPConnection::SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len)
{
if (!m_Stream)
m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (remote, port);
if (m_Stream)
{
m_Stream->Send ((uint8_t *)buf, len);
AsyncStreamReceive ();
}
}
void HTTPConnection::AsyncStreamReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, 8192),
std::bind (&HTTPConnection::HandleStreamReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
45); // 45 seconds timeout
}
void HTTPConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (!ecode)
{
boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
std::bind (&HTTPConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
}
else
{
if (ecode == boost::asio::error::timed_out)
SendReply ("<html>" + itoopieImage + "<br>Not responding</html>", 504);
else if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
}
void HTTPConnection::SendReply (const std::string& content, int status)
{
m_Reply.content = content;
m_Reply.headers.resize(3);
// we need the date header to be complaint with http 1.1
std::time_t time_now = std::time(nullptr);
char time_buff[128];
if (std::strftime(time_buff, sizeof(time_buff), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&time_now)))
{
m_Reply.headers[0].name = "Date";
m_Reply.headers[0].value = std::string(time_buff);
m_Reply.headers[1].name = "Content-Length";
m_Reply.headers[1].value = boost::lexical_cast<std::string>(m_Reply.content.size());
m_Reply.headers[2].name = "Content-Type";
m_Reply.headers[2].value = "text/html";
}
boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status),
std::bind (&HTTPConnection::HandleWriteReply, shared_from_this (), std::placeholders::_1));
}
HTTPServer::HTTPServer (const std::string& address, int port):
m_Thread (nullptr), m_Work (m_Service),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port))
{
}
HTTPServer::~HTTPServer ()
{
Stop ();
}
void HTTPServer::Start ()
{
m_Thread = std::unique_ptr<std::thread>(new std::thread (std::bind (&HTTPServer::Run, this)));
m_Acceptor.listen ();
Accept ();
}
void HTTPServer::Stop ()
{
m_Acceptor.close();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
m_Thread = nullptr;
}
}
void HTTPServer::Run ()
{
m_Service.run ();
}
void HTTPServer::Accept ()
{
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
m_Acceptor.async_accept (*newSocket, boost::bind (&HTTPServer::HandleAccept, this,
boost::asio::placeholders::error, newSocket));
}
void HTTPServer::HandleAccept(const boost::system::error_code& ecode,
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
{
if (!ecode)
{
CreateConnection(newSocket);
Accept ();
}
}
void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
{
auto conn = std::make_shared<HTTPConnection> (newSocket);
conn->Receive ();
}
}
}

View File

@@ -1,137 +0,0 @@
#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
{
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) {};
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 HandleRequest (const std::string& address);
void HandleCommand (const std::string& command, std::stringstream& s);
void ShowTransports (std::stringstream& s);
void ShowTunnels (std::stringstream& s);
void ShowTransitTunnels (std::stringstream& s);
void ShowLocalDestinations (std::stringstream& s);
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
void ShowSAMSessions (std::stringstream& s);
void ShowSAMSession (const std::string& id, std::stringstream& s);
void StartAcceptingTunnels (std::stringstream& s);
void StopAcceptingTunnels (std::stringstream& s);
void 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:
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];
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;
};
class HTTPServer
{
public:
HTTPServer (const std::string& address, int port);
virtual ~HTTPServer ();
void Start ();
void Stop ();
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode,
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
private:
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor m_Acceptor;
protected:
virtual void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
};
}
}
#endif

View File

@@ -1,605 +0,0 @@
#include <string.h>
#include <atomic>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Base.h"
#include "Log.h"
#include "Crypto.h"
#include "I2PEndian.h"
#include "Timestamp.h"
#include "RouterContext.h"
#include "NetDb.h"
#include "Tunnel.h"
#include "Transports.h"
#include "Garlic.h"
#include "I2NPProtocol.h"
using namespace i2p::transport;
namespace i2p
{
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
{
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> >();
}
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ()
{
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
}
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
{
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
}
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID)
{
SetTypeID (msgType);
if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4);
SetMsgID (replyMsgID);
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
UpdateSize ();
UpdateChks ();
}
void I2NPMessage::RenewI2NPMessageHeader ()
{
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
SetMsgID (msgID);
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000);
}
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
auto msg = NewI2NPMessage (len);
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length ", msg->maxLen);
msg->FillI2NPMessageHeader (msgType, replyMsgID);
return msg;
}
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
auto msg = NewI2NPMessage ();
if (msg->offset + len < msg->maxLen)
{
memcpy (msg->GetBuffer (), buf, len);
msg->len = msg->offset + len;
msg->from = from;
}
else
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length");
return msg;
}
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
{
auto m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
if (msgID)
{
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ());
}
else // for SSU establishment
{
RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
}
m->len += DELIVERY_STATUS_SIZE;
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
return m;
}
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{
auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, key, 32); // key
buf += 32;
memcpy (buf, from, 32); // from
buf += 32;
uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP;
if (replyTunnelID)
{
*buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag
htobe32buf (buf+1, replyTunnelID);
buf += 5;
}
else
{
*buf = flag; // flag
buf++;
}
if (excludedPeers)
{
int cnt = excludedPeers->size ();
htobe16buf (buf, cnt);
buf += 2;
for (auto& it: *excludedPeers)
{
memcpy (buf, it, 32);
buf += 32;
}
}
else
{
// nothing to exclude
htobuf16 (buf, 0);
buf += 2;
}
m->len += (buf - m->GetPayload ());
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
return m;
}
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
{
int cnt = excludedFloodfills.size ();
auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, dest, 32); // key
buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 5;
// excluded
htobe16buf (buf, cnt);
buf += 2;
if (cnt > 0)
{
for (auto& it: excludedFloodfills)
{
memcpy (buf, it, 32);
buf += 32;
}
}
// encryption
memcpy (buf, replyKey, 32);
buf[32] = 1; // 1 tag
memcpy (buf + 33, replyTag, 32);
buf += 65;
m->len += (buf - m->GetPayload ());
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
std::vector<i2p::data::IdentHash> routers)
{
auto m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
size_t len = 0;
memcpy (buf, ident, 32);
len += 32;
buf[len] = routers.size ();
len++;
for (auto it: routers)
{
memcpy (buf + len, it, 32);
len += 32;
}
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
len += 32;
m->len += len;
m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
{
if (!router) // we send own RouterInfo
router = context.GetSharedRouterInfo ();
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
memset (buf, 0, 4); // zero tunnelID means direct reply
buf += 4;
memcpy (buf, router->GetIdentHash (), 32);
buf += 32;
}
uint8_t * sizePtr = buf;
buf += 2;
m->len += (buf - payload); // payload size
i2p::data::GzipDeflator deflator;
size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
if (size)
{
htobe16buf (sizePtr, size); // size
m->len += size;
}
else
m = nullptr;
if (m)
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
auto leases = leaseSet->GetNonExpiredLeases ();
if (leases.size () > 0)
{
htobe32buf (payload + size, leases[0].tunnelID);
size += 4; // reply tunnelID
memcpy (payload + size, leases[0].tunnelGateway, 32);
size += 32; // reply tunnel gateway
}
else
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
}
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen ();
m->len += size;
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg)
{
if (!msg || msg->GetTypeID () != eI2NPDatabaseStore) return false;
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
}
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
{
for (int i = 0; i < num; i++)
{
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
// replace record to reply
if (i2p::context.AcceptsTunnels () &&
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS &&
!i2p::transport::transports.IsBandwidthExceeded ())
{
i2p::tunnel::TransitTunnel * transitTunnel =
i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
}
else
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
//TODO: fill filler
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
// encrypt reply
i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++)
{
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
return true;
}
}
return false;
}
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
if (tunnel)
{
// endpoint of inbound tunnel
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
}
else
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
}
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
{
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPTunnelBuildReply, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
if (tunnel)
{
// reply for outbound tunnel
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
}
else
{
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
}
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{
auto msg = NewI2NPShortMessage ();
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
}
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
{
auto msg = NewI2NPShortMessage ();
htobe32buf (msg->GetPayload (), tunnelID);
msg->len += 4; // tunnelID
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
}
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
{
auto msg = NewI2NPShortMessage ();
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
return msg;
}
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
{
auto msg = NewI2NPMessage (len);
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->len += TUNNEL_GATEWAY_HEADER_SIZE;
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
return msg;
}
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
{
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
{
// message is capable to be used without copying
uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE;
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
int len = msg->GetLength ();
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
return msg;
}
else
return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
}
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
auto msg = NewI2NPMessage (len);
size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->offset += gatewayMsgOffset;
msg->len += gatewayMsgOffset;
if (msg->Concat (buf, len) < len)
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message
len = msg->GetLength ();
msg->offset -= gatewayMsgOffset;
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message
return msg;
}
size_t GetI2NPMessageLength (const uint8_t * msg)
{
return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
}
void HandleI2NPMessage (uint8_t * msg, size_t len)
{
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
uint8_t * buf = msg + I2NP_HEADER_SIZE;
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
switch (typeID)
{
case eI2NPVariableTunnelBuild:
HandleVariableTunnelBuildMsg (msgID, buf, size);
break;
case eI2NPVariableTunnelBuildReply:
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break;
case eI2NPTunnelBuild:
HandleTunnelBuildMsg (buf, size);
break;
case eI2NPTunnelBuildReply:
// TODO:
break;
default:
LogPrint (eLogWarning, "I2NP: Unexpected message ", (int)typeID);
}
}
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{
uint8_t typeID = msg->GetTypeID ();
LogPrint (eLogDebug, "I2NP: Handling message with type ", (int)typeID);
switch (typeID)
{
case eI2NPTunnelData:
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
case eI2NPTunnelGateway:
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
case eI2NPGarlic:
{
if (msg->from)
{
if (msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
else
LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore");
}
else
i2p::context.ProcessGarlicMessage (msg);
break;
}
case eI2NPDatabaseStore:
case eI2NPDatabaseSearchReply:
case eI2NPDatabaseLookup:
// forward to netDb
i2p::data::netdb.PostI2NPMsg (msg);
break;
case eI2NPDeliveryStatus:
{
if (msg->from && msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg);
else
i2p::context.ProcessDeliveryStatusMessage (msg);
break;
}
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply:
// forward to tunnel thread
i2p::tunnel::tunnels.PostTunnelData (msg);
break;
default:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
}
}
}
I2NPMessagesHandler::~I2NPMessagesHandler ()
{
Flush ();
}
void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{
switch (msg->GetTypeID ())
{
case eI2NPTunnelData:
m_TunnelMsgs.push_back (msg);
break;
case eI2NPTunnelGateway:
m_TunnelGatewayMsgs.push_back (msg);
break;
default:
HandleI2NPMessage (msg);
}
}
}
void I2NPMessagesHandler::Flush ()
{
if (!m_TunnelMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
m_TunnelMsgs.clear ();
}
if (!m_TunnelGatewayMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
m_TunnelGatewayMsgs.clear ();
}
}
}

View File

@@ -1,259 +0,0 @@
#ifndef I2NP_PROTOCOL_H__
#define I2NP_PROTOCOL_H__
#include <inttypes.h>
#include <string.h>
#include <set>
#include <memory>
#include <openssl/sha.h>
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "LeaseSet.h"
namespace i2p
{
// I2NP header
const size_t I2NP_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4;
const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8;
const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2;
const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1;
// I2NP short header
const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4;
// Tunnel Gateway header
const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0;
const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4;
const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2;
// DeliveryStatus
const size_t DELIVERY_STATUS_MSGID_OFFSET = 0;
const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4;
const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8;
// DatabaseStore
const size_t DATABASE_STORE_KEY_OFFSET = 0;
const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32;
const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1;
const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4;
// TunnelBuild
const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
//BuildRequestRecordClearText
const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222;
// BuildRequestRecordEncrypted
const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16;
// BuildResponseRecord
const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0;
const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32;
const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495;
const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE;
enum I2NPMessageType
{
eI2NPDatabaseStore = 1,
eI2NPDatabaseLookup = 2,
eI2NPDatabaseSearchReply = 3,
eI2NPDeliveryStatus = 10,
eI2NPGarlic = 11,
eI2NPTunnelData = 18,
eI2NPTunnelGateway = 19,
eI2NPData = 20,
eI2NPTunnelBuild = 21,
eI2NPTunnelBuildReply = 22,
eI2NPVariableTunnelBuild = 23,
eI2NPVariableTunnelBuildReply = 24
};
const int NUM_TUNNEL_BUILD_RECORDS = 8;
// DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
const unsigned int MAX_NUM_TRANSIT_TUNNELS = 2500;
namespace tunnel
{
class InboundTunnel;
class TunnelPool;
}
const size_t I2NP_MAX_MESSAGE_SIZE = 32768;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
struct I2NPMessage
{
uint8_t * buf;
size_t len, offset, maxLen;
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
// header accessors
uint8_t * GetHeader () { return GetBuffer (); };
const uint8_t * GetHeader () const { return GetBuffer (); };
void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; };
void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); };
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); };
uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); };
void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); };
uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); };
void UpdateSize () { SetSize (GetPayloadLength ()); };
void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; };
void UpdateChks ()
{
uint8_t hash[32];
SHA256(GetPayload (), GetPayloadLength (), hash);
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
}
// payload
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; };
const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; };
uint8_t * GetBuffer () { return buf + offset; };
const uint8_t * GetBuffer () const { return buf + offset; };
size_t GetLength () const { return len - offset; };
size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; };
void Align (size_t alignment)
{
if (len + alignment > maxLen) return;
size_t rem = ((size_t)GetBuffer ()) % alignment;
if (rem)
{
offset += (alignment - rem);
len += (alignment - rem);
}
}
size_t Concat (const uint8_t * buf1, size_t len1)
{
// make sure with don't write beyond maxLen
if (len + len1 > maxLen) len1 = maxLen - len;
memcpy (buf + len, buf1, len1);
len += len1;
return len1;
}
I2NPMessage& operator=(const I2NPMessage& other)
{
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
len = offset + other.GetLength ();
from = other.from;
return *this;
}
// for SSU only
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
{
const uint8_t * ssu = GetSSUHeader ();
GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID (msgID);
SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL);
SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0);
}
uint32_t ToSSU () // return msgID
{
uint8_t header[I2NP_HEADER_SIZE];
memcpy (header, GetHeader (), I2NP_HEADER_SIZE);
uint8_t * ssu = GetSSUHeader ();
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
}
void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0);
void RenewI2NPMessageHeader ();
};
template<int sz>
struct I2NPMessageBuffer: public I2NPMessage
{
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
};
std::shared_ptr<I2NPMessage> NewI2NPMessage ();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ();
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
size_t GetI2NPMessageLength (const uint8_t * msg);
void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler
{
public:
~I2NPMessagesHandler ();
void PutNextMessage (std::shared_ptr<I2NPMessage> msg);
void Flush ();
private:
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
};
}
#endif

View File

@@ -1,577 +0,0 @@
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
#include <stdio.h>
#include <sstream>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp>
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp>
#endif
#include "Log.h"
#include "NetDb.h"
#include "RouterContext.h"
#include "Daemon.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "Transports.h"
#include "version.h"
#include "I2PControl.h"
namespace i2p
{
namespace client
{
I2PControlService::I2PControlService (const std::string& address, int port):
m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_SSLContext (m_Service, boost::asio::ssl::context::sslv23),
m_ShutdownTimer (m_Service)
{
LoadConfig ();
// certificate
auto path = GetPath ();
if (!boost::filesystem::exists (path))
{
if (!boost::filesystem::create_directory (path))
LogPrint (eLogError, "Failed to create i2pcontrol directory");
}
if (!boost::filesystem::exists (path / I2P_CONTROL_KEY_FILE) ||
!boost::filesystem::exists (path / I2P_CONTROL_CERT_FILE))
{
// create new certificate
CreateCertificate ();
LogPrint (eLogInfo, "I2PControl certificates created");
}
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
m_SSLContext.use_certificate_file ((path / I2P_CONTROL_CERT_FILE).string (), boost::asio::ssl::context::pem);
m_SSLContext.use_private_key_file ((path / I2P_CONTROL_KEY_FILE).string (), boost::asio::ssl::context::pem);
// handlers
m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler;
// I2PControl
m_I2PControlHandlers[I2P_CONTROL_I2PCONTROL_PASSWORD] = &I2PControlService::PasswordHandler;
// RouterInfo
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_UPTIME] = &I2PControlService::UptimeHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_VERSION] = &I2PControlService::VersionHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_STATUS] = &I2PControlService::StatusHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NET_STATUS] = &I2PControlService::NetStatusHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_IB_1S] = &I2PControlService::InboundBandwidth1S ;
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_OB_1S] = &I2PControlService::OutboundBandwidth1S ;
// RouterManager
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler;
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler;
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler;
}
I2PControlService::~I2PControlService ()
{
Stop ();
}
void I2PControlService::LoadConfig ()
{
auto path = GetPath ();
if (!boost::filesystem::exists (path))
{
if (!boost::filesystem::create_directory (path))
LogPrint (eLogError, "Failed to create i2pcontrol directory");
}
boost::property_tree::ptree pt;
auto filename = path / I2P_CONTROL_CONFIG_FILE;
bool isNew = true;
if (boost::filesystem::exists (filename))
{
try
{
boost::property_tree::read_ini (filename.string (), pt);
isNew = false;
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ());
}
}
m_Password = pt.get (I2P_CONTROL_I2PCONTROL_PASSWORD, I2P_CONTROL_DEFAULT_PASSWORD);
if (isNew) SaveConfig ();
}
void I2PControlService::SaveConfig ()
{
boost::property_tree::ptree pt;
pt.put (I2P_CONTROL_I2PCONTROL_PASSWORD, m_Password);
auto filename = GetPath () / I2P_CONTROL_CONFIG_FILE;
// we take care about directory in LoadConfig
try
{
boost::property_tree::write_ini (filename.string (), pt);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ());
}
}
void I2PControlService::Start ()
{
if (!m_IsRunning)
{
Accept ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
}
}
void I2PControlService::Stop ()
{
if (m_IsRunning)
{
m_IsRunning = false;
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
void I2PControlService::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl: ", ex.what ());
}
}
}
void I2PControlService::Accept ()
{
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
{
if (ecode != boost::asio::error::operation_aborted)
Accept ();
if (!ecode)
{
LogPrint (eLogInfo, "New I2PControl request from ", socket->lowest_layer ().remote_endpoint ());
Handshake (socket);
}
else
LogPrint (eLogError, "I2PControl accept error: ", ecode.message ());
}
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
{
socket->async_handshake(boost::asio::ssl::stream_base::server,
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
}
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
{
if (!ecode)
{
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
ReadRequest (socket);
}
else
LogPrint (eLogError, "I2PControl handshake error: ", ecode.message ());
}
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
{
auto request = std::make_shared<I2PControlBuffer>();
socket->async_read_some (
#if BOOST_VERSION >= 104900
boost::asio::buffer (*request),
#else
boost::asio::buffer (request->data (), request->size ()),
#endif
std::bind(&I2PControlService::HandleRequestReceived, this,
std::placeholders::_1, std::placeholders::_2, socket, request));
}
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode)
{
LogPrint (eLogError, "I2PControl read error: ", ecode.message ());
}
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, "Malformed I2PControl 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);
}
}
#if GCC47_BOOST149
LogPrint (eLogError, "json_read is not supported due bug in boost 1.49 with gcc 4.7");
#else
boost::property_tree::ptree pt;
boost::property_tree::read_json (ss, pt);
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ())
{
std::ostringstream response;
response << "{\"id\":" << pt.get<std::string>(I2P_CONTROL_PROPERTY_ID) << ",\"result\":{";
(this->*(it->second))(pt.get_child (I2P_CONTROL_PROPERTY_PARAMS), response);
response << "},\"jsonrpc\":\"2.0\"}";
SendResponse (socket, buf, response, isHtml);
}
else
LogPrint (eLogWarning, "Unknown I2PControl method ", method);
#endif
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl handle request: ", ex.what ());
}
catch (...)
{
LogPrint (eLogError, "I2PControl handle request unknown exception");
}
}
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
{
ss << "\"" << name << "\":" << value;
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const
{
ss << "\"" << name << "\":";
if (value.length () > 0)
ss << "\"" << value << "\"";
else
ss << "null";
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
{
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
{
size_t len = response.str ().length (), offset = 0;
if (isHtml)
{
std::ostringstream header;
header << "HTTP/1.1 200 OK\r\n";
header << "Connection: close\r\n";
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
header << "Content-Type: application/json\r\n";
header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
header.imbue(std::locale (header.getloc(), facet));
header << boost::posix_time::second_clock::local_time() << "\r\n";
header << "\r\n";
offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset);
}
memcpy (buf->data () + offset, response.str ().c_str (), len);
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
boost::asio::transfer_all (),
std::bind(&I2PControlService::HandleResponseSent, this,
std::placeholders::_1, std::placeholders::_2, socket, buf));
}
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode)
LogPrint (eLogError, "I2PControl write error: ", ecode.message ());
}
// handlers
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
int api = params.get<int> (I2P_CONTROL_PARAM_API);
auto password = params.get<std::string> (I2P_CONTROL_PARAM_PASSWORD);
LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password);
if (password != m_Password)
LogPrint (eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", m_Password);
InsertParam (results, I2P_CONTROL_PARAM_API, api);
results << ",";
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
m_Tokens.insert (token);
InsertParam (results, I2P_CONTROL_PARAM_TOKEN, token);
}
void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
auto echo = params.get<std::string> (I2P_CONTROL_PARAM_ECHO);
LogPrint (eLogDebug, "I2PControl Echo Echo=", echo);
InsertParam (results, I2P_CONTROL_PARAM_RESULT, echo);
}
// I2PControl
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl I2PControl");
for (auto& it: params)
{
LogPrint (eLogDebug, it.first);
auto it1 = m_I2PControlHandlers.find (it.first);
if (it1 != m_I2PControlHandlers.end ())
{
(this->*(it1->second))(it.second.data ());
InsertParam (results, it.first, "");
}
else
LogPrint (eLogError, "I2PControl I2PControl unknown request ", it.first);
}
}
void I2PControlService::PasswordHandler (const std::string& value)
{
LogPrint (eLogDebug, "I2PControl new password=", value);
m_Password = value;
m_Tokens.clear ();
SaveConfig ();
}
// RouterInfo
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl RouterInfo");
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
if (it1 != m_RouterInfoHandlers.end ())
{
if (it != params.begin ()) results << ",";
(this->*(it1->second))(results);
}
else
LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it->first);
}
}
void I2PControlService::UptimeHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime ()*1000);
}
void I2PControlService::VersionHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_VERSION, VERSION);
}
void I2PControlService::StatusHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_STATUS, "???"); // TODO:
}
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters ());
}
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS, (int)i2p::transport::transports.GetPeers ().size ());
}
void I2PControlService::NetStatusHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus ());
}
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING, (int)i2p::tunnel::tunnels.GetTransitTunnels ().size ());
}
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_IB_1S, (double)i2p::transport::transports.GetInBandwidth ());
}
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
{
InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_OB_1S, (double)i2p::transport::transports.GetOutBandwidth ());
}
// RouterManager
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl RouterManager");
for (auto it = params.begin (); it != params.end (); it++)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, it->first);
auto it1 = m_RouterManagerHandlers.find (it->first);
if (it1 != m_RouterManagerHandlers.end ())
(this->*(it1->second))(results);
else
LogPrint (eLogError, "I2PControl RouterManager unknown request ", it->first);
}
}
void I2PControlService::ShutdownHandler (std::ostringstream& results)
{
LogPrint (eLogInfo, "Shutdown requested");
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
});
}
void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results)
{
i2p::context.SetAcceptsTunnels (false);
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout ();
LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds");
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL, "");
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode)
{
Daemon.running = 0;
});
}
void I2PControlService::ReseedHandler (std::ostringstream& results)
{
LogPrint (eLogInfo, "Reseed requested");
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
i2p::data::netdb.Reseed ();
}
// network setting
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
LogPrint (eLogDebug, "I2PControl NetworkSetting");
for (auto it = params.begin (); it != params.end (); it++)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, it->first);
auto it1 = m_NetworkSettingHandlers.find (it->first);
if (it1 != m_NetworkSettingHandlers.end ())
(this->*(it1->second))(it->second.data (), results);
else
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it->first);
}
}
// certificate
void I2PControlService::CreateCertificate ()
{
EVP_PKEY * pkey = EVP_PKEY_new ();
RSA * rsa = RSA_new ();
BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ());
RSA_generate_key_ex (rsa, 4096, e, NULL);
BN_free (e);
if (rsa)
{
EVP_PKEY_assign_RSA (pkey, rsa);
X509 * x509 = X509_new ();
ASN1_INTEGER_set (X509_get_serialNumber (x509), 1);
X509_gmtime_adj (X509_get_notBefore (x509), 0);
X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration
X509_set_pubkey (x509, pkey); // public key
X509_NAME * name = X509_get_subject_name (x509);
X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"RU", -1, -1, 0); // country (Russia by default)
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
X509_set_issuer_name (x509, name); // set issuer to ourselves
X509_sign (x509, pkey, EVP_sha1 ()); // sign
// save key and certificate
// keys
auto filename = GetPath () / I2P_CONTROL_KEY_FILE;
FILE * f= fopen (filename.string ().c_str (), "wb");
if (f)
{
PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL);
fclose (f);
}
else
LogPrint (eLogError, "Can't open file ", filename);
// certificate
filename = GetPath () / I2P_CONTROL_CERT_FILE;
f= fopen (filename.string ().c_str (), "wb");
if (f)
{
PEM_write_X509 (f, x509);
fclose (f);
}
else
LogPrint (eLogError, "Can't open file ", filename);
X509_free (x509);
}
else
LogPrint (eLogError, "Couldn't create RSA key for certificate");
EVP_PKEY_free (pkey);
}
}
}

View File

@@ -1,173 +0,0 @@
#ifndef I2P_CONTROL_H__
#define I2P_CONTROL_H__
#include <inttypes.h>
#include <thread>
#include <memory>
#include <array>
#include <string>
#include <sstream>
#include <map>
#include <set>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/filesystem.hpp>
#include "util.h"
namespace i2p
{
namespace client
{
const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024;
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
const char I2P_CONTROL_PATH[] = "ipcontrol";
const char I2P_CONTROL_KEY_FILE[] = "key.pem";
const char I2P_CONTROL_CERT_FILE[] = "cert.pem";
const char I2P_CONTROL_CONFIG_FILE[] = "i2pcontrol.conf";
const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie";
const char I2P_CONTROL_PROPERTY_ID[] = "id";
const char I2P_CONTROL_PROPERTY_METHOD[] = "method";
const char I2P_CONTROL_PROPERTY_PARAMS[] = "params";
const char I2P_CONTROL_PROPERTY_RESULT[] = "result";
// methods
const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate";
const char I2P_CONTROL_METHOD_ECHO[] = "Echo";
const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl";
const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo";
const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager";
const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting";
// params
const char I2P_CONTROL_PARAM_API[] = "API";
const char I2P_CONTROL_PARAM_PASSWORD[] = "Password";
const char I2P_CONTROL_PARAM_TOKEN[] = "Token";
const char I2P_CONTROL_PARAM_ECHO[] = "Echo";
const char I2P_CONTROL_PARAM_RESULT[] = "Result";
// I2PControl
const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address";
const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password";
const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port";
// RouterInfo requests
const char I2P_CONTROL_ROUTER_INFO_UPTIME[] = "i2p.router.uptime";
const char I2P_CONTROL_ROUTER_INFO_VERSION[] = "i2p.router.version";
const char I2P_CONTROL_ROUTER_INFO_STATUS[] = "i2p.router.status";
const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers";
const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers";
const char I2P_CONTROL_ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status";
const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating";
const char I2P_CONTROL_ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s";
const char I2P_CONTROL_ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s";
// RouterManager requests
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown";
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful";
const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed";
// Certificate
const long I2P_CONTROL_CERTIFICATE_VALIDITY = 365*10; // 10 years
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
class I2PControlService
{
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
public:
I2PControlService (const std::string& address, int port);
~I2PControlService ();
void Start ();
void Stop ();
private:
void LoadConfig ();
void SaveConfig ();
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
void Handshake (std::shared_ptr<ssl_socket> socket);
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
void ReadRequest (std::shared_ptr<ssl_socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir() / I2P_CONTROL_PATH; };
void CreateCertificate ();
private:
void InsertParam (std::ostringstream& ss, const std::string& name, int value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, double value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const;
// methods
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results);
void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
// I2PControl
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
void PasswordHandler (const std::string& value);
// RouterInfo
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results);
void UptimeHandler (std::ostringstream& results);
void VersionHandler (std::ostringstream& results);
void StatusHandler (std::ostringstream& results);
void NetDbKnownPeersHandler (std::ostringstream& results);
void NetDbActivePeersHandler (std::ostringstream& results);
void NetStatusHandler (std::ostringstream& results);
void TunnelsParticipatingHandler (std::ostringstream& results);
void InboundBandwidth1S (std::ostringstream& results);
void OutboundBandwidth1S (std::ostringstream& results);
// RouterManager
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
void ShutdownHandler (std::ostringstream& results);
void ShutdownGracefulHandler (std::ostringstream& results);
void ReseedHandler (std::ostringstream& results);
// NetworkSetting
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
private:
std::string m_Password;
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ssl::context m_SSLContext;
boost::asio::deadline_timer m_ShutdownTimer;
std::set<std::string> m_Tokens;
std::map<std::string, MethodHandler> m_MethodHandlers;
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers;
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
};
}
}
#endif

View File

@@ -1,83 +0,0 @@
#include "I2PEndian.h"
// http://habrahabr.ru/post/121811/
// http://codepad.org/2ycmkz2y
#include "LittleBigEndian.h"
#ifdef NEEDS_LOCAL_ENDIAN
uint16_t htobe16(uint16_t int16)
{
BigEndian<uint16_t> u16(int16);
return u16.raw_value;
}
uint32_t htobe32(uint32_t int32)
{
BigEndian<uint32_t> u32(int32);
return u32.raw_value;
}
uint64_t htobe64(uint64_t int64)
{
BigEndian<uint64_t> u64(int64);
return u64.raw_value;
}
uint16_t be16toh(uint16_t big16)
{
LittleEndian<uint16_t> u16(big16);
return u16.raw_value;
}
uint32_t be32toh(uint32_t big32)
{
LittleEndian<uint32_t> u32(big32);
return u32.raw_value;
}
uint64_t be64toh(uint64_t big64)
{
LittleEndian<uint64_t> u64(big64);
return u64.raw_value;
}
#endif
/* it can be used in Windows 8
#include <Winsock2.h>
uint16_t htobe16(uint16_t int16)
{
return htons(int16);
}
uint32_t htobe32(uint32_t int32)
{
return htonl(int32);
}
uint64_t htobe64(uint64_t int64)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx
//return htonll(int64);
return 0;
}
uint16_t be16toh(uint16_t big16)
{
return ntohs(big16);
}
uint32_t be32toh(uint32_t big32)
{
return ntohl(big32);
}
uint64_t be64toh(uint64_t big64)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx
//return ntohll(big64);
return 0;
}
*/

View File

@@ -1,79 +0,0 @@
#include "Destination.h"
#include "Identity.h"
#include "ClientContext.h"
#include "I2PService.h"
namespace i2p
{
namespace client
{
static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
m_LocalDestination (localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE))
{
}
I2PService::I2PService (i2p::data::SigningKeyType kt):
m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt))
{
}
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
assert(streamRequestComplete);
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "Remote destination ", dest, " not found");
streamRequestComplete (nullptr);
}
}
void TCPIPAcceptor::Start ()
{
m_Acceptor.listen ();
Accept ();
}
void TCPIPAcceptor::Stop ()
{
m_Acceptor.close();
m_Timer.cancel ();
ClearHandlers();
}
void TCPIPAcceptor::Accept ()
{
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (!ecode)
{
LogPrint(eLogDebug,"--- ",GetName()," accepted");
auto handler = CreateHandler(socket);
if (handler)
{
AddHandler(handler);
handler->Handle();
}
else
socket->close();
Accept();
}
else
{
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogError,"--- ",GetName()," Closing socket on accept because: ", ecode.message ());
}
}
}
}

View File

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

View File

@@ -1,407 +0,0 @@
#include <cassert>
#include "Base.h"
#include "Log.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PTunnel.h"
namespace i2p
{
namespace client
{
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port):
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
m_IsQuiet (true)
{
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port);
}
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
{
}
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (target), m_IsQuiet (quiet)
{
}
I2PTunnelConnection::~I2PTunnelConnection ()
{
}
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
{
if (m_Stream)
{
if (msg)
m_Stream->Send (msg, len); // connect and send
else
m_Stream->Send (m_Buffer, 0); // connect
}
StreamReceive ();
Receive ();
}
void I2PTunnelConnection::Connect ()
{
if (m_Socket)
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
shared_from_this (), std::placeholders::_1));
}
void I2PTunnelConnection::Terminate ()
{
if (Kill()) return;
if (m_Stream)
{
m_Stream->Close ();
m_Stream.reset ();
}
m_Socket->close ();
Done(shared_from_this ());
}
void I2PTunnelConnection::Receive ()
{
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (m_Stream)
{
auto s = shared_from_this ();
m_Stream->AsyncSend (m_Buffer, bytes_transferred,
[s](const boost::system::error_code& ecode)
{
if (!ecode)
s->Receive ();
else
s->Terminate ();
});
}
}
}
void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
{
if (ecode)
{
LogPrint (eLogError, "I2PTunnel: write error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
StreamReceive ();
}
void I2PTunnelConnection::StreamReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
I2P_TUNNEL_CONNECTION_MAX_IDLE);
}
void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
Write (m_StreamBuffer, bytes_transferred);
}
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
{
m_Socket->async_send (boost::asio::buffer (buf, len),
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
}
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
{
if (ecode)
{
LogPrint (eLogError, "I2PTunnel: connect error: ", ecode.message ());
Terminate ();
}
else
{
LogPrint (eLogDebug, "I2PTunnel: connected");
if (m_IsQuiet)
StreamReceive ();
else
{
// send destination first like received from I2P
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
dest += "\n";
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
HandleStreamReceive (boost::system::error_code (), dest.size ());
}
Receive ();
}
}
I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_From (stream->GetRemoteIdentity ())
{
}
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
{
if (m_HeaderSent)
I2PTunnelConnection::Write (buf, len);
else
{
m_InHeader.clear ();
m_InHeader.write ((const char *)buf, len);
std::string line;
bool endOfHeader = false;
while (!endOfHeader)
{
std::getline(m_InHeader, line);
if (!m_InHeader.fail ())
{
if (line.find ("Host:") != std::string::npos)
m_OutHeader << "Host: " << m_Host << "\r\n";
else
m_OutHeader << line << "\n";
if (line == "\r") endOfHeader = true;
}
else
break;
}
// add X-I2P fields
if (m_From)
{
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
// m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
}
if (endOfHeader)
{
m_OutHeader << m_InHeader.str (); // data right after header
m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
}
}
}
/* This handler tries to stablish a connection with the desired server and dies if it fails to do so */
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
{
public:
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
I2PServiceHandler(parent), m_DestinationIdentHash(destination),
m_DestinationPort (destinationPort), m_Socket(socket) {};
void Handle();
void Terminate();
private:
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
i2p::data::IdentHash m_DestinationIdentHash;
int m_DestinationPort;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
};
void I2PClientTunnelHandler::Handle()
{
GetOwner()->GetLocalDestination ()->CreateStream (
std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1),
m_DestinationIdentHash, m_DestinationPort);
}
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (Kill()) return;
LogPrint (eLogDebug, "I2PTunnel: new connection");
auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect ();
Done(shared_from_this());
}
else
{
LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
Terminate();
}
}
void I2PClientTunnelHandler::Terminate()
{
if (Kill()) return;
if (m_Socket)
{
m_Socket->close();
m_Socket = nullptr;
}
Done(shared_from_this());
}
I2PClientTunnel::I2PClientTunnel (const std::string& destination, const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
TCPIPAcceptor (address, port, localDestination), m_Destination (destination), m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort) {}
void I2PClientTunnel::Start ()
{
TCPIPAcceptor::Start ();
GetIdentHash();
}
void I2PClientTunnel::Stop ()
{
TCPIPAcceptor::Stop();
auto *originalIdentHash = m_DestinationIdentHash;
m_DestinationIdentHash = nullptr;
delete originalIdentHash;
}
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash ()
{
if (!m_DestinationIdentHash)
{
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
else
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
}
return m_DestinationIdentHash;
}
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
const i2p::data::IdentHash *identHash = GetIdentHash();
if (identHash)
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, m_DestinationPort, socket);
else
return nullptr;
}
I2PServerTunnel::I2PServerTunnel (const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, int inport):
I2PService (localDestination), m_Address (address), m_Port (port), m_IsAccessList (false)
{
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port);
}
void I2PServerTunnel::Start ()
{
m_Endpoint.port (m_Port);
boost::system::error_code ec;
auto addr = boost::asio::ip::address::from_string (m_Address, ec);
if (!ec)
{
m_Endpoint.address (addr);
Accept ();
}
else
{
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ());
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""),
std::bind (&I2PServerTunnel::HandleResolve, this,
std::placeholders::_1, std::placeholders::_2, resolver));
}
}
void I2PServerTunnel::Stop ()
{
ClearHandlers ();
}
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
{
if (!ecode)
{
auto addr = (*it).endpoint ().address ();
LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr);
m_Endpoint.address (addr);
Accept ();
}
else
LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ());
}
void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList)
{
m_AccessList = accessList;
m_IsAccessList = true;
}
void I2PServerTunnel::Accept ()
{
if (m_PortDestination)
m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
auto localDestination = GetLocalDestination ();
if (localDestination)
{
if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
}
else
LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel");
}
void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (m_IsAccessList)
{
if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ()))
{
LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
stream->Close ();
return;
}
}
CreateI2PConnection (stream);
}
}
void I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
AddHandler (conn);
conn->Connect ();
}
I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int inport):
I2PServerTunnel (address, port, localDestination, inport)
{
}
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), GetAddress ());
AddHandler (conn);
conn->Connect ();
}
}
}

View File

@@ -1,157 +0,0 @@
#ifndef I2PTUNNEL_H__
#define I2PTUNNEL_H__
#include <inttypes.h>
#include <string>
#include <set>
#include <memory>
#include <sstream>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Destination.h"
#include "Streaming.h"
#include "I2PService.h"
namespace i2p
{
namespace client
{
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
// for HTTP tunnels
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
//const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
//const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // full address in base32
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
{
public:
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect ();
protected:
void Terminate ();
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
void HandleWrite (const boost::system::error_code& ecode);
void StreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleConnect (const boost::system::error_code& ecode);
private:
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
std::shared_ptr<i2p::stream::Stream> m_Stream;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsQuiet; // don't send destination
};
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
{
public:
I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host);
protected:
void Write (const uint8_t * buf, size_t len);
private:
std::string m_Host;
std::stringstream m_InHeader, m_OutHeader;
bool m_HeaderSent;
std::shared_ptr<const i2p::data::IdentityEx> m_From;
};
class I2PClientTunnel: public TCPIPAcceptor
{
protected:
// Implements TCPIPAcceptor
std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
const char* GetName() { return "I2P Client Tunnel"; }
public:
I2PClientTunnel (const std::string& destination, const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort = 0);
~I2PClientTunnel () {}
void Start ();
void Stop ();
private:
const i2p::data::IdentHash * GetIdentHash ();
std::string m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash;
int m_DestinationPort;
};
class I2PServerTunnel: public I2PService
{
public:
I2PServerTunnel (const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, int inport = 0);
void Start ();
void Stop ();
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
const std::string& GetAddress() const { return m_Address; }
int GetPort () const { return m_Port; };
const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; }
private:
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
void Accept ();
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
virtual void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
std::string m_Address;
int m_Port;
boost::asio::ip::tcp::endpoint m_Endpoint;
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
std::set<i2p::data::IdentHash> m_AccessList;
bool m_IsAccessList;
};
class I2PServerTunnelHTTP: public I2PServerTunnel
{
public:
I2PServerTunnelHTTP (const std::string& address, int port,
std::shared_ptr<ClientDestination> localDestination, int inport = 0);
private:
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
};
}
}
#endif

View File

@@ -1,563 +0,0 @@
#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"
#include "Identity.h"
namespace i2p
{
namespace data
{
Identity& Identity::operator=(const Keys& keys)
{
// copy public and signing keys together
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
memset (certificate, 0, sizeof (certificate));
return *this;
}
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
{
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
return DEFAULT_IDENTITY_SIZE;
}
IdentHash Identity::Hash () const
{
IdentHash hash;
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
return hash;
}
IdentityEx::IdentityEx ():
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
}
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type)
{
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
size_t excessLen = 0;
uint8_t * excessBuf = nullptr;
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
{
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128
excessBuf = new uint8_t[excessLen];
memcpy (excessBuf, signingKey + 128, excessLen);
break;
}
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break;
}
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
}
m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length
// fill certificate
m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY;
htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen);
// fill extended buffer
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
htobe16buf (m_ExtendedBuffer, type);
htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL);
if (excessLen && excessBuf)
{
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
delete[] excessBuf;
}
// calculate ident hash
uint8_t * buf = new uint8_t[GetFullLen ()];
ToBuffer (buf, GetFullLen ());
SHA256(buf, GetFullLen (), m_IdentHash);
delete[] buf;
}
else // DSA-SHA1
{
memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey));
memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
m_IdentHash = m_StandardIdentity.Hash ();
m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
}
CreateVerifier ();
}
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
FromBuffer (buf, len);
}
IdentityEx::IdentityEx (const IdentityEx& other):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = other;
}
IdentityEx::IdentityEx (const Identity& standard):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = standard;
}
IdentityEx::~IdentityEx ()
{
delete[] m_ExtendedBuffer;
}
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
{
memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
m_IdentHash = other.m_IdentHash;
delete[] m_ExtendedBuffer;
m_ExtendedLen = other.m_ExtendedLen;
if (m_ExtendedLen > 0)
{
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen);
}
else
m_ExtendedBuffer = nullptr;
m_Verifier = nullptr;
return *this;
}
IdentityEx& IdentityEx::operator=(const Identity& standard)
{
m_StandardIdentity = standard;
m_IdentHash = m_StandardIdentity.Hash ();
delete[] m_ExtendedBuffer;
m_ExtendedBuffer = nullptr;
m_ExtendedLen = 0;
m_Verifier = nullptr;
return *this;
}
size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len)
{
if (len < DEFAULT_IDENTITY_SIZE)
{
LogPrint (eLogError, "Identity: buffer length ", len, " is too small");
return 0;
}
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
delete[] m_ExtendedBuffer;
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
if (m_ExtendedLen)
{
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
{
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
}
else
{
LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
return 0;
}
}
else
{
m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
}
SHA256(buf, GetFullLen (), m_IdentHash);
m_Verifier = nullptr;
return GetFullLen ();
}
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
{
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
return GetFullLen ();
}
size_t IdentityEx::FromBase64(const std::string& s)
{
uint8_t buf[1024];
auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024);
return FromBuffer (buf, len);
}
std::string IdentityEx::ToBase64 () const
{
uint8_t buf[1024];
char str[1536];
size_t l = ToBuffer (buf, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536);
str[l1] = 0;
return std::string (str);
}
size_t IdentityEx::GetSigningPublicKeyLen () const
{
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->GetPublicKeyLen ();
return 128;
}
size_t IdentityEx::GetSigningPrivateKeyLen () const
{
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->GetPrivateKeyLen ();
return GetSignatureLen ()/2;
}
size_t IdentityEx::GetSignatureLen () const
{
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->GetSignatureLen ();
return i2p::crypto::DSA_SIGNATURE_LENGTH;
}
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
if (!m_Verifier) CreateVerifier ();
if (m_Verifier)
return m_Verifier->Verify (buf, len, signature);
return false;
}
SigningKeyType IdentityEx::GetSigningKeyType () const
{
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return bufbe16toh (m_ExtendedBuffer); // signing key
return SIGNING_KEY_TYPE_DSA_SHA1;
}
CryptoKeyType IdentityEx::GetCryptoKeyType () const
{
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
return CRYPTO_KEY_TYPE_ELGAMAL;
}
void IdentityEx::CreateVerifier () const
{
auto keyType = GetSigningKeyType ();
switch (keyType)
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Verifier.reset (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));
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
m_Verifier.reset (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
{
uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto::ECDSAP521Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
{
uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
{
uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
{
uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH];
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier.reset (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
break;
}
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
m_Verifier.reset (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
break;
}
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
}
}
void IdentityEx::DropVerifier () const
{
// TODO: potential race condition with Verify
m_Verifier = nullptr;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{
m_Public = std::make_shared<IdentityEx>(Identity (keys));
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_Signer = nullptr;
CreateSigner ();
return *this;
}
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
{
m_Public = std::make_shared<IdentityEx>(*other.m_Public);
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ());
m_Signer = nullptr;
CreateSigner ();
return *this;
}
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
{
m_Public = std::make_shared<IdentityEx>(buf, len);
size_t ret = m_Public->GetFullLen ();
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
ret += signingPrivateKeySize;
m_Signer = nullptr;
CreateSigner ();
return ret;
}
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
{
size_t ret = m_Public->ToBuffer (buf, len);
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize;
return ret;
}
size_t PrivateKeys::FromBase64(const std::string& s)
{
uint8_t * buf = new uint8_t[s.length ()];
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
size_t ret = FromBuffer (buf, l);
delete[] buf;
return ret;
}
std::string PrivateKeys::ToBase64 () const
{
uint8_t * buf = new uint8_t[GetFullLen ()];
char * str = new char[GetFullLen ()*2];
size_t l = ToBuffer (buf, GetFullLen ());
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
str[l1] = 0;
delete[] buf;
std::string ret(str);
delete[] str;
return ret;
}
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
if (m_Signer)
m_Signer->Sign (buf, len, signature);
}
void PrivateKeys::CreateSigner ()
{
switch (m_Public->GetSigningKeyType ())
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
m_Signer.reset (new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
m_Signer.reset (new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
m_Signer.reset (new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey));
break;
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
}
}
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type)
{
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
PrivateKeys keys;
// signature
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
}
// encryption
uint8_t publicKey[256];
i2p::crypto::GenerateElGamalKeyPair (keys.m_PrivateKey, publicKey);
// identity
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type);
keys.CreateSigner ();
return keys;
}
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
}
Keys CreateRandomKeys ()
{
Keys keys;
// encryption
i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey);
// signing
i2p::crypto::CreateDSARandomKeys (keys.signingPrivateKey, keys.signingKey);
return keys;
}
IdentHash CreateRoutingKey (const IdentHash& ident)
{
uint8_t buf[41]; // ident + yyyymmdd
memcpy (buf, (const uint8_t *)ident, 32);
time_t t = time (nullptr);
struct tm tm;
#ifdef _WIN32
gmtime_s(&tm, &t);
sprintf_s((char *)(buf + 32), 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#else
gmtime_r(&t, &tm);
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#endif
IdentHash key;
SHA256(buf, 40, key);
return key;
}
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
{
XORMetric m;
const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL ();
m.metric_ll[0] = hash1[0] ^ hash2[0];
m.metric_ll[1] = hash1[1] ^ hash2[1];
m.metric_ll[2] = hash1[2] ^ hash2[2];
m.metric_ll[3] = hash1[3] ^ hash2[3];
return m;
}
}
}

View File

@@ -1,195 +0,0 @@
#ifndef IDENTITY_H__
#define IDENTITY_H__
#include <inttypes.h>
#include <string.h>
#include <string>
#include <memory>
#include "Base.h"
#include "Signature.h"
namespace i2p
{
namespace data
{
typedef Tag<32> IdentHash;
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
{
return ident.ToBase64 ().substr (0, 4);
}
struct Keys
{
uint8_t privateKey[256];
uint8_t signingPrivateKey[20];
uint8_t publicKey[256];
uint8_t signingKey[128];
};
const uint8_t CERTIFICATE_TYPE_NULL = 0;
const uint8_t CERTIFICATE_TYPE_HASHCASH = 1;
const uint8_t CERTIFICATE_TYPE_HIDDEN = 2;
const uint8_t CERTIFICATE_TYPE_SIGNED = 3;
const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4;
const uint8_t CERTIFICATE_TYPE_KEY = 5;
struct Identity
{
uint8_t publicKey[256];
uint8_t signingKey[128];
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
Identity () = default;
Identity (const Keys& keys) { *this = keys; };
Identity& operator=(const Keys& keys);
size_t FromBuffer (const uint8_t * buf, size_t len);
IdentHash Hash () const;
};
Keys CreateRandomKeys ();
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
typedef uint16_t SigningKeyType;
typedef uint16_t CryptoKeyType;
class IdentityEx
{
public:
IdentityEx ();
IdentityEx (const uint8_t * publicKey, const uint8_t * signingKey,
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
IdentityEx (const uint8_t * buf, size_t len);
IdentityEx (const IdentityEx& other);
IdentityEx (const Identity& standard);
~IdentityEx ();
IdentityEx& operator=(const IdentityEx& other);
IdentityEx& operator=(const Identity& standard);
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
size_t FromBase64(const std::string& s);
std::string ToBase64 () const;
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; };
const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; };
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
size_t GetSigningPublicKeyLen () const;
size_t GetSigningPrivateKeyLen () const;
size_t GetSignatureLen () const;
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
SigningKeyType GetSigningKeyType () const;
CryptoKeyType GetCryptoKeyType () const;
void DropVerifier () const; // to save memory
private:
void CreateVerifier () const;
private:
Identity m_StandardIdentity;
IdentHash m_IdentHash;
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
size_t m_ExtendedLen;
uint8_t * m_ExtendedBuffer;
};
class PrivateKeys // for eepsites
{
public:
PrivateKeys () = default;
PrivateKeys (const PrivateKeys& other) { *this = other; };
PrivateKeys (const Keys& keys) { *this = keys; };
PrivateKeys& operator=(const Keys& keys);
PrivateKeys& operator=(const PrivateKeys& other);
~PrivateKeys () = default;
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
size_t GetFullLen () const { return m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); };
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
size_t FromBase64(const std::string& s);
std::string ToBase64 () const;
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
private:
void CreateSigner ();
private:
std::shared_ptr<IdentityEx> m_Public;
uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
std::unique_ptr<i2p::crypto::Signer> m_Signer;
};
// kademlia
struct XORMetric
{
union
{
uint8_t metric[32];
uint64_t metric_ll[4];
};
void SetMin () { memset (metric, 0, 32); };
void SetMax () { memset (metric, 0xFF, 32); };
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
};
IdentHash CreateRoutingKey (const IdentHash& ident);
XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
// destination for delivery instuctions
class RoutingDestination
{
public:
RoutingDestination () {};
virtual ~RoutingDestination () {};
virtual const IdentHash& GetIdentHash () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual bool IsDestination () const = 0; // for garlic
};
class LocalDestination
{
public:
virtual ~LocalDestination() {};
virtual const PrivateKeys& GetPrivateKeys () const = 0;
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () 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);
};
};
}
}
#endif

View File

@@ -1,157 +0,0 @@
#include <string.h>
#include "I2PEndian.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "TunnelPool.h"
#include "LeaseSet.h"
namespace i2p
{
namespace data
{
LeaseSet::LeaseSet (const uint8_t * buf, size_t len):
m_IsValid (true)
{
m_Buffer = new uint8_t[len];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer ();
}
LeaseSet::LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool):
m_IsValid (true)
{
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;
auto tunnels = pool->GetInboundTunnels (5); // 5 tunnels maximum
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
m_BufferLen++;
// leases
for (auto it: tunnels)
{
memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32);
m_BufferLen += 32; // gateway id
htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ());
m_BufferLen += 4; // tunnel id
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
ts += rand () % 6; // + random milliseconds 0-5
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)
{
m_Leases.clear ();
if (len > m_BufferLen)
{
auto oldBuffer = m_Buffer;
m_Buffer = new uint8_t[len];
delete[] oldBuffer;
}
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer (false);
}
void LeaseSet::ReadFromBuffer (bool readIdentity)
{
if (readIdentity || !m_Identity)
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t size = m_Identity->GetFullLen ();
memcpy (m_EncryptionKey, m_Buffer + size, 256);
size += 256; // encryption key
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
uint8_t num = m_Buffer[size];
size++; // num
LogPrint (eLogDebug, "LeaseSet: read num=", (int)num);
if (!num) m_IsValid = false;
// process leases
const uint8_t * leases = m_Buffer + size;
for (int i = 0; i < num; i++)
{
Lease lease;
lease.tunnelGateway = leases;
leases += 32; // gateway
lease.tunnelID = bufbe32toh (leases);
leases += 4; // tunnel ID
lease.endDate = bufbe64toh (leases);
leases += 8; // end date
m_Leases.push_back (lease);
// check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway))
{
// if not found request it
LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting");
netdb.RequestDestination (lease.tunnelGateway);
}
}
// verify
if (!m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
{
LogPrint (eLogWarning, "LeaseSet: verification failed");
m_IsValid = false;
}
}
const std::vector<Lease> LeaseSet::GetNonExpiredLeases (bool withThreshold) const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<Lease> leases;
for (auto& it: m_Leases)
{
auto endDate = it.endDate;
if (!withThreshold)
endDate -= i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000;
if (ts < endDate)
leases.push_back (it);
}
return leases;
}
bool LeaseSet::HasExpiredLeases () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto& it: m_Leases)
if (ts >= it.endDate) return true;
return false;
}
bool LeaseSet::HasNonExpiredLeases () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto& it: m_Leases)
if (ts < it.endDate) return true;
return false;
}
}
}

View File

@@ -1,74 +0,0 @@
#ifndef LEASE_SET_H__
#define LEASE_SET_H__
#include <inttypes.h>
#include <string.h>
#include <vector>
#include "Identity.h"
namespace i2p
{
namespace tunnel
{
class TunnelPool;
}
namespace data
{
struct Lease
{
IdentHash tunnelGateway;
uint32_t tunnelID;
uint64_t endDate;
bool operator< (const Lease& other) const
{
if (endDate != other.endDate)
return endDate > other.endDate;
else
return tunnelID < other.tunnelID;
}
};
const int MAX_LS_BUFFER_SIZE = 3072;
class LeaseSet: public RoutingDestination
{
public:
LeaseSet (const uint8_t * buf, size_t len);
LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool);
~LeaseSet () { delete[] m_Buffer; };
void Update (const uint8_t * buf, size_t len);
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
const uint8_t * GetBuffer () const { return m_Buffer; };
size_t GetBufferLen () const { return m_BufferLen; };
bool IsValid () const { return m_IsValid; };
// implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
const std::vector<Lease>& GetLeases () const { return m_Leases; };
const std::vector<Lease> GetNonExpiredLeases (bool withThreshold = true) const;
bool HasExpiredLeases () const;
bool HasNonExpiredLeases () const;
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
bool IsDestination () const { return true; };
private:
void ReadFromBuffer (bool readIdentity = true);
private:
bool m_IsValid;
std::vector<Lease> m_Leases;
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t m_EncryptionKey[256];
uint8_t * m_Buffer;
size_t m_BufferLen;
};
}
}
#endif

View File

@@ -1,88 +0,0 @@
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/Win32Service.cpp
include Makefile.mingw
endif
all: mk_build_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
mk_build_dir:
mkdir -p obj
api: mk_build_dir $(SHLIB) $(ARLIB)
api_client: mk_build_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:
@mkdir -p obj
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
obj/%.o : %.cpp
@mkdir -p obj
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
# '-' is 'ignore if missing' on first run
-include $(DEPS)
$(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) $(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)
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_build_dir

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
CXX = g++
CXXFLAGS = -O2 -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
NEEDED_CXXFLAGS = -std=c++11
BOOST_SUFFIX = -mgw48-mt-1_59
INCFLAGS = -I/usr/include/ -I/usr/local/include/ -I/c/dev/openssl/include -I/c/dev/boost/include/boost-1_59
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/c/dev/openssl -L/c/dev/boost/lib
LDLIBS = -lboost_system$(BOOST_SUFFIX) -lboost_date_time$(BOOST_SUFFIX) -lboost_filesystem$(BOOST_SUFFIX) -lboost_regex$(BOOST_SUFFIX) -lboost_program_options$(BOOST_SUFFIX) -lssl -lcrypto -lz -lwsock32 -lws2_32 -lgdi32 -liphlpapi -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread

View File

@@ -1,25 +0,0 @@
CXX = clang++
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
#CXXFLAGS = -g -O2 -Wall -std=c++11
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
ifeq ($(USE_UPNP),1)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif
# OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
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,933 +0,0 @@
#include <string.h>
#include <stdlib.h>
#include <openssl/dh.h>
#include <openssl/sha.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Log.h"
#include "Timestamp.h"
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "RouterContext.h"
#include "Transports.h"
#include "NetDb.h"
#include "NTCPSession.h"
using namespace i2p::crypto;
namespace i2p
{
namespace transport
{
NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()),
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
{
m_DHKeysPair = transports.GetNextDHKeysPair ();
m_Establisher = new Establisher;
}
NTCPSession::~NTCPSession ()
{
delete m_Establisher;
}
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
{
uint8_t sharedKey[256];
m_DHKeysPair->Agree (pubKey, sharedKey);
uint8_t * aesKey = key;
if (sharedKey[0] & 0x80)
{
aesKey[0] = 0;
memcpy (aesKey + 1, sharedKey, 31);
}
else if (sharedKey[0])
memcpy (aesKey, sharedKey, 32);
else
{
// find first non-zero byte
uint8_t * nonZero = sharedKey + 1;
while (!*nonZero)
{
nonZero++;
if (nonZero - sharedKey > 32)
{
LogPrint (eLogWarning, "NTCP: First 32 bytes of shared key is all zeros, ignored");
return;
}
}
memcpy (aesKey, nonZero, 32);
}
}
void NTCPSession::Done ()
{
m_Server.GetService ().post (std::bind (&NTCPSession::Terminate, shared_from_this ()));
}
void NTCPSession::Terminate ()
{
if (!m_IsTerminated)
{
m_IsTerminated = true;
m_IsEstablished = false;
m_Socket.close ();
transports.PeerDisconnected (shared_from_this ());
m_Server.RemoveNTCPSession (shared_from_this ());
m_SendQueue.clear ();
m_NextMessage = nullptr;
m_TerminationTimer.cancel ();
LogPrint (eLogDebug, "NTCP: session terminated");
}
}
void NTCPSession::Connected ()
{
m_IsEstablished = true;
delete m_Establisher;
m_Establisher = nullptr;
m_DHKeysPair = nullptr;
SendTimeSyncMessage ();
m_SendQueue.push_back (CreateDatabaseStoreMsg ()); // we tell immediately who we are
transports.PeerConnected (shared_from_this ());
}
void NTCPSession::ClientLogin ()
{
if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair ();
// send Phase1
const uint8_t * x = m_DHKeysPair->GetPublicKey ();
memcpy (m_Establisher->phase1.pubKey, x, 256);
SHA256(x, 256, m_Establisher->phase1.HXxorHI);
const uint8_t * ident = m_RemoteIdentity->GetIdentHash ();
for (int i = 0; i < 32; i++)
m_Establisher->phase1.HXxorHI[i] ^= ident[i];
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
void NTCPSession::ServerLogin ()
{
boost::system::error_code ec;
auto ep = m_Socket.remote_endpoint(ec);
if (!ec)
{
m_ConnectedFrom = ep.address ();
// receive Phase1
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
}
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: couldn't send Phase 1 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
}
void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: phase 1 read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
// verify ident
uint8_t digest[32];
SHA256(m_Establisher->phase1.pubKey, 256, digest);
const uint8_t * ident = i2p::context.GetIdentHash ();
for (int i = 0; i < 32; i++)
{
if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i])
{
LogPrint (eLogError, "NTCP: phase 1 error: ident mismatch");
Terminate ();
return;
}
}
SendPhase2 ();
}
}
void NTCPSession::SendPhase2 ()
{
if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair ();
const uint8_t * y = m_DHKeysPair->GetPublicKey ();
memcpy (m_Establisher->phase2.pubKey, y, 256);
uint8_t xy[512];
memcpy (xy, m_Establisher->phase1.pubKey, 256);
memcpy (xy + 256, y, 256);
SHA256(xy, 512, m_Establisher->phase2.encrypted.hxy);
uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ());
memcpy (m_Establisher->phase2.encrypted.timestamp, &tsB, 4);
RAND_bytes (m_Establisher->phase2.encrypted.filler, 12);
i2p::crypto::AESKey aesKey;
CreateAESKey (m_Establisher->phase1.pubKey, aesKey);
m_Encryption.SetKey (aesKey);
m_Encryption.SetIV (y + 240);
m_Decryption.SetKey (aesKey);
m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB));
}
void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Couldn't send Phase 2 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, tsB));
}
}
void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
if (ecode != boost::asio::error::operation_aborted)
{
// this RI is not valid
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
transports.ReuseDHKeysPair (m_DHKeysPair);
m_DHKeysPair = nullptr;
Terminate ();
}
}
else
{
i2p::crypto::AESKey aesKey;
CreateAESKey (m_Establisher->phase2.pubKey, aesKey);
m_Decryption.SetKey (aesKey);
m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240);
m_Encryption.SetKey (aesKey);
m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
// verify
uint8_t xy[512];
memcpy (xy, m_DHKeysPair->GetPublicKey (), 256);
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
uint8_t digest[32];
SHA256 (xy, 512, digest);
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
{
LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash");
transports.ReuseDHKeysPair (m_DHKeysPair);
m_DHKeysPair = nullptr;
Terminate ();
return ;
}
SendPhase3 ();
}
}
void NTCPSession::SendPhase3 ()
{
auto keys = i2p::context.GetPrivateKeys ();
uint8_t * buf = m_ReceiveBuffer;
htobe16buf (buf, keys.GetPublic ()->GetFullLen ());
buf += 2;
buf += i2p::context.GetIdentity ()->ToBuffer (buf, NTCP_BUFFER_SIZE);
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
htobuf32(buf,tsA);
buf += 4;
size_t signatureLen = keys.GetPublic ()->GetSignatureLen ();
size_t len = (buf - m_ReceiveBuffer) + signatureLen;
size_t paddingSize = len & 0x0F; // %16
if (paddingSize > 0)
{
paddingSize = 16 - paddingSize;
// TODO: fill padding with random data
buf += paddingSize;
len += paddingSize;
}
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB
s.Sign (keys, buf);
m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer);
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA));
}
void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Couldn't send Phase 3 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
// wait for phase4
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize);
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase4Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, tsA));
}
}
void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Phase 3 read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
uint8_t * buf = m_ReceiveBuffer;
uint16_t size = bufbe16toh (buf);
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (buf + 2, size));
if (m_Server.FindNTCPSession (m_RemoteIdentity->GetIdentHash ()))
{
LogPrint (eLogError, "NTCP: session already exists");
Terminate ();
}
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen ();
size_t paddingLen = expectedSize & 0x0F;
if (paddingLen) paddingLen = (16 - paddingLen);
if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE)
{
// we need more bytes for Phase3
expectedSize += paddingLen;
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize - NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, tsB, paddingLen));
}
else
HandlePhase3 (tsB, paddingLen);
}
}
void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Phase 3 extra read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE);
HandlePhase3 (tsB, paddingLen);
}
}
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
{
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity->GetFullLen () + 2 /*size*/;
uint32_t tsA = buf32toh(buf);
buf += 4;
buf += paddingLen;
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (tsB); // tsB
if (!s.Verify (m_RemoteIdentity, buf))
{
LogPrint (eLogError, "NTCP: signature verification failed");
Terminate ();
return;
}
SendPhase4 (tsA, tsB);
}
void NTCPSession::SendPhase4 (uint32_t tsA, uint32_t tsB)
{
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (tsB); // tsB
auto keys = i2p::context.GetPrivateKeys ();
auto signatureLen = keys.GetPublic ()->GetSignatureLen ();
s.Sign (keys, m_ReceiveBuffer);
size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize);
m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer);
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogWarning, "NTCP: Couldn't send Phase 4 message: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
LogPrint (eLogInfo, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
m_Server.AddNTCPSession (shared_from_this ());
Connected ();
m_ReceiveBufferOffset = 0;
m_NextMessage = nullptr;
Receive ();
}
}
void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Phase 4 read error: ", ecode.message (), ". Check your clock");
if (ecode != boost::asio::error::operation_aborted)
{
// this router doesn't like us
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
Terminate ();
}
}
else
{
m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
// verify signature
SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (i2p::context.GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB
if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer))
{
LogPrint (eLogError, "NTCP: Phase 4 process error: signature verification failed");
Terminate ();
return;
}
LogPrint (eLogDebug, "NTCP: session to ", m_Socket.remote_endpoint (), " connected");
Connected ();
m_ReceiveBufferOffset = 0;
m_NextMessage = nullptr;
Receive ();
}
}
void NTCPSession::Receive ()
{
m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, NTCP_BUFFER_SIZE - m_ReceiveBufferOffset),
std::bind(&NTCPSession::HandleReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Read error: ", ecode.message ());
if (!m_NumReceivedBytes) m_Server.Ban (m_ConnectedFrom);
//if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_NumReceivedBytes += bytes_transferred;
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
m_ReceiveBufferOffset += bytes_transferred;
if (m_ReceiveBufferOffset >= 16)
{
int numReloads = 0;
do
{
uint8_t * nextBlock = m_ReceiveBuffer;
while (m_ReceiveBufferOffset >= 16)
{
if (!DecryptNextBlock (nextBlock)) // 16 bytes
{
Terminate ();
return;
}
nextBlock += 16;
m_ReceiveBufferOffset -= 16;
}
if (m_ReceiveBufferOffset > 0)
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
// try to read more
if (numReloads < 5)
{
boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec);
if (moreBytes)
{
if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset)
moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset;
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes));
if (ec)
{
LogPrint (eLogError, "NTCP: Read more bytes error: ", ec.message ());
Terminate ();
return;
}
m_NumReceivedBytes += moreBytes;
m_ReceiveBufferOffset += moreBytes;
numReloads++;
}
}
}
while (m_ReceiveBufferOffset >= 16);
m_Handler.Flush ();
}
ScheduleTermination (); // reset termination timer
Receive ();
}
}
bool NTCPSession::DecryptNextBlock (const uint8_t * encrypted) // 16 bytes
{
if (!m_NextMessage) // new message, header expected
{
// decrypt header and extract length
uint8_t buf[16];
m_Decryption.Decrypt (encrypted, buf);
uint16_t dataSize = bufbe16toh (buf);
if (dataSize)
{
// new message
if (dataSize + 16U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
{
LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size");
return false;
}
auto msg = (dataSize + 16U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
m_NextMessage = msg;
memcpy (m_NextMessage->buf, buf, 16);
m_NextMessageOffset = 16;
m_NextMessage->offset = 2; // size field
m_NextMessage->len = dataSize + 2;
}
else
{
// timestamp
LogPrint (eLogDebug, "NTCP: Timestamp");
return true;
}
}
else // message continues
{
m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset);
m_NextMessageOffset += 16;
}
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
{
// we have a complete I2NP message
uint8_t checksum[4];
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->buf, m_NextMessageOffset - 4));
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4))
m_Handler.PutNextMessage (m_NextMessage);
else
LogPrint (eLogWarning, "NTCP: Incorrect adler checksum of message, dropped");
m_NextMessage = nullptr;
}
return true;
}
void NTCPSession::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{
m_IsSending = true;
boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<std::shared_ptr<I2NPMessage> >{ msg }));
}
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg)
{
uint8_t * sendBuffer;
int len;
if (msg)
{
// regular I2NP
if (msg->offset < 2)
LogPrint (eLogError, "NTCP: Malformed I2NP message"); // TODO:
sendBuffer = msg->GetBuffer () - 2;
len = msg->GetLength ();
htobe16buf (sendBuffer, len);
}
else
{
// prepare timestamp
sendBuffer = m_TimeSyncBuffer;
len = 4;
htobuf16(sendBuffer, 0);
htobe32buf (sendBuffer + 2, time (0));
}
int rem = (len + 6) & 0x0F; // %16
int padding = 0;
if (rem > 0) padding = 16 - rem;
// TODO: fill padding
htobe32buf (sendBuffer + len + 2 + padding, adler32 (adler32 (0, Z_NULL, 0), sendBuffer, len + 2+ padding));
int l = len + padding + 6;
m_Encryption.Encrypt(sendBuffer, l, sendBuffer);
return boost::asio::buffer ((const uint8_t *)sendBuffer, l);
}
void NTCPSession::Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
m_IsSending = true;
std::vector<boost::asio::const_buffer> bufs;
for (auto it: msgs)
bufs.push_back (CreateMsgBuffer (it));
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
}
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs)
{
m_IsSending = false;
if (ecode)
{
LogPrint (eLogWarning, "NTCP: Couldn't send msgs: ", ecode.message ());
// we shouldn't call Terminate () here, because HandleReceive takes care
// TODO: 'delete this' statement in Terminate () must be eliminated later
// Terminate ();
}
else
{
m_NumSentBytes += bytes_transferred;
i2p::transport::transports.UpdateSentBytes (bytes_transferred);
if (!m_SendQueue.empty())
{
Send (m_SendQueue);
m_SendQueue.clear ();
}
else
ScheduleTermination (); // reset termination timer
}
}
void NTCPSession::SendTimeSyncMessage ()
{
Send (nullptr);
}
void NTCPSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs));
}
void NTCPSession::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
{
if (m_IsTerminated) return;
if (m_IsSending)
{
for (auto it: msgs)
m_SendQueue.push_back (it);
}
else
Send (msgs);
}
void NTCPSession::ScheduleTermination ()
{
m_TerminationTimer.cancel ();
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer,
shared_from_this (), std::placeholders::_1));
}
void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "NTCP: No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds");
//Terminate ();
m_Socket.close ();// invoke Terminate () from HandleReceive
}
}
//-----------------------------------------
NTCPServer::NTCPServer (int port):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
{
}
NTCPServer::~NTCPServer ()
{
Stop ();
}
void NTCPServer::Start ()
{
if (!m_IsRunning)
{
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
// create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses ();
for (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 ())
{
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));
}
}
}
}
}
void NTCPServer::Stop ()
{
m_NTCPSessions.clear ();
if (m_IsRunning)
{
m_IsRunning = false;
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
void NTCPServer::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "NTCP: runtime exception: ", ex.what ());
}
}
}
bool NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (!session || !session->GetRemoteIdentity ()) return false;
auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_NTCPSessions.find (ident);
if (it != m_NTCPSessions.end ())
{
LogPrint (eLogWarning, "NTCP: session to ", ident.ToBase64 (), " already exists");
return false;
}
m_NTCPSessions.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<NTCPSession> >(ident, session));
return true;
}
void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
{
if (session && session->GetRemoteIdentity ())
m_NTCPSessions.erase (session->GetRemoteIdentity ()->GetIdentHash ());
}
std::shared_ptr<NTCPSession> NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident)
{
auto it = m_NTCPSessions.find (ident);
if (it != m_NTCPSessions.end ())
return it->second;
return nullptr;
}
void NTCPServer::HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
{
if (!error)
{
boost::system::error_code ec;
auto ep = conn->GetSocket ().remote_endpoint(ec);
if (!ec)
{
LogPrint (eLogDebug, "NTCP: Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
m_BanList.erase (it);
}
if (conn)
conn->ServerLogin ();
}
else
LogPrint (eLogError, "NTCP: Connected from error ", ec.message ());
}
if (error != boost::asio::error::operation_aborted)
{
conn = std::make_shared<NTCPSession> (*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
}
}
void NTCPServer::HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
{
if (!error)
{
boost::system::error_code ec;
auto ep = conn->GetSocket ().remote_endpoint(ec);
if (!ec)
{
LogPrint (eLogDebug, "NTCP: Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
m_BanList.erase (it);
}
if (conn)
conn->ServerLogin ();
}
else
LogPrint (eLogError, "NTCP: Connected from error ", ec.message ());
}
if (error != boost::asio::error::operation_aborted)
{
conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this,
conn, std::placeholders::_1));
}
}
void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn)
{
LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port);
m_Service.post([=]()
{
if (this->AddNTCPSession (conn))
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
});
}
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
{
if (ecode)
{
LogPrint (eLogError, "NTCP: Connect error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
}
else
{
LogPrint (eLogDebug, "NTCP: Connected to ", conn->GetSocket ().remote_endpoint ());
if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ());
conn->ClientLogin ();
}
}
void NTCPServer::Ban (const boost::asio::ip::address& addr)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
LogPrint (eLogWarning, "NTCP: ", addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
}
}
}

View File

@@ -1,176 +0,0 @@
#ifndef NTCP_SESSION_H__
#define NTCP_SESSION_H__
#include <inttypes.h>
#include <map>
#include <memory>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include "Crypto.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "TransportSession.h"
namespace i2p
{
namespace transport
{
struct NTCPPhase1
{
uint8_t pubKey[256];
uint8_t HXxorHI[32];
};
struct NTCPPhase2
{
uint8_t pubKey[256];
struct
{
uint8_t hxy[32];
uint8_t timestamp[4];
uint8_t filler[12];
} encrypted;
};
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
const 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
class NTCPServer;
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
{
public:
NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
~NTCPSession ();
void Terminate ();
void Done ();
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
bool IsEstablished () const { return m_IsEstablished; };
void ClientLogin ();
void ServerLogin ();
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
private:
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void Connected ();
void SendTimeSyncMessage ();
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
// client
void SendPhase3 ();
void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
//server
void SendPhase2 ();
void SendPhase4 (uint32_t tsA, uint32_t tsB);
void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen);
void HandlePhase3 (uint32_t tsB, size_t paddingLen);
void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
// common
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
bool DecryptNextBlock (const uint8_t * encrypted);
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
NTCPServer& m_Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::deadline_timer m_TerminationTimer;
bool m_IsEstablished, m_IsTerminated;
i2p::crypto::CBCDecryption m_Decryption;
i2p::crypto::CBCEncryption m_Encryption;
struct Establisher
{
NTCPPhase1 phase1;
NTCPPhase2 phase2;
} * m_Establisher;
i2p::crypto::AESAlignedBuffer<NTCP_BUFFER_SIZE + 16> m_ReceiveBuffer;
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
int m_ReceiveBufferOffset;
std::shared_ptr<I2NPMessage> m_NextMessage;
size_t m_NextMessageOffset;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsSending;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
boost::asio::ip::address m_ConnectedFrom; // for ban
};
// TODO: move to NTCP.h/.cpp
class NTCPServer
{
public:
NTCPServer (int port);
~NTCPServer ();
void Start ();
void Stop ();
bool AddNTCPSession (std::shared_ptr<NTCPSession> session);
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
boost::asio::io_service& GetService () { return m_Service; };
void Ban (const boost::asio::ip::address& addr);
private:
void Run ();
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
public:
// for HTTP/I2PControl
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
};
}
}
#endif

992
NetDb.cpp
View File

@@ -1,992 +0,0 @@
#include <string.h>
#include "I2PEndian.h"
#include <fstream>
#include <vector>
#include <boost/asio.hpp>
#include <openssl/rand.h>
#include <zlib.h>
#include "Base.h"
#include "Log.h"
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "Transports.h"
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
#include "util.h"
using namespace i2p::transport;
namespace i2p
{
namespace data
{
const char NetDb::m_NetDbPath[] = "netDb";
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr)
{
}
NetDb::~NetDb ()
{
Stop ();
delete m_Reseeder;
}
void NetDb::Start ()
{
Load ();
if (m_RouterInfos.size () < 25) // reseed if # of router less than 50
Reseed ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NetDb::Run, this));
}
void NetDb::Stop ()
{
if (m_IsRunning)
{
for (auto it: m_RouterInfos)
it.second->SaveProfile ();
DeleteObsoleteProfiles ();
m_RouterInfos.clear ();
m_Floodfills.clear ();
if (m_Thread)
{
m_IsRunning = false;
m_Queue.WakeUp ();
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
m_LeaseSets.clear();
m_Requests.Stop ();
}
}
void NetDb::Run ()
{
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0;
while (m_IsRunning)
{
try
{
auto msg = m_Queue.GetNextWithTimeout (15000); // 15 sec
if (msg)
{
int numMsgs = 0;
while (msg)
{
LogPrint(eLogDebug, "NetDb: got request with type ", (int) msg->GetTypeID ());
switch (msg->GetTypeID ())
{
case eI2NPDatabaseStore:
HandleDatabaseStoreMsg (msg);
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMsg (msg);
break;
case eI2NPDatabaseLookup:
HandleDatabaseLookupMsg (msg);
break;
default: // WTF?
LogPrint (eLogError, "NetDb: unexpected message type ", (int) msg->GetTypeID ());
//i2p::HandleI2NPMessage (msg);
}
if (numMsgs > 100) break;
msg = m_Queue.Get ();
numMsgs++;
}
}
if (!m_IsRunning) break;
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastManageRequest >= 15) // manage requests every 15 seconds
{
m_Requests.ManageRequests ();
lastManageRequest = ts;
}
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
{
if (lastSave)
{
SaveUpdated ();
ManageLeaseSets ();
}
lastSave = ts;
}
if (ts - lastPublish >= 2400) // publish every 40 minutes
{
Publish ();
lastPublish = ts;
}
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
{
auto numRouters = m_RouterInfos.size ();
if (numRouters == 0)
{
LogPrint(eLogError, "NetDb: no known routers, reseed seems to be totally failed");
break;
}
if (numRouters < 2500 || ts - lastExploratory >= 90)
{
numRouters = 800/numRouters;
if (numRouters < 1) numRouters = 1;
if (numRouters > 9) numRouters = 9;
m_Requests.ManageRequests ();
Explore (numRouters);
lastExploratory = ts;
}
}
}
catch (std::exception& ex)
{
LogPrint (eLogError, "NetDb: runtime exception: ", ex.what ());
}
}
}
void NetDb::AddRouterInfo (const uint8_t * buf, int len)
{
IdentityEx identity;
if (identity.FromBuffer (buf, len))
AddRouterInfo (identity.GetIdentHash (), buf, len);
}
void NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len)
{
auto r = FindRouter (ident);
if (r)
{
auto ts = r->GetTimestamp ();
r->Update (buf, len);
if (r->GetTimestamp () > ts)
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase32());
}
else
{
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase32());
r = std::make_shared<RouterInfo> (buf, len);
{
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
m_RouterInfos[r->GetIdentHash ()] = r;
}
if (r->IsFloodfill ())
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (r);
}
}
// take care about requested destination
m_Requests.RequestComplete (ident, r);
}
void NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
if (!from) // unsolicited LS must be received directly
{
auto it = m_LeaseSets.find(ident);
if (it != m_LeaseSets.end ())
{
it->second->Update (buf, len);
if (it->second->IsValid ())
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32());
else
{
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase32());
m_LeaseSets.erase (it);
}
}
else
{
auto leaseSet = std::make_shared<LeaseSet> (buf, len);
if (leaseSet->IsValid ())
{
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
}
else
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase32());
}
}
}
std::shared_ptr<RouterInfo> NetDb::FindRouter (const IdentHash& ident) const
{
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
auto it = m_RouterInfos.find (ident);
if (it != m_RouterInfos.end ())
return it->second;
else
return nullptr;
}
std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const
{
auto it = m_LeaseSets.find (destination);
if (it != m_LeaseSets.end ())
return it->second;
else
return nullptr;
}
std::shared_ptr<RouterProfile> NetDb::FindRouterProfile (const IdentHash& ident) const
{
auto router = FindRouter (ident);
return router ? router->GetProfile () : nullptr;
}
void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable)
{
auto it = m_RouterInfos.find (ident);
if (it != m_RouterInfos.end ())
return it->second->SetUnreachable (unreachable);
}
// TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.)
bool NetDb::CreateNetDb(boost::filesystem::path directory)
{
LogPrint (eLogInfo, "NetDb: storage directory doesn't exist, trying to create it.");
if (!boost::filesystem::create_directory (directory))
{
LogPrint (eLogError, "NetDb: failed to create directory ", directory);
return false;
}
// list of chars might appear in base64 string
const char * chars = GetBase64SubstitutionTable (); // 64 bytes
for (int i = 0; i < 64; i++)
{
auto p = directory / (std::string ("r") + chars[i]);
if (!boost::filesystem::exists (p) && !boost::filesystem::create_directory (p))
{
LogPrint (eLogError, "NetDb: failed to create directory ", p);
return false;
}
}
return true;
}
void NetDb::Reseed ()
{
if (!m_Reseeder)
{
m_Reseeder = new Reseeder ();
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
}
int reseedRetries = 0;
while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ())
reseedRetries++;
if (reseedRetries >= 10)
LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts");
}
void NetDb::Load ()
{
boost::filesystem::path p(i2p::util::filesystem::GetDataDir() / m_NetDbPath);
if (!boost::filesystem::exists (p))
{
// seems netDb doesn't exist yet
if (!CreateNetDb(p)) return;
}
// make sure we cleanup netDb from previous attempts
m_RouterInfos.clear ();
m_Floodfills.clear ();
// load routers now
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
int numRouters = 0;
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
{
if (boost::filesystem::is_directory (it->status()))
{
for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1)
{
#if BOOST_VERSION > 10500
const std::string& fullPath = it1->path().string();
#else
const std::string& fullPath = it1->path();
#endif
auto r = std::make_shared<RouterInfo>(fullPath);
if (!r->IsUnreachable () && (!r->UsesIntroducer () || ts < r->GetTimestamp () + 3600*1000LL)) // 1 hour
{
r->DeleteBuffer ();
r->ClearProperties (); // properties are not used for regular routers
m_RouterInfos[r->GetIdentHash ()] = r;
if (r->IsFloodfill ())
m_Floodfills.push_back (r);
numRouters++;
}
else
{
if (boost::filesystem::exists (fullPath))
boost::filesystem::remove (fullPath);
}
}
}
}
LogPrint (eLogInfo, "NetDb: ", numRouters, " routers loaded (", m_Floodfills.size (), " floodfils)");
}
void NetDb::SaveUpdated ()
{
auto GetFilePath = [](const boost::filesystem::path& directory, const RouterInfo * routerInfo)
{
std::string s(routerInfo->GetIdentHashBase64());
return directory / (std::string("r") + s[0]) / ("routerInfo-" + s + ".dat");
};
boost::filesystem::path fullDirectory (i2p::util::filesystem::GetDataDir() / m_NetDbPath);
int count = 0, deletedCount = 0;
auto total = m_RouterInfos.size ();
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_RouterInfos)
{
if (it.second->IsUpdated ())
{
std::string f = GetFilePath(fullDirectory, it.second.get()).string();
it.second->SaveToFile (f);
it.second->SetUpdated (false);
it.second->SetUnreachable (false);
it.second->DeleteBuffer ();
count++;
}
else
{
// RouterInfo expires after 1 hour if uses introducer
if (it.second->UsesIntroducer () && ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hour
it.second->SetUnreachable (true);
else if (total > 75 && ts > (i2p::context.GetStartupTime () + 600)*1000LL) // routers don't expire if less than 25 or uptime is less than 10 minutes
{
if (i2p::context.IsFloodfill ())
{
if (ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hours
{
it.second->SetUnreachable (true);
total--;
}
}
else if (total > 300)
{
if (ts > it.second->GetTimestamp () + 30*3600*1000LL) // 30 hours
{
it.second->SetUnreachable (true);
total--;
}
}
else if (total > 120)
{
if (ts > it.second->GetTimestamp () + 72*3600*1000LL) // 72 hours
{
it.second->SetUnreachable (true);
total--;
}
}
}
if (it.second->IsUnreachable ())
{
total--;
// delete RI file
if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second.get ())))
{
boost::filesystem::remove (GetFilePath (fullDirectory, it.second.get ()));
deletedCount++;
}
// delete from floodfills list
if (it.second->IsFloodfill ())
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.remove (it.second);
}
}
}
}
if (count > 0)
LogPrint (eLogInfo, "NetDb: ", count, " new/updated routers saved");
if (deletedCount > 0)
{
LogPrint (eLogDebug, "NetDb: ", deletedCount, " routers deleted");
// clean up RouterInfos table
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();)
{
if (it->second->IsUnreachable ())
{
it->second->SaveProfile ();
it = m_RouterInfos.erase (it);
}
else
it++;
}
}
}
void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete)
{
auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: destination ", destination.ToBase32(), " is requested already");
return;
}
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
else
{
LogPrint (eLogError, "NetDb: ", destination.ToBase32(), " destination requested, but no floodfills found");
m_Requests.RequestComplete (destination, nullptr);
}
}
void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m)
{
const uint8_t * buf = m->GetPayload ();
size_t len = m->GetSize ();
IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET);
if (ident.IsZero ())
{
LogPrint (eLogError, "NetDb: database store with zero ident, dropped");
return;
}
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
auto deliveryStatus = CreateDeliveryStatusMsg (replyToken);
uint32_t tunnelID = bufbe32toh (buf + offset);
offset += 4;
if (!tunnelID) // send response directly
transports.SendMessage (buf + offset, deliveryStatus);
else
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr;
if (outbound)
outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus);
else
LogPrint (eLogError, "NetDb: no outbound tunnels for DatabaseStore reply found");
}
offset += 32;
if (context.IsFloodfill ())
{
// flood it
auto floodMsg = NewI2NPShortMessage ();
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 - offset;
floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen;
if (floodMsg->len < floodMsg->maxLen)
{
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + offset, msgLen);
floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore);
std::set<IdentHash> excluded;
for (int i = 0; i < 3; i++)
{
auto floodfill = GetClosestFloodfill (ident, excluded);
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), floodMsg);
}
}
else
LogPrint (eLogError, "Database store message is too long ", floodMsg->len);
}
}
if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
{
LogPrint (eLogDebug, "NetDb: store request: LeaseSet");
AddLeaseSet (ident, buf + offset, len - offset, m->from);
}
else
{
LogPrint (eLogDebug, "NetDb: store request: RouterInfo");
size_t size = bufbe16toh (buf + offset);
offset += 2;
if (size > 2048 || size > len - offset)
{
LogPrint (eLogError, "NetDb: invalid RouterInfo length ", (int)size);
return;
}
uint8_t uncompressed[2048];
size_t uncompressedSize = m_Inflator.Inflate (buf + offset, size, uncompressed, 2048);
if (uncompressedSize)
AddRouterInfo (ident, uncompressed, uncompressedSize);
}
}
void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
int num = buf[32]; // num
LogPrint (eLogDebug, "NetDb: DatabaseSearchReply for ", key, " num=", num);
IdentHash ident (buf);
auto dest = m_Requests.FindRequest (ident);
if (dest)
{
bool deleteDest = true;
if (num > 0)
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr;
auto inbound = pool ? pool->GetNextInboundTunnel () : nullptr;
if (!dest->IsExploratory ())
{
// reply to our destination. Try other floodfills
if (outbound && inbound )
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto count = dest->GetExcludedPeers ().size ();
if (count < 7)
{
auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
if (nextFloodfill)
{
// tell floodfill about us
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg ()
});
// request destination
LogPrint (eLogDebug, "NetDb: Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ());
auto msg = dest->CreateRequestMessage (nextFloodfill, inbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0, msg
});
deleteDest = false;
}
}
else
LogPrint (eLogWarning, "NetDb: ", key, " was not found on ", count, " floodfills");
if (msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
}
}
if (deleteDest)
// no more requests for the destinationation. delete it
m_Requests.RequestComplete (ident, nullptr);
}
else
// no more requests for detination possible. delete it
m_Requests.RequestComplete (ident, nullptr);
}
else
LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found");
// try responses
for (int i = 0; i < num; i++)
{
const uint8_t * router = buf + 33 + i*32;
char peerHash[48];
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
peerHash[l1] = 0;
LogPrint (eLogDebug, "NetDb: ", i, ": ", peerHash);
auto r = FindRouter (router);
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
{
// router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ...");
RequestDestination (router);
}
else
LogPrint (eLogDebug, "NetDb: [:|||:]");
}
}
void NetDb::HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
IdentHash ident (buf);
if (ident.IsZero ())
{
LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored");
return;
}
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
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);
excluded += 4;
}
uint16_t numExcluded = bufbe16toh (excluded);
excluded += 2;
if (numExcluded > 512)
{
LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512");
numExcluded = 0; // TODO:
}
std::shared_ptr<I2NPMessage> replyMsg;
if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP)
{
LogPrint (eLogInfo, "NetDb: exploratory close to ", key, " ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 32;
}
std::vector<IdentHash> routers;
for (int i = 0; i < 3; i++)
{
auto r = GetClosestNonFloodfill (ident, excludedRouters);
if (r)
{
routers.push_back (r->GetIdentHash ());
excludedRouters.insert (r->GetIdentHash ());
}
}
replyMsg = CreateDatabaseSearchReply (ident, routers);
}
else
{
if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP ||
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)
{
auto router = FindRouter (ident);
if (router)
{
LogPrint (eLogDebug, "NetDb: requested RouterInfo ", key, " found");
router->LoadBuffer ();
if (router->GetBuffer ())
replyMsg = CreateDatabaseStoreMsg (router);
}
}
if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP ||
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP))
{
auto leaseSet = FindLeaseSet (ident);
if (leaseSet) // we don't send back our LeaseSets
{
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
}
}
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;
}
replyMsg = CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters));
}
}
if (replyMsg)
{
if (replyTunnelID)
{
// encryption might be used though tunnel only
if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested
{
const uint8_t * sessionKey = excluded;
uint8_t numTags = sessionKey[32];
if (numTags > 0)
{
const uint8_t * sessionTag = sessionKey + 33; // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
}
}
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
if (outbound)
outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
else
transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
}
else
transports.SendMessage (buf+32, replyMsg);
}
}
void NetDb::Explore (int numDestinations)
{
// new requests
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
bool throughTunnels = outbound && inbound;
uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
std::set<const RouterInfo *> floodfills;
LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++)
{
RAND_bytes (randomHash, 32);
auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: exploratory destination is requested already");
return;
}
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
{
floodfills.insert (floodfill.get ());
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
throughTunnels = false;
if (throughTunnels)
{
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg () // tell floodfill about us
});
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound) // explore
});
}
else
i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
else
m_Requests.RequestComplete (randomHash, nullptr);
}
if (throughTunnels && msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
}
void NetDb::Publish ()
{
i2p::context.UpdateStats (); // for floodfill
std::set<IdentHash> excluded; // TODO: fill up later
for (int i = 0; i < 2; i++)
{
auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded);
if (floodfill)
{
uint32_t replyToken;
RAND_bytes ((uint8_t *)&replyToken, 4);
LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
excluded.insert (floodfill->GetIdentHash ());
}
}
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter () const
{
return GetRandomRouter (
[](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden ();
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const
{
return GetRandomRouter (
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router != compatibleWith &&
router->IsCompatible (*compatibleWith);
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter () const
{
return GetRandomRouter (
[](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsPeerTesting ();
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomIntroducer () const
{
return GetRandomRouter (
[](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsIntroducer ();
});
}
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const
{
return GetRandomRouter (
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router != compatibleWith &&
router->IsCompatible (*compatibleWith) &&
(router->GetCaps () & RouterInfo::eHighBandwidth);
});
}
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 ();
for (int j = 0; j < 2; j++)
{
uint32_t i = 0;
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
for (auto it: m_RouterInfos)
{
if (i >= ind)
{
if (!it.second->IsUnreachable () && filter (it.second))
return it.second;
}
else
i++;
}
// we couldn't find anything, try second pass
ind = 0;
}
return nullptr; // seems we have too few routers
}
void NetDb::PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg)
{
if (msg) m_Queue.Put (msg);
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
{
std::shared_ptr<const RouterInfo> r;
XORMetric minMetric;
IdentHash destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
XORMetric m = destKey ^ it->GetIdentHash ();
if (m < minMetric && !excluded.count (it->GetIdentHash ()))
{
minMetric = m;
r = it;
}
}
}
return r;
}
std::vector<IdentHash> NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded) const
{
struct Sorted
{
std::shared_ptr<const RouterInfo> r;
XORMetric metric;
bool operator< (const Sorted& other) const { return metric < other.metric; };
};
std::set<Sorted> sorted;
IdentHash destKey = CreateRoutingKey (destination);
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
XORMetric m = destKey ^ it->GetIdentHash ();
if (sorted.size () < num)
sorted.insert ({it, m});
else if (m < sorted.rbegin ()->metric)
{
sorted.insert ({it, m});
sorted.erase (std::prev (sorted.end ()));
}
}
}
}
std::vector<IdentHash> res;
size_t i = 0;
for (auto it: sorted)
{
if (i < num)
{
auto& ident = it.r->GetIdentHash ();
if (!excluded.count (ident))
{
res.push_back (ident);
i++;
}
}
else
break;
}
return res;
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
{
std::shared_ptr<const RouterInfo> r;
XORMetric minMetric;
IdentHash destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
// must be called from NetDb thread only
for (auto it: m_RouterInfos)
{
if (!it.second->IsFloodfill ())
{
XORMetric m = destKey ^ it.first;
if (m < minMetric && !excluded.count (it.first))
{
minMetric = m;
r = it.second;
}
}
}
return r;
}
void NetDb::ManageLeaseSets ()
{
for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();)
{
if (!it->second->HasNonExpiredLeases ()) // all leases expired
{
LogPrint (eLogWarning, "NetDb: LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_LeaseSets.erase (it);
}
else
it++;
}
}
}
}

109
NetDb.h
View File

@@ -1,109 +0,0 @@
#ifndef NETDB_H__
#define NETDB_H__
#include <inttypes.h>
#include <set>
#include <map>
#include <list>
#include <string>
#include <thread>
#include <mutex>
#include <boost/filesystem.hpp>
#include "Base.h"
#include "Queue.h"
#include "I2NPProtocol.h"
#include "RouterInfo.h"
#include "LeaseSet.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Reseed.h"
#include "NetDbRequests.h"
namespace i2p
{
namespace data
{
class NetDb
{
public:
NetDb ();
~NetDb ();
void Start ();
void Stop ();
void AddRouterInfo (const uint8_t * buf, int len);
void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter () const;
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
void Reseed ();
// for web interface
int GetNumRouters () const { return m_RouterInfos.size (); };
int GetNumFloodfills () const { return m_Floodfills.size (); };
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
private:
bool CreateNetDb(boost::filesystem::path directory);
void Load ();
void SaveUpdated ();
void Run (); // exploratory thread
void Explore (int numDestinations);
void Publish ();
void ManageLeaseSets ();
void ManageRequests ();
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
private:
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex;
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
mutable std::mutex m_FloodfillsMutex;
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
bool m_IsRunning;
std::thread * m_Thread;
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
GzipInflator m_Inflator;
Reseeder * m_Reseeder;
friend class NetDbRequests;
NetDbRequests m_Requests;
static const char m_NetDbPath[];
};
extern NetDb netdb;
}
}
#endif

View File

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

View File

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

View File

@@ -1,214 +0,0 @@
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "Base.h"
#include "util.h"
#include "Log.h"
#include "Profiling.h"
namespace i2p
{
namespace data
{
RouterProfile::RouterProfile (const IdentHash& identHash):
m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0)
{
}
boost::posix_time::ptime RouterProfile::GetTime () const
{
return boost::posix_time::second_clock::local_time();
}
void RouterProfile::UpdateTime ()
{
m_LastUpdateTime = GetTime ();
}
void RouterProfile::Save ()
{
// fill sections
boost::property_tree::ptree participation;
participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
boost::property_tree::ptree usage;
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
// fill property tree
boost::property_tree::ptree pt;
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
// save to file
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
if (!boost::filesystem::exists (path))
{
// Create directory is necessary
if (!boost::filesystem::create_directory (path))
{
LogPrint (eLogError, "Failed to create directory ", path);
return;
}
const char * chars = GetBase64SubstitutionTable (); // 64 bytes
for (int i = 0; i < 64; i++)
{
auto path1 = path / (std::string ("p") + chars[i]);
if (!boost::filesystem::exists (path1) && !boost::filesystem::create_directory (path1))
{
LogPrint (eLogError, "Failed to create directory ", path1);
return;
}
}
}
std::string base64 = m_IdentHash.ToBase64 ();
path = path / (std::string ("p") + base64[0]);
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
try
{
boost::property_tree::write_ini (filename.string (), pt);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ());
}
}
void RouterProfile::Load ()
{
std::string base64 = m_IdentHash.ToBase64 ();
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
path /= std::string ("p") + base64[0];
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
if (boost::filesystem::exists (filename))
{
boost::property_tree::ptree pt;
try
{
boost::property_tree::read_ini (filename.string (), pt);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ());
return;
}
try
{
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (t.length () > 0)
m_LastUpdateTime = boost::posix_time::time_from_string (t);
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT)
{
try
{
// read participations
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION);
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0);
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0);
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_PARTICIPATION);
}
try
{
// read usage
auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE);
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE);
}
}
else
*this = RouterProfile (m_IdentHash);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read profile ", base64, " :", ex.what ());
}
}
}
void RouterProfile::TunnelBuildResponse (uint8_t ret)
{
UpdateTime ();
if (ret > 0)
m_NumTunnelsDeclined++;
else
m_NumTunnelsAgreed++;
}
void RouterProfile::TunnelNonReplied ()
{
m_NumTunnelsNonReplied++;
UpdateTime ();
}
bool RouterProfile::IsLowPartcipationRate () const
{
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
}
bool RouterProfile::IsLowReplyRate () const
{
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
return m_NumTunnelsNonReplied > 10*(total + 1);
}
bool RouterProfile::IsBad ()
{
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{
// reset profile
m_NumTunnelsAgreed = 0;
m_NumTunnelsDeclined = 0;
m_NumTunnelsNonReplied = 0;
isBad = false;
}
if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++;
return isBad;
}
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
{
auto profile = std::make_shared<RouterProfile> (identHash);
profile->Load (); // if possible
return profile;
}
void DeleteObsoleteProfiles ()
{
int num = 0;
auto ts = boost::posix_time::second_clock::local_time();
boost::filesystem::path p (i2p::util::filesystem::GetDataDir()/PEER_PROFILES_DIRECTORY);
if (boost::filesystem::exists (p))
{
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
{
if (boost::filesystem::is_directory (it->status()))
{
for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1)
{
auto lastModified = boost::posix_time::from_time_t (boost::filesystem::last_write_time (it1->path ()));
if ((ts - lastModified).hours () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
{
boost::filesystem::remove (it1->path ());
num++;
}
}
}
}
}
LogPrint (eLogInfo, num, " obsolete profiles deleted");
}
}
}

View File

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

169
Queue.h
View File

@@ -1,169 +0,0 @@
#ifndef QUEUE_H__
#define QUEUE_H__
#include <queue>
#include <vector>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <functional>
namespace i2p
{
namespace util
{
template<typename Element>
class Queue
{
public:
void Put (Element e)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_Queue.push (e);
m_NonEmpty.notify_one ();
}
void Put (const std::vector<Element>& vec)
{
if (!vec.empty ())
{
std::unique_lock<std::mutex> l(m_QueueMutex);
for (auto it: vec)
m_Queue.push (it);
m_NonEmpty.notify_one ();
}
}
Element GetNext ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait (l);
el = GetNonThreadSafe ();
}
return el;
}
Element GetNextWithTimeout (int usec)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec));
el = GetNonThreadSafe ();
}
return el;
}
void Wait ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_NonEmpty.wait (l);
}
bool Wait (int sec, int usec)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_NonEmpty.wait_for (l, std::chrono::seconds (sec) + std::chrono::milliseconds (usec)) != std::cv_status::timeout;
}
bool IsEmpty ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.empty ();
}
int GetSize ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.size ();
}
void WakeUp () { m_NonEmpty.notify_all (); };
Element Get ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe ();
}
Element Peek ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe (true);
}
private:
Element GetNonThreadSafe (bool peek = false)
{
if (!m_Queue.empty ())
{
auto el = m_Queue.front ();
if (!peek)
m_Queue.pop ();
return el;
}
return nullptr;
}
private:
std::queue<Element> m_Queue;
std::mutex m_QueueMutex;
std::condition_variable m_NonEmpty;
};
template<class Msg>
class MsgQueue: public Queue<Msg *>
{
public:
typedef std::function<void()> OnEmpty;
MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) {};
~MsgQueue () { Stop (); };
void Stop()
{
if (m_IsRunning)
{
m_IsRunning = false;
Queue<Msg *>::WakeUp ();
m_Thread.join();
}
}
void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; };
private:
void Run ()
{
while (m_IsRunning)
{
while (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;
};
}
}
#endif

View File

@@ -1,41 +1,21 @@
i2pd
====
I2P router written in C++
Contains all ongoing changes from https://bitbucket.org/orignal/i2pd/src
I2P router written in C++.
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
Documentation for config files, command line options, build instructions and more can be found under the doc directory.
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 - Proved working.
* Mac OS X - Not well tested. (Only works with clang, not GCC)
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
Build Statuses
---------------
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/)
- Linux x64 - Maintenance
- Linux ARM - Maintenance
- Mac OS X - Maintenance
- Microsoft VC13 - To be added

View File

@@ -1,415 +0,0 @@
#include <string.h>
#include <fstream>
#include <sstream>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/bn.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <zlib.h>
#include "I2PEndian.h"
#include "Reseed.h"
#include "Log.h"
#include "Identity.h"
#include "Crypto.h"
#include "NetDb.h"
#include "util.h"
namespace i2p
{
namespace data
{
static std::vector<std::string> httpsReseedHostList =
{
"https://reseed.i2p-projekt.de/", // Only HTTPS
"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v3) support
"https://i2p.mooo.com/netDb/",
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
"https://us.reseed.i2p2.no:444/",
"https://uk.reseed.i2p2.no:444/",
"https://www.torontocrypto.org:8443/"
"https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
"https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
"https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v3) support
};
Reseeder::Reseeder()
{
}
Reseeder::~Reseeder()
{
}
int Reseeder::ReseedNowSU3 ()
{
auto ind = rand () % httpsReseedHostList.size ();
std::string& reseedHost = httpsReseedHostList[ind];
return ReseedFromSU3 (reseedHost);
}
int Reseeder::ReseedFromSU3 (const std::string& host)
{
std::string url = host + "i2pseeds.su3";
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
std::string su3 = HttpsRequest (url);
if (su3.length () > 0)
{
std::stringstream s(su3);
return ProcessSU3Stream (s);
}
else
{
LogPrint (eLogWarning, "Reseed: SU3 download failed");
return 0;
}
}
int Reseeder::ProcessSU3File (const char * filename)
{
std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ())
return ProcessSU3Stream (s);
else
{
LogPrint (eLogError, "Reseed: Can't open file ", filename);
return 0;
}
}
const char SU3_MAGIC_NUMBER[]="I2Psu3";
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008;
int Reseeder::ProcessSU3Stream (std::istream& s)
{
char magicNumber[7];
s.read (magicNumber, 7); // magic number and zero byte 6
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
{
LogPrint (eLogError, "Reseed: Unexpected SU3 magic number");
return 0;
}
s.seekg (1, std::ios::cur); // su3 file format version
SigningKeyType signatureType;
s.read ((char *)&signatureType, 2); // signature type
signatureType = be16toh (signatureType);
uint16_t signatureLength;
s.read ((char *)&signatureLength, 2); // signature length
signatureLength = be16toh (signatureLength);
s.seekg (1, std::ios::cur); // unused
uint8_t versionLength;
s.read ((char *)&versionLength, 1); // version length
s.seekg (1, std::ios::cur); // unused
uint8_t signerIDLength;
s.read ((char *)&signerIDLength, 1); // signer ID length
uint64_t contentLength;
s.read ((char *)&contentLength, 8); // content length
contentLength = be64toh (contentLength);
s.seekg (1, std::ios::cur); // unused
uint8_t fileType;
s.read ((char *)&fileType, 1); // file type
if (fileType != 0x00) // zip file
{
LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType);
return 0;
}
s.seekg (1, std::ios::cur); // unused
uint8_t contentType;
s.read ((char *)&contentType, 1); // content type
if (contentType != 0x03) // reseed data
{
LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType);
return 0;
}
s.seekg (12, std::ios::cur); // unused
s.seekg (versionLength, std::ios::cur); // skip version
char signerID[256];
s.read (signerID, signerIDLength); // signerID
signerID[signerIDLength] = 0;
//try to verify signature
auto it = m_SigningKeys.find (signerID);
if (it != m_SigningKeys.end ())
{
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
{
size_t pos = s.tellg ();
size_t tbsLen = pos + contentLength;
uint8_t * tbs = new uint8_t[tbsLen];
s.seekg (0, std::ios::beg);
s.read ((char *)tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength);
// RSA-raw
{
// calculate digest
uint8_t digest[64];
SHA512 (tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
}
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
}
else
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
// handle content
int numFiles = 0;
size_t contentPos = s.tellg ();
while (!s.eof ())
{
uint32_t signature;
s.read ((char *)&signature, 4);
signature = le32toh (signature);
if (signature == ZIP_HEADER_SIGNATURE)
{
// next local file
s.seekg (2, std::ios::cur); // version
uint16_t bitFlag;
s.read ((char *)&bitFlag, 2);
bitFlag = le16toh (bitFlag);
uint16_t compressionMethod;
s.read ((char *)&compressionMethod, 2);
compressionMethod = le16toh (compressionMethod);
s.seekg (4, std::ios::cur); // skip fields we don't care about
uint32_t compressedSize, uncompressedSize;
uint32_t crc_32;
s.read ((char *)&crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize);
s.read ((char *)&uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize);
uint16_t fileNameLength, extraFieldLength;
s.read ((char *)&fileNameLength, 2);
fileNameLength = le16toh (fileNameLength);
s.read ((char *)&extraFieldLength, 2);
extraFieldLength = le16toh (extraFieldLength);
char localFileName[255];
s.read (localFileName, fileNameLength);
localFileName[fileNameLength] = 0;
s.seekg (extraFieldLength, std::ios::cur);
// take care about data desriptor if presented
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
{
size_t pos = s.tellg ();
if (!FindZipDataDescriptor (s))
{
LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found");
return numFiles;
}
s.read ((char *)&crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
s.read ((char *)&uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize);
// now we know compressed and uncompressed size
s.seekg (pos, std::ios::beg); // back to compressed data
}
LogPrint (eLogDebug, "Reseed: Proccessing file ", localFileName, " ", compressedSize, " bytes");
if (!compressedSize)
{
LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped");
continue;
}
uint8_t * compressed = new uint8_t[compressedSize];
s.read ((char *)compressed, compressedSize);
if (compressionMethod) // we assume Deflate
{
z_stream inflator;
memset (&inflator, 0, sizeof (inflator));
inflateInit2 (&inflator, -MAX_WBITS); // no zlib header
uint8_t * uncompressed = new uint8_t[uncompressedSize];
inflator.next_in = compressed;
inflator.avail_in = compressedSize;
inflator.next_out = uncompressed;
inflator.avail_out = uncompressedSize;
int err;
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0)
{
uncompressedSize -= inflator.avail_out;
if (crc32 (0, uncompressed, uncompressedSize) == crc_32)
{
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
numFiles++;
}
else
LogPrint (eLogError, "Reseed: CRC32 verification failed");
}
else
LogPrint (eLogError, "Reseed: SU3 decompression error ", err);
delete[] uncompressed;
inflateEnd (&inflator);
}
else // no compression
{
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
numFiles++;
}
delete[] compressed;
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
}
else
{
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
LogPrint (eLogWarning, "Reseed: Missing zip central directory header");
break; // no more files
}
size_t end = s.tellg ();
if (end - contentPos >= contentLength)
break; // we are beyond contentLength
}
return numFiles;
}
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 };
bool Reseeder::FindZipDataDescriptor (std::istream& s)
{
size_t nextInd = 0;
while (!s.eof ())
{
uint8_t nextByte;
s.read ((char *)&nextByte, 1);
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd])
{
nextInd++;
if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE))
return true;
}
else
nextInd = 0;
}
return false;
}
void Reseeder::LoadCertificate (const std::string& filename)
{
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
SSL * ssl = SSL_new (ctx);
X509 * cert = SSL_get_certificate (ssl);
// verify
if (cert)
{
// extract issuer name
char name[100];
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
// extract RSA key (we need n only, e = 65537)
RSA * key = X509_get_pubkey (cert)->pkey.rsa;
PublicKey value;
i2p::crypto::bn2buf (key->n, value, 512);
m_SigningKeys[name] = value;
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Reseed: Can't open certificate file ", filename);
SSL_CTX_free (ctx);
}
void Reseeder::LoadCertificates ()
{
boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed";
if (!boost::filesystem::exists (reseedDir))
{
LogPrint (eLogWarning, "Reseed: certificates not loaded, ", reseedDir, " doesn't exist");
return;
}
int numCertificates = 0;
boost::filesystem::directory_iterator end; // empty
for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it)
{
if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt")
{
LoadCertificate (it->path ().string ());
numCertificates++;
}
}
LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded");
}
std::string Reseeder::HttpsRequest (const std::string& address)
{
i2p::util::http::url u(address);
if (u.port_ == 80) u.port_ = 443;
boost::asio::io_service service;
boost::system::error_code ecode;
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode);
if (!ecode)
{
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> s(service, ctx);
s.lowest_layer().connect (*it, ecode);
if (!ecode)
{
s.handshake (boost::asio::ssl::stream_base::client, ecode);
if (!ecode)
{
LogPrint (eLogInfo, "Reseed: Connected to ", u.host_, ":", u.port_);
// send request
std::stringstream ss;
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
s.write_some (boost::asio::buffer (ss.str ()));
// read response
std::stringstream rs;
char response[1024]; size_t l = 0;
do
{
l = s.read_some (boost::asio::buffer (response, 1024), ecode);
if (l) rs.write (response, l);
}
while (!ecode && l);
// process response
return i2p::util::http::GetHttpContent (rs);
}
else
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to ", u.host_, ": ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't resolve address ", u.host_, ": ", ecode.message ());
return "";
}
}
}

View File

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

View File

@@ -1,362 +0,0 @@
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "Crypto.h"
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "NetDb.h"
#include "util.h"
#include "version.h"
#include "Log.h"
#include "RouterContext.h"
namespace i2p
{
RouterContext context;
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_StartupTime (0), m_Status (eRouterStatusOK )
{
}
void RouterContext::Init ()
{
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
if (!Load ())
CreateNewRouter ();
UpdateRouterInfo ();
}
void RouterContext::CreateNewRouter ()
{
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
#else
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
#endif
SaveKeys ();
NewRouterInfo ();
}
void RouterContext::NewRouterInfo ()
{
i2p::data::RouterInfo routerInfo;
routerInfo.SetRouterIdentity (GetIdentity ());
int port = i2p::util::config::GetArg("-port", 0);
if (!port)
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
routerInfo.AddSSUAddress (i2p::util::config::GetArg("-host", "127.0.0.1").c_str (), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (i2p::util::config::GetArg("-host", "127.0.0.1").c_str (), port);
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
routerInfo.SetProperty ("coreVersion", I2P_VERSION);
routerInfo.SetProperty ("netId", "2");
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.SetProperty ("stat_uptime", "90m");
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
}
void RouterContext::UpdateRouterInfo ()
{
m_RouterInfo.CreateBuffer (m_Keys);
m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO));
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
}
void RouterContext::SetStatus (RouterStatus status)
{
if (status != m_Status)
{
m_Status = status;
switch (m_Status)
{
case eRouterStatusOK:
SetReachable ();
break;
case eRouterStatusFirewalled:
SetUnreachable ();
break;
default:
;
}
}
}
void RouterContext::UpdatePort (int port)
{
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address.port != port)
{
address.port = port;
updated = true;
}
}
if (updated)
UpdateRouterInfo ();
}
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
{
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address.host != host && address.IsCompatible (host))
{
address.host = host;
updated = true;
}
}
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
UpdateRouterInfo ();
}
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
{
bool ret = m_RouterInfo.AddIntroducer (introducer);
if (ret)
UpdateRouterInfo ();
return ret;
}
void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
{
if (m_RouterInfo.RemoveIntroducer (e))
UpdateRouterInfo ();
}
void RouterContext::SetFloodfill (bool floodfill)
{
m_IsFloodfill = floodfill;
if (floodfill)
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
else
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
// we don't publish number of routers and leaseset for non-floodfill
m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_LEASESETS);
m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_ROUTERS);
}
UpdateRouterInfo ();
}
void RouterContext::SetHighBandwidth ()
{
if (!m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
{
m_RouterInfo.SetCaps ((m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth) & ~i2p::data::RouterInfo::eExtraBandwidth);
UpdateRouterInfo ();
}
}
void RouterContext::SetLowBandwidth ()
{
if (m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth & ~i2p::data::RouterInfo::eExtraBandwidth);
UpdateRouterInfo ();
}
}
void RouterContext::SetExtraBandwidth ()
{
if (!m_RouterInfo.IsExtraBandwidth () || !m_RouterInfo.IsHighBandwidth ())
{
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eExtraBandwidth | i2p::data::RouterInfo::eHighBandwidth);
UpdateRouterInfo ();
}
}
bool RouterContext::IsUnreachable () const
{
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
}
void RouterContext::SetUnreachable ()
{
// set caps
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++)
{
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
addresses.erase (addresses.begin () + i);
break;
}
}
// delete previous introducers
for (auto& addr : addresses)
addr.introducers.clear ();
// update
UpdateRouterInfo ();
}
void RouterContext::SetReachable ()
{
// update caps
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eUnreachable;
caps |= i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eSSUIntroducer;
if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps);
// insert NTCP back
auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++)
{
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU)
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port);
break;
}
}
// delete previous introducers
for (auto& addr : addresses)
addr.introducers.clear ();
// update
UpdateRouterInfo ();
}
void RouterContext::SetSupportsV6 (bool supportsV6)
{
if (supportsV6)
m_RouterInfo.EnableV6 ();
else
m_RouterInfo.DisableV6 ();
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)
{
if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
if (addr.host != host)
{
addr.host = host;
updated = true;
}
found = true;
}
else
port = addr.port;
}
if (!found)
{
// create new address
m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port);
auto mtu = i2p::util::net::GetMTU (host);
if (mtu)
{
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
if (mtu > 1472) { // TODO: magic constant
mtu = 1472;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
}
}
m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO
updated = true;
}
if (updated)
UpdateRouterInfo ();
}
void RouterContext::UpdateStats ()
{
if (m_IsFloodfill)
{
// update routers and leasesets
m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumLeaseSets ()));
m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ()));
UpdateRouterInfo ();
}
}
bool RouterContext::Load ()
{
std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in);
if (!fk.is_open ()) return false;
fk.seekg (0, std::ios::end);
size_t len = fk.tellg();
fk.seekg (0, std::ios::beg);
if (len == sizeof (i2p::data::Keys)) // old keys file format
{
i2p::data::Keys keys;
fk.read ((char *)&keys, sizeof (keys));
m_Keys = keys;
}
else // new keys file format
{
uint8_t * buf = new uint8_t[len];
fk.read ((char *)buf, len);
m_Keys.FromBuffer (buf, len);
delete[] buf;
}
i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (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);
if (IsUnreachable ())
SetReachable (); // we assume reachable until we discover firewall through peer tests
return true;
}
void RouterContext::SaveKeys ()
{
// save in the same format as .dat files
std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out);
size_t len = m_Keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
m_Keys.ToBuffer (buf, len);
fk.write ((char *)buf, len);
delete[] buf;
}
std::shared_ptr<i2p::tunnel::TunnelPool> RouterContext::GetTunnelPool () const
{
return i2p::tunnel::tunnels.GetExploratoryPool ();
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
}
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
uint32_t RouterContext::GetUptime () const
{
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
}
}

View File

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

View File

@@ -1,685 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "I2PEndian.h"
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "Crypto.h"
#include "Base.h"
#include "Timestamp.h"
#include "Log.h"
#include "RouterInfo.h"
namespace i2p
{
namespace data
{
RouterInfo::RouterInfo (const std::string& fullPath):
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0), m_Caps (0)
{
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
ReadFromFile ();
}
RouterInfo::RouterInfo (const uint8_t * buf, int len):
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
{
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer (true);
}
RouterInfo::~RouterInfo ()
{
delete[] m_Buffer;
}
void RouterInfo::Update (const uint8_t * buf, int len)
{
// verify signature since we have indentity already
int l = len - m_RouterIdentity->GetSignatureLen ();
if (m_RouterIdentity->Verify (buf, l, buf + l))
{
// clean up
m_IsUpdated = true;
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
m_Properties.clear ();
// copy buffer
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
// skip identity
size_t identityLen = m_RouterIdentity->GetFullLen ();
// read new RI
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
// don't delete buffer until saved to the file
}
else
{
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
}
}
void RouterInfo::SetRouterIdentity (std::shared_ptr<const IdentityEx> identity)
{
m_RouterIdentity = identity;
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
}
bool RouterInfo::LoadFile ()
{
std::ifstream s(m_FullPath.c_str (), std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0,std::ios::end);
m_BufferLen = s.tellg ();
if (m_BufferLen < 40)
{
LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed");
return false;
}
s.seekg(0, std::ios::beg);
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
s.read((char *)m_Buffer, m_BufferLen);
}
else
{
LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath);
return false;
}
return true;
}
void RouterInfo::ReadFromFile ()
{
if (LoadFile ())
ReadFromBuffer (false);
}
void RouterInfo::ReadFromBuffer (bool verifySignature)
{
m_RouterIdentity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t identityLen = m_RouterIdentity->GetFullLen ();
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
if (verifySignature)
{
// verify signature
int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
if (!m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
{
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
}
m_RouterIdentity->DropVerifier ();
}
}
void RouterInfo::ReadFromStream (std::istream& s)
{
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses));
bool introducers = false;
for (int i = 0; i < numAddresses; i++)
{
bool isValidAddress = true;
Address address;
s.read ((char *)&address.cost, sizeof (address.cost));
s.read ((char *)&address.date, sizeof (address.date));
char transportStyle[5];
ReadString (transportStyle, s);
if (!strcmp (transportStyle, "NTCP"))
address.transportStyle = eTransportNTCP;
else if (!strcmp (transportStyle, "SSU"))
address.transportStyle = eTransportSSU;
else
address.transportStyle = eTransportUnknown;
address.port = 0;
address.mtu = 0;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size));
size = be16toh (size);
while (r < size)
{
char key[500], value[500];
r += ReadString (key, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!strcmp (key, "host"))
{
boost::system::error_code ecode;
address.host = boost::asio::ip::address::from_string (value, ecode);
if (ecode)
{
if (address.transportStyle == eTransportNTCP)
{
m_SupportedTransports |= eNTCPV4; // TODO:
address.addressString = value;
}
else
{
m_SupportedTransports |= eSSUV4; // TODO:
address.addressString = value;
}
}
else
{
// add supported protocol
if (address.host.is_v4 ())
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
}
}
else if (!strcmp (key, "port"))
address.port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "mtu"))
address.mtu = boost::lexical_cast<int>(value);
else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32);
else if (!strcmp (key, "caps"))
ExtractCaps (value);
else if (key[0] == 'i')
{
// introducers
introducers = true;
size_t l = strlen(key);
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
if (index >= address.introducers.size ())
address.introducers.resize (index + 1);
Introducer& introducer = address.introducers.at (index);
if (!strcmp (key, "ihost"))
{
boost::system::error_code ecode;
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
}
else if (!strcmp (key, "iport"))
introducer.iPort = boost::lexical_cast<int>(value);
else if (!strcmp (key, "itag"))
introducer.iTag = boost::lexical_cast<uint32_t>(value);
else if (!strcmp (key, "ikey"))
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
}
}
if (isValidAddress)
m_Addresses.push_back(address);
}
// read peers
uint8_t numPeers;
s.read ((char *)&numPeers, sizeof (numPeers));
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
// read properties
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size));
size = be16toh (size);
while (r < size)
{
#ifdef _WIN32
char key[500], value[500];
// TODO: investigate why properties get read as one long string under Windows
// length should not be more than 44
#else
char key[50], value[50];
#endif
r += ReadString (key, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
s.seekg (1, std::ios_base::cur); r++; // ;
m_Properties[key] = value;
// extract caps
if (!strcmp (key, "caps"))
ExtractCaps (value);
}
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
SetUnreachable (true);
}
void RouterInfo::ExtractCaps (const char * value)
{
const char * cap = value;
while (*cap)
{
switch (*cap)
{
case CAPS_FLAG_FLOODFILL:
m_Caps |= Caps::eFloodfill;
break;
case CAPS_FLAG_HIGH_BANDWIDTH1:
case CAPS_FLAG_HIGH_BANDWIDTH2:
case CAPS_FLAG_HIGH_BANDWIDTH3:
m_Caps |= Caps::eHighBandwidth;
break;
case CAPS_FLAG_EXTRA_BANDWIDTH1:
case CAPS_FLAG_EXTRA_BANDWIDTH2:
m_Caps |= Caps::eExtraBandwidth;
break;
case CAPS_FLAG_HIDDEN:
m_Caps |= Caps::eHidden;
break;
case CAPS_FLAG_REACHABLE:
m_Caps |= Caps::eReachable;
break;
case CAPS_FLAG_UNREACHABLE:
m_Caps |= Caps::eUnreachable;
break;
case CAPS_FLAG_SSU_TESTING:
m_Caps |= Caps::eSSUTesting;
break;
case CAPS_FLAG_SSU_INTRODUCER:
m_Caps |= Caps::eSSUIntroducer;
break;
default: ;
}
cap++;
}
}
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'
caps += CAPS_FLAG_FLOODFILL; // floodfill
}
else
{
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1;
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
}
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
SetProperty ("caps", caps.c_str ());
}
void RouterInfo::WriteToStream (std::ostream& s)
{
uint64_t ts = htobe64 (m_Timestamp);
s.write ((char *)&ts, sizeof (ts));
// addresses
uint8_t numAddresses = m_Addresses.size ();
s.write ((char *)&numAddresses, sizeof (numAddresses));
for (auto& address : m_Addresses)
{
s.write ((char *)&address.cost, sizeof (address.cost));
s.write ((char *)&address.date, sizeof (address.date));
std::stringstream properties;
if (address.transportStyle == eTransportNTCP)
WriteString ("NTCP", s);
else if (address.transportStyle == eTransportSSU)
{
WriteString ("SSU", s);
// caps
WriteString ("caps", properties);
properties << '=';
std::string caps;
if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
WriteString (caps, properties);
properties << ';';
}
else
WriteString ("", s);
WriteString ("host", properties);
properties << '=';
WriteString (address.host.to_string (), properties);
properties << ';';
if (address.transportStyle == eTransportSSU)
{
// write introducers if any
if (address.introducers.size () > 0)
{
int i = 0;
for (auto introducer: address.introducers)
{
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
WriteString (introducer.iHost.to_string (), properties);
properties << ';';
i++;
}
i = 0;
for (auto introducer: address.introducers)
{
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
char value[64];
size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64);
value[l] = 0;
WriteString (value, properties);
properties << ';';
i++;
}
i = 0;
for (auto introducer: address.introducers)
{
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
WriteString (boost::lexical_cast<std::string>(introducer.iPort), properties);
properties << ';';
i++;
}
i = 0;
for (auto introducer: address.introducers)
{
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
WriteString (boost::lexical_cast<std::string>(introducer.iTag), properties);
properties << ';';
i++;
}
}
// write intro key
WriteString ("key", properties);
properties << '=';
char value[64];
size_t l = ByteStreamToBase64 (address.key, 32, value, 64);
value[l] = 0;
WriteString (value, properties);
properties << ';';
// write mtu
if (address.mtu)
{
WriteString ("mtu", properties);
properties << '=';
WriteString (boost::lexical_cast<std::string>(address.mtu), properties);
properties << ';';
}
}
WriteString ("port", properties);
properties << '=';
WriteString (boost::lexical_cast<std::string>(address.port), properties);
properties << ';';
uint16_t size = htobe16 (properties.str ().size ());
s.write ((char *)&size, sizeof (size));
s.write (properties.str ().c_str (), properties.str ().size ());
}
// peers
uint8_t numPeers = 0;
s.write ((char *)&numPeers, sizeof (numPeers));
// properties
std::stringstream properties;
for (auto& p : m_Properties)
{
WriteString (p.first, properties);
properties << '=';
WriteString (p.second, properties);
properties << ';';
}
uint16_t size = htobe16 (properties.str ().size ());
s.write ((char *)&size, sizeof (size));
s.write (properties.str ().c_str (), properties.str ().size ());
}
const uint8_t * RouterInfo::LoadBuffer ()
{
if (!m_Buffer)
{
if (LoadFile ())
LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
}
return m_Buffer;
}
void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys)
{
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
std::stringstream s;
uint8_t ident[1024];
auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024);
s.write ((char *)ident, identLen);
WriteToStream (s);
m_BufferLen = s.str ().size ();
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
// signature
privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
m_BufferLen += privateKeys.GetPublic ()->GetSignatureLen ();
}
void 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
LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL");
}
size_t RouterInfo::ReadString (char * str, std::istream& s)
{
uint8_t len;
s.read ((char *)&len, 1);
s.read (str, len);
str[len] = 0;
return len+1;
}
void RouterInfo::WriteString (const std::string& str, std::ostream& s)
{
uint8_t len = str.size ();
s.write ((char *)&len, 1);
s.write (str.c_str (), len);
}
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;
}
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;
}
bool RouterInfo::AddIntroducer (const Introducer& introducer)
{
for (auto& addr : m_Addresses)
{
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
{
for (auto intro: addr.introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
addr.introducers.push_back (introducer);
return true;
}
}
return false;
}
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
{
for (auto& addr : m_Addresses)
{
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
{
for (std::vector<Introducer>::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++)
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
{
addr.introducers.erase (it);
return true;
}
}
}
return false;
}
void RouterInfo::SetCaps (uint8_t caps)
{
m_Caps = caps;
UpdateCapsProperty ();
}
void RouterInfo::SetCaps (const char * caps)
{
SetProperty ("caps", caps);
m_Caps = 0;
ExtractCaps (caps);
}
void RouterInfo::SetProperty (const std::string& key, const std::string& value)
{
m_Properties[key] = value;
}
void RouterInfo::DeleteProperty (const std::string& key)
{
m_Properties.erase (key);
}
bool RouterInfo::IsFloodfill () const
{
return m_Caps & Caps::eFloodfill;
}
bool RouterInfo::IsNTCP (bool v4only) const
{
if (v4only)
return m_SupportedTransports & eNTCPV4;
else
return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
}
bool RouterInfo::IsSSU (bool v4only) const
{
if (v4only)
return m_SupportedTransports & eSSUV4;
else
return m_SupportedTransports & (eSSUV4 | eSSUV6);
}
bool RouterInfo::IsV6 () const
{
return m_SupportedTransports & (eNTCPV6 | eSSUV6);
}
void RouterInfo::EnableV6 ()
{
if (!IsV6 ())
m_SupportedTransports |= eNTCPV6 | eSSUV6;
}
void RouterInfo::DisableV6 ()
{
if (IsV6 ())
{
// NTCP
m_SupportedTransports &= ~eNTCPV6;
for (size_t i = 0; i < m_Addresses.size (); i++)
{
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;
}
}
}
}
bool RouterInfo::UsesIntroducer () const
{
return m_Caps & Caps::eUnreachable; // non-reachable
}
const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const
{
return GetAddress (eTransportNTCP, v4only);
}
const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const
{
return GetAddress (eTransportSSU, v4only);
}
const RouterInfo::Address * RouterInfo::GetSSUV6Address () const
{
return GetAddress (eTransportSSU, false, true);
}
const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
{
for (auto& address : m_Addresses)
{
if (address.transportStyle == s)
{
if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ()))
return &address;
}
}
return nullptr;
}
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
{
if (!m_Profile)
m_Profile = GetRouterProfile (GetIdentHash ());
return m_Profile;
}
}
}

View File

@@ -1,196 +0,0 @@
#ifndef ROUTER_INFO_H__
#define ROUTER_INFO_H__
#include <inttypes.h>
#include <string>
#include <map>
#include <vector>
#include <iostream>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Profiling.h"
namespace i2p
{
namespace data
{
const char CAPS_FLAG_FLOODFILL = 'f';
const char CAPS_FLAG_HIDDEN = 'H';
const char CAPS_FLAG_REACHABLE = 'R';
const char CAPS_FLAG_UNREACHABLE = 'U';
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K';
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L';
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M';
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N';
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O';
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P';
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X';
const char CAPS_FLAG_SSU_TESTING = 'B';
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
const int MAX_RI_BUFFER_SIZE = 2048;
class RouterInfo: public RoutingDestination
{
public:
enum SupportedTranports
{
eNTCPV4 = 0x01,
eNTCPV6 = 0x02,
eSSUV4 = 0x04,
eSSUV6 = 0x08
};
enum Caps
{
eFloodfill = 0x01,
eHighBandwidth = 0x02,
eExtraBandwidth = 0x04,
eReachable = 0x08,
eSSUTesting = 0x10,
eSSUIntroducer = 0x20,
eHidden = 0x40,
eUnreachable = 0x80
};
enum TransportStyle
{
eTransportUnknown = 0,
eTransportNTCP,
eTransportSSU
};
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer
{
boost::asio::ip::address iHost;
int iPort;
IntroKey iKey;
uint32_t iTag;
};
struct Address
{
TransportStyle transportStyle;
boost::asio::ip::address host;
std::string addressString;
int port, mtu;
uint64_t date;
uint8_t cost;
// SSU only
IntroKey key; // intro key for SSU
std::vector<Introducer> introducers;
bool IsCompatible (const boost::asio::ip::address& other) const
{
return (host.is_v4 () && other.is_v4 ()) ||
(host.is_v6 () && other.is_v6 ());
}
bool operator==(const Address& other) const
{
return transportStyle == other.transportStyle && host == other.host && port == other.port;
}
bool operator!=(const Address& other) const
{
return !(*this == other);
}
};
RouterInfo (const std::string& fullPath);
RouterInfo (): m_Buffer (nullptr) { };
RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (const uint8_t * buf, int len);
~RouterInfo ();
std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; };
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<Address>& GetAddresses () { return m_Addresses; };
const Address * GetNTCPAddress (bool v4only = true) const;
const Address * GetSSUAddress (bool v4only = true) const;
const Address * GetSSUV6Address () const;
void AddNTCPAddress (const char * host, int port);
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
bool AddIntroducer (const Introducer& introducer);
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
void DeleteProperty (const std::string& key); // called from RouterContext only
void ClearProperties () { m_Properties.clear (); };
bool IsFloodfill () const;
bool IsNTCP (bool v4only = true) const;
bool IsSSU (bool v4only = true) const;
bool IsV6 () const;
void EnableV6 ();
void DisableV6 ();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool UsesIntroducer () const;
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps);
void SetCaps (const char * caps);
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable () const { return m_IsUnreachable; };
const uint8_t * GetBuffer () const { return m_Buffer; };
const uint8_t * LoadBuffer (); // load if necessary
int GetBufferLen () const { return m_BufferLen; };
void CreateBuffer (const PrivateKeys& privateKeys);
bool IsUpdated () const { return m_IsUpdated; };
void SetUpdated (bool updated) { m_IsUpdated = updated; };
void SaveToFile (const std::string& fullPath);
std::shared_ptr<RouterProfile> GetProfile () const;
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
void Update (const uint8_t * buf, int len);
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
// 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 ExtractCaps (const char * value);
const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
void UpdateCapsProperty ();
private:
std::string m_FullPath;
std::shared_ptr<const IdentityEx> m_RouterIdentity;
uint8_t * m_Buffer;
int m_BufferLen;
uint64_t m_Timestamp;
std::vector<Address> m_Addresses;
std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_Caps;
mutable std::shared_ptr<RouterProfile> m_Profile;
};
}
}
#endif

873
SAM.cpp
View File

@@ -1,873 +0,0 @@
#include <string.h>
#include <stdio.h>
#ifdef _MSC_VER
#include <stdlib.h>
#endif
#include <boost/lexical_cast.hpp>
#include "Base.h"
#include "Identity.h"
#include "Log.h"
#include "Destination.h"
#include "ClientContext.h"
#include "SAM.h"
namespace i2p
{
namespace client
{
SAMSocket::SAMSocket (SAMBridge& owner):
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()),
m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
m_Stream (nullptr), m_Session (nullptr)
{
}
SAMSocket::~SAMSocket ()
{
Terminate ();
}
void SAMSocket::CloseStream ()
{
if (m_Stream)
{
m_Stream->Close ();
m_Stream.reset ();
}
}
void SAMSocket::Terminate ()
{
CloseStream ();
switch (m_SocketType)
{
case eSAMSocketTypeSession:
m_Owner.CloseSession (m_ID);
break;
case eSAMSocketTypeStream:
{
if (m_Session)
m_Session->sockets.remove (shared_from_this ());
break;
}
case eSAMSocketTypeAcceptor:
{
if (m_Session)
{
m_Session->sockets.remove (shared_from_this ());
m_Session->localDestination->StopAcceptingStreams ();
}
break;
}
default:
;
}
m_SocketType = eSAMSocketTypeTerminated;
m_Socket.close ();
}
void SAMSocket::ReceiveHandshake ()
{
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_Buffer[bytes_transferred] = 0;
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
*eol = 0;
LogPrint (eLogDebug, "SAM: handshake ", m_Buffer);
char * separator = strchr (m_Buffer, ' ');
if (separator)
{
separator = strchr (separator + 1, ' ');
if (separator)
*separator = 0;
}
if (!strcmp (m_Buffer, SAM_HANDSHAKE))
{
std::string version("3.0");
// try to find MIN and MAX, 3.0 if not found
if (separator)
{
separator++;
std::map<std::string, std::string> params;
ExtractParams (separator, params);
auto it = params.find (SAM_PARAM_MAX);
// TODO: check MIN as well
if (it != params.end ())
version = it->second;
}
if (version[0] == '3') // we support v3 (3.0 and 3.1) only
{
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
#else
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
#endif
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
else
SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true);
}
else
{
LogPrint (eLogError, "SAM: handshake mismatch");
Terminate ();
}
}
}
void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
std::bind(&SAMSocket::HandleMessage, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
}
void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close)
{
if (!m_IsSilent)
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, close));
else
{
if (close)
Terminate ();
else
Receive ();
}
}
void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close)
{
if (ecode)
{
LogPrint (eLogError, "SAM: reply send error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (close)
Terminate ();
else
Receive ();
}
}
void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else if (m_SocketType == eSAMSocketTypeStream)
HandleReceived (ecode, bytes_transferred);
else
{
bytes_transferred += m_BufferOffset;
m_BufferOffset = 0;
m_Buffer[bytes_transferred] = 0;
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
{
*eol = 0;
char * separator = strchr (m_Buffer, ' ');
if (separator)
{
separator = strchr (separator + 1, ' ');
if (separator)
*separator = 0;
else
separator = eol;
if (!strcmp (m_Buffer, SAM_SESSION_CREATE))
ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT))
ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT))
ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DEST_GENERATE))
ProcessDestGenerate ();
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND))
{
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
if (processed < len)
{
m_BufferOffset = len - processed;
if (processed > 0)
memmove (m_Buffer, separator + 1 + processed, m_BufferOffset);
else
{
// restore string back
*separator = ' ';
*eol = '\n';
}
}
// since it's SAM v1 reply is not expected
Receive ();
}
else
{
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
Terminate ();
}
}
else
{
LogPrint (eLogError, "SAM: malformed message ", m_Buffer);
Terminate ();
}
}
else
{
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
m_BufferOffset = bytes_transferred;
// try to receive remaining message
Receive ();
}
}
}
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: session create: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& style = params[SAM_PARAM_STYLE];
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
m_ID = id;
if (m_Owner.FindSession (id))
{
// session exists
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true);
return;
}
// create destination
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
if (m_Session)
{
m_SocketType = eSAMSocketTypeSession;
if (style == SAM_VALUE_DATAGRAM)
{
auto dest = m_Session->localDestination->CreateDatagramDestination ();
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
}
if (m_Session->localDestination->IsReady ())
SendSessionCreateReplyOk ();
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
}
else
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true);
}
void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_Session->localDestination->IsReady ())
SendSessionCreateReplyOk ();
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
}
}
void SAMSocket::SendSessionCreateReplyOk ()
{
uint8_t buf[1024];
char priv[1024];
size_t l = m_Session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
priv[l1] = 0;
#ifdef _MSC_VER
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
#else
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
#endif
SendMessageReply (m_Buffer, l2, false);
}
void SAMSocket::ProcessStreamConnect (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: stream connect: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
m_ID = id;
m_Session = m_Owner.FindSession (id);
if (m_Session)
{
auto dest = std::make_shared<i2p::data::IdentityEx> ();
size_t len = dest->FromBase64(destination);
if (len > 0)
{
context.GetAddressBook().InsertAddress(dest);
auto leaseSet = m_Session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet)
Connect(leaseSet);
else
{
m_Session->localDestination->RequestDestination(dest->GetIdentHash(),
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1));
}
}
else
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
{
m_SocketType = eSAMSocketTypeStream;
m_Session->sockets.push_back (shared_from_this ());
m_Stream = m_Session->localDestination->CreateStream (remote);
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
I2PReceive ();
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet)
Connect (leaseSet);
else
{
LogPrint (eLogError, "SAM: destination to connect not found");
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
}
}
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: stream accept: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
m_ID = id;
m_Session = m_Owner.FindSession (id);
if (m_Session)
{
if (!m_Session->localDestination->IsAcceptingStreams ())
{
m_SocketType = eSAMSocketTypeAcceptor;
m_Session->sockets.push_back (shared_from_this ());
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
else
SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
{
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
size_t size = boost::lexical_cast<int>(params[SAM_PARAM_SIZE]), offset = data - buf;
if (offset + size <= len)
{
if (m_Session)
{
auto d = m_Session->localDestination->GetDatagramDestination ();
if (d)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
}
else
LogPrint (eLogError, "SAM: missing datagram destination");
}
else
LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND");
}
else
{
LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset);
return 0; // try to receive more
}
return offset + size;
}
void SAMSocket::ProcessDestGenerate ()
{
LogPrint (eLogDebug, "SAM: dest generate");
auto keys = i2p::data::PrivateKeys::CreateRandomKeys ();
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#endif
SendMessageReply (m_Buffer, len, false);
}
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: naming lookup: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& name = params[SAM_PARAM_NAME];
std::shared_ptr<const i2p::data::IdentityEx> identity;
i2p::data::IdentHash ident;
if (name == "ME")
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
SendNamingLookupReply (identity);
else if (m_Session && m_Session->localDestination &&
context.GetAddressBook ().GetIdentHash (name, ident))
{
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ());
else
m_Session->localDestination->RequestDestination (ident,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, ident));
}
else
{
LogPrint (eLogError, "SAM: naming failed, unknown address ", name);
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#endif
SendMessageReply (m_Buffer, len, false);
}
}
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
{
if (leaseSet)
{
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
SendNamingLookupReply (leaseSet->GetIdentity ());
}
else
{
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
#endif
SendMessageReply (m_Buffer, len, false);
}
}
void SAMSocket::SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity)
{
auto base64 = identity->ToBase64 ();
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#else
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#endif
SendMessageReply (m_Buffer, l, false);
}
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
{
char * separator;
do
{
separator = strchr (buf, ' ');
if (separator) *separator = 0;
char * value = strchr (buf, '=');
if (value)
{
*value = 0;
value++;
params[buf] = value;
}
buf = separator + 1;
}
while (separator);
}
void SAMSocket::Receive ()
{
if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE)
{
LogPrint (eLogError, "SAM: Buffer is full, terminate");
Terminate ();
return;
}
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
if (m_Stream)
{
auto s = shared_from_this ();
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
[s](const boost::system::error_code& ecode)
{
if (!ecode)
s->Receive ();
else
s->Terminate ();
});
}
}
}
void SAMSocket::I2PReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
SAM_SOCKET_CONNECTION_MAX_IDLE);
}
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
{
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
}
}
void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode)
{
if (ecode)
{
LogPrint (eLogError, "SAM: socket write error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else
I2PReceive ();
}
void SAMSocket::HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
m_Stream = stream;
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
auto session = m_Owner.FindSession (m_ID);
if (session)
session->localDestination->StopAcceptingStreams ();
m_SocketType = eSAMSocketTypeStream;
if (!m_IsSilent)
{
// send remote peer address
uint8_t ident[1024];
size_t l = stream->GetRemoteIdentity ()->ToBuffer (ident, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
m_StreamBuffer[l1] = '\n';
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
}
else
I2PReceive ();
}
else
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
}
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: datagram received ", len);
auto base64 = from.ToBase64 ();
#ifdef _MSC_VER
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
#else
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
#endif
if (len < SAM_SOCKET_BUFFER_SIZE - l)
{
memcpy (m_StreamBuffer + l, buf, len);
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
}
else
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
}
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
localDestination (dest)
{
}
SAMSession::~SAMSession ()
{
for (auto it: sockets)
it->SetSocketType (eSAMSocketTypeTerminated);
i2p::client::context.DeleteLocalDestination (localDestination);
}
void SAMSession::CloseStreams ()
{
for (auto it: sockets)
{
it->CloseStream ();
it->SetSocketType (eSAMSocketTypeTerminated);
}
sockets.clear ();
}
SAMBridge::SAMBridge (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
{
}
SAMBridge::~SAMBridge ()
{
if (m_IsRunning)
Stop ();
}
void SAMBridge::Start ()
{
Accept ();
ReceiveDatagram ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&SAMBridge::Run, this));
}
void SAMBridge::Stop ()
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto it: m_Sessions)
delete it.second;
m_Sessions.clear ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void SAMBridge::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
}
}
void SAMBridge::Accept ()
{
auto newSocket = std::make_shared<SAMSocket> (*this);
m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket)
{
if (!ecode)
{
boost::system::error_code ec;
auto ep = socket->GetSocket ().remote_endpoint (ec);
if (!ec)
{
LogPrint (eLogDebug, "SAM: new connection from ", ep);
socket->ReceiveHandshake ();
}
else
LogPrint (eLogError, "SAM: incoming connection error ", ec.message ());
}
else
LogPrint (eLogError, "SAM: accept error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Accept ();
}
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination,
const std::map<std::string, std::string> * params)
{
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (destination != "")
{
i2p::data::PrivateKeys keys;
keys.FromBase64 (destination);
localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params);
}
else // transient
{
// extract signature type
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
if (params)
{
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params->end ())
// TODO: extract string values
signatureType = boost::lexical_cast<int> (it->second);
}
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
}
if (localDestination)
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession *>(id, new SAMSession (localDestination)));
if (!ret.second)
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
return ret.first->second;
}
return nullptr;
}
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 ())
{
auto session = it->second;
session->localDestination->StopAcceptingStreams ();
session->CloseStreams ();
m_Sessions.erase (it);
delete session;
}
}
SAMSession * SAMBridge::FindSession (const std::string& id) const
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
return it->second;
return nullptr;
}
void SAMBridge::ReceiveDatagram ()
{
m_DatagramSocket.async_receive_from (
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
m_SenderEndpoint,
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
}
void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (!ecode)
{
m_DatagramReceiveBuffer[bytes_transferred] = 0;
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
*eol = 0; eol++;
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
if (sessionID)
{
sessionID++;
char * destination = strchr (sessionID, ' ');
if (destination)
{
*destination = 0; destination++;
auto session = FindSession (sessionID);
if (session)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
}
else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
}
else
LogPrint (eLogError, "SAM: Missing destination key");
}
else
LogPrint (eLogError, "SAM: Missing sessionID");
ReceiveDatagram ();
}
else
LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ());
}
}
}

192
SAM.h
View File

@@ -1,192 +0,0 @@
#ifndef SAM_H__
#define SAM_H__
#include <inttypes.h>
#include <string>
#include <map>
#include <list>
#include <thread>
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "Identity.h"
#include "LeaseSet.h"
#include "Streaming.h"
#include "Destination.h"
namespace i2p
{
namespace client
{
const size_t SAM_SOCKET_BUFFER_SIZE = 8192;
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
const char SAM_HANDSHAKE[] = "HELLO VERSION";
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n";
const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n";
const char SAM_SESSION_CREATE[] = "SESSION CREATE";
const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n";
const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n";
const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n";
const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n";
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
const char SAM_DEST_GENERATE[] = "DEST GENERATE";
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%zu\n";
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n";
const char SAM_PARAM_MIN[] = "MIN";
const char SAM_PARAM_MAX[] = "MAX";
const char SAM_PARAM_STYLE[] = "STYLE";
const char SAM_PARAM_ID[] = "ID";
const char SAM_PARAM_SILENT[] = "SILENT";
const char SAM_PARAM_DESTINATION[] = "DESTINATION";
const char SAM_PARAM_NAME[] = "NAME";
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
const char SAM_PARAM_SIZE[] = "SIZE";
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
const char SAM_VALUE_STREAM[] = "STREAM";
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
const char SAM_VALUE_RAW[] = "RAW";
const char SAM_VALUE_TRUE[] = "true";
const char SAM_VALUE_FALSE[] = "false";
enum SAMSocketType
{
eSAMSocketTypeUnknown,
eSAMSocketTypeSession,
eSAMSocketTypeStream,
eSAMSocketTypeAcceptor,
eSAMSocketTypeTerminated
};
class SAMBridge;
struct SAMSession;
class SAMSocket: public std::enable_shared_from_this<SAMSocket>
{
public:
SAMSocket (SAMBridge& owner);
~SAMSocket ();
void CloseStream (); // TODO: implement it better
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
void ReceiveHandshake ();
void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; };
SAMSocketType GetSocketType () const { return m_SocketType; };
private:
void Terminate ();
void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void SendMessageReply (const char * msg, size_t len, bool close);
void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close);
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void I2PReceive ();
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
void HandleWriteI2PData (const boost::system::error_code& ecode);
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void ProcessSessionCreate (char * buf, size_t len);
void ProcessStreamConnect (char * buf, size_t len);
void ProcessStreamAccept (char * buf, size_t len);
void ProcessDestGenerate ();
void ProcessNamingLookup (char * buf, size_t len);
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk ();
private:
SAMBridge& m_Owner;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::deadline_timer m_Timer;
char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1];
size_t m_BufferOffset;
uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE];
SAMSocketType m_SocketType;
std::string m_ID; // nickname
bool m_IsSilent;
std::shared_ptr<i2p::stream::Stream> m_Stream;
SAMSession * m_Session;
};
struct SAMSession
{
std::shared_ptr<ClientDestination> localDestination;
std::list<std::shared_ptr<SAMSocket> > sockets;
SAMSession (std::shared_ptr<ClientDestination> dest);
~SAMSession ();
void CloseStreams ();
};
class SAMBridge
{
public:
SAMBridge (const std::string& address, int port);
~SAMBridge ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params);
void CloseSession (const std::string& id);
SAMSession * FindSession (const std::string& id) const;
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket);
void ReceiveDatagram ();
void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_DatagramSocket;
mutable std::mutex m_SessionsMutex;
std::map<std::string, SAMSession *> m_Sessions;
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
public:
// for HTTP
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
};
}
}
#endif

574
SOCKS.cpp
View File

@@ -1,574 +0,0 @@
#include <cstring>
#include <cassert>
#include <string>
#include <atomic>
#include "SOCKS.h"
#include "Identity.h"
#include "Streaming.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include "I2PTunnel.h"
namespace i2p
{
namespace proxy
{
static const size_t socks_buffer_size = 8192;
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
struct SOCKSDnsAddress
{
uint8_t size;
char value[max_socks_hostname_size];
void FromString (std::string str)
{
size = str.length();
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
memcpy(value,str.c_str(),size);
}
std::string ToString() { return std::string(value, size); }
void push_back (char c) { value[size++] = c; }
};
class SOCKSServer;
class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<SOCKSHandler>
{
private:
enum state
{
GET_SOCKSV,
GET_COMMAND,
GET_PORT,
GET_IPV4,
GET4_IDENT,
GET4A_HOST,
GET5_AUTHNUM,
GET5_AUTH,
GET5_REQUESTV,
GET5_GETRSV,
GET5_GETADDRTYPE,
GET5_IPV6,
GET5_HOST_SIZE,
GET5_HOST,
DONE
};
enum authMethods
{
AUTH_NONE = 0, //No authentication, skip to next step
AUTH_GSSAPI = 1, //GSSAPI authentication
AUTH_USERPASSWD = 2, //Username and password
AUTH_UNACCEPTABLE = 0xff //No acceptable method found
};
enum addrTypes
{
ADDR_IPV4 = 1, //IPv4 address (4 octets)
ADDR_DNS = 3, // DNS name (up to 255 octets)
ADDR_IPV6 = 4 //IPV6 address (16 octets)
};
enum errTypes
{
SOCKS5_OK = 0, // No error for SOCKS5
SOCKS5_GEN_FAIL = 1, // General server failure
SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset
SOCKS5_NET_UNREACH = 3, // Network unreachable
SOCKS5_HOST_UNREACH = 4, // Host unreachable
SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer
SOCKS5_TTL_EXPIRED = 6, // TTL Expired
SOCKS5_CMD_UNSUP = 7, // Command unsuported
SOCKS5_ADDR_UNSUP = 8, // Address type unsuported
SOCKS4_OK = 90, // No error for SOCKS4
SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed
SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server
SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ
};
enum cmdTypes
{
CMD_CONNECT = 1, // TCP Connect
CMD_BIND = 2, // TCP Bind
CMD_UDP = 3 // UDP associate
};
enum socksVersions
{
SOCKS4 = 4, // SOCKS4
SOCKS5 = 5 // SOCKS5
};
union address
{
uint32_t ip;
SOCKSDnsAddress dns;
uint8_t ipv6[16];
};
void EnterState(state nstate, uint8_t parseleft = 1);
bool HandleData(uint8_t *sock_buff, std::size_t len);
bool ValidateSOCKSRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void AsyncSockRead();
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
bool Socks5ChooseAuth();
void SocksRequestFailed(errTypes error);
void SocksRequestSuccess();
void SentSocksFailed(const boost::system::error_code & ecode);
void SentSocksDone(const boost::system::error_code & ecode);
void SentSocksResponse(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
uint8_t m_sock_buff[socks_buffer_size];
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::shared_ptr<i2p::stream::Stream> m_stream;
uint8_t *m_remaining_data; //Data left to be sent
uint8_t m_response[7+max_socks_hostname_size];
address m_address; //Address
std::size_t m_remaining_data_len; //Size of the data left to be sent
uint32_t m_4aip; //Used in 4a requests
uint16_t m_port;
uint8_t m_command;
uint8_t m_parseleft; //Octets left to parse
authMethods m_authchosen; //Authentication chosen
addrTypes m_addrtype; //Address type chosen
socksVersions m_socksv; //Socks version
cmdTypes m_cmd; // Command requested
state m_state;
public:
SOCKSHandler(SOCKSServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4)
{ m_address.ip = 0; EnterState(GET_SOCKSV); }
~SOCKSHandler() { Terminate(); }
void Handle() { AsyncSockRead(); }
};
void SOCKSHandler::AsyncSockRead()
{
LogPrint(eLogDebug,"--- SOCKS async sock read");
if(m_sock)
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
else
LogPrint(eLogError,"--- SOCKS no socket for read");
}
void SOCKSHandler::Terminate()
{
if (Kill()) return;
if (m_sock)
{
LogPrint(eLogDebug,"--- SOCKS close sock");
m_sock->close();
m_sock = nullptr;
}
if (m_stream)
{
LogPrint(eLogDebug,"--- SOCKS close stream");
m_stream.reset ();
}
Done(shared_from_this());
}
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
{
assert(error >= SOCKS4_OK);
m_response[0] = '\x00'; //Version
m_response[1] = error; //Response code
htobe16buf(m_response+2,port); //Port
htobe32buf(m_response+4,ip); //IP
return boost::asio::const_buffers_1(m_response,8);
}
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port)
{
size_t size = 6;
assert(error <= SOCKS5_ADDR_UNSUP);
m_response[0] = '\x05'; //Version
m_response[1] = error; //Response code
m_response[2] = '\x00'; //RSV
m_response[3] = type; //Address type
switch (type)
{
case ADDR_IPV4:
size = 10;
htobe32buf(m_response+4,addr.ip);
break;
case ADDR_IPV6:
size = 22;
memcpy(m_response+4,addr.ipv6, 16);
break;
case ADDR_DNS:
size = 7+addr.dns.size;
m_response[4] = addr.dns.size;
memcpy(m_response+5,addr.dns.value, addr.dns.size);
break;
}
htobe16buf(m_response+size-2,port); //Port
return boost::asio::const_buffers_1(m_response,size);
}
bool SOCKSHandler::Socks5ChooseAuth()
{
m_response[0] = '\x05'; //Version
m_response[1] = m_authchosen; //Response code
boost::asio::const_buffers_1 response(m_response,2);
if (m_authchosen == AUTH_UNACCEPTABLE)
{
LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
shared_from_this(), std::placeholders::_1));
return false;
}
else
{
LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen);
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse,
shared_from_this(), std::placeholders::_1));
return true;
}
}
/* All hope is lost beyond this point */
void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error)
{
boost::asio::const_buffers_1 response(nullptr,0);
assert(error != SOCKS4_OK && error != SOCKS5_OK);
switch (m_socksv)
{
case SOCKS4:
LogPrint(eLogWarning,"--- SOCKS4 failed: ", error);
if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors
response = GenerateSOCKS4Response(error, m_4aip, m_port);
break;
case SOCKS5:
LogPrint(eLogWarning,"--- SOCKS5 failed: ", error);
response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port);
break;
}
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
shared_from_this(), std::placeholders::_1));
}
void SOCKSHandler::SocksRequestSuccess()
{
boost::asio::const_buffers_1 response(nullptr,0);
//TODO: this should depend on things like the command type and callbacks may change
switch (m_socksv)
{
case SOCKS4:
LogPrint(eLogInfo,"--- SOCKS4 connection success");
response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
break;
case SOCKS5:
LogPrint(eLogInfo,"--- SOCKS5 connection success");
auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash());
address ad; ad.dns.FromString(s);
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID());
break;
}
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone,
shared_from_this(), std::placeholders::_1));
}
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
switch (nstate)
{
case GET_PORT: parseleft = 2; break;
case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break;
case GET4_IDENT: m_4aip = m_address.ip; break;
case GET4A_HOST:
case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break;
case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break;
default:;
}
m_parseleft = parseleft;
m_state = nstate;
}
bool SOCKSHandler::ValidateSOCKSRequest()
{
if ( m_cmd != CMD_CONNECT )
{
//TODO: we need to support binds and other shit!
LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd);
SocksRequestFailed(SOCKS5_CMD_UNSUP);
return false;
}
//TODO: we may want to support other address types!
if ( m_addrtype != ADDR_DNS )
{
switch (m_socksv)
{
case SOCKS5:
LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype);
break;
case SOCKS4:
LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4");
break;
}
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
return false;
}
//TODO: we may want to support other domains
if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos)
{
LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString());
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
return false;
}
return true;
}
bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
{
assert(len); // This should always be called with a least a byte left to parse
while (len > 0)
{
switch (m_state)
{
case GET_SOCKSV:
m_socksv = (SOCKSHandler::socksVersions) *sock_buff;
switch (*sock_buff)
{
case SOCKS4:
EnterState(GET_COMMAND); //Initialize the parser at the right position
break;
case SOCKS5:
EnterState(GET5_AUTHNUM); //Initialize the parser at the right position
break;
default:
LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff));
Terminate();
return false;
}
break;
case GET5_AUTHNUM:
EnterState(GET5_AUTH, *sock_buff);
break;
case GET5_AUTH:
m_parseleft --;
if (*sock_buff == AUTH_NONE)
m_authchosen = AUTH_NONE;
if ( m_parseleft == 0 )
{
if (!Socks5ChooseAuth()) return false;
EnterState(GET5_REQUESTV);
}
break;
case GET_COMMAND:
switch (*sock_buff)
{
case CMD_CONNECT:
case CMD_BIND:
break;
case CMD_UDP:
if (m_socksv == SOCKS5) break;
default:
LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
m_cmd = (SOCKSHandler::cmdTypes)*sock_buff;
switch (m_socksv)
{
case SOCKS5: EnterState(GET5_GETRSV); break;
case SOCKS4: EnterState(GET_PORT); break;
}
break;
case GET_PORT:
m_port = (m_port << 8)|((uint16_t)*sock_buff);
m_parseleft--;
if (m_parseleft == 0)
{
switch (m_socksv)
{
case SOCKS5: EnterState(DONE); break;
case SOCKS4: EnterState(GET_IPV4); break;
}
}
break;
case GET_IPV4:
m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff);
m_parseleft--;
if (m_parseleft == 0)
{
switch (m_socksv)
{
case SOCKS5: EnterState(GET_PORT); break;
case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break;
}
}
break;
case GET4_IDENT:
if (!*sock_buff)
{
if( m_4aip == 0 || m_4aip > 255 )
EnterState(DONE);
else
EnterState(GET4A_HOST);
}
break;
case GET4A_HOST:
if (!*sock_buff)
{
EnterState(DONE);
break;
}
if (m_address.dns.size >= max_socks_hostname_size)
{
LogPrint(eLogError,"--- SOCKS4a destination is too large");
SocksRequestFailed(SOCKS4_FAIL);
return false;
}
m_address.dns.push_back(*sock_buff);
break;
case GET5_REQUESTV:
if (*sock_buff != SOCKS5)
{
LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
EnterState(GET_COMMAND);
break;
case GET5_GETRSV:
if ( *sock_buff != 0 )
{
LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
EnterState(GET5_GETADDRTYPE);
break;
case GET5_GETADDRTYPE:
switch (*sock_buff)
{
case ADDR_IPV4: EnterState(GET_IPV4); break;
case ADDR_IPV6: EnterState(GET5_IPV6); break;
case ADDR_DNS : EnterState(GET5_HOST_SIZE); break;
default:
LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
break;
case GET5_IPV6:
m_address.ipv6[16-m_parseleft] = *sock_buff;
m_parseleft--;
if (m_parseleft == 0) EnterState(GET_PORT);
break;
case GET5_HOST_SIZE:
EnterState(GET5_HOST, *sock_buff);
break;
case GET5_HOST:
m_address.dns.push_back(*sock_buff);
m_parseleft--;
if (m_parseleft == 0) EnterState(GET_PORT);
break;
default:
LogPrint(eLogError,"--- SOCKS parse state?? ", m_state);
Terminate();
return false;
}
sock_buff++;
len--;
if (m_state == DONE)
{
m_remaining_data_len = len;
m_remaining_data = sock_buff;
return ValidateSOCKSRequest();
}
}
return true;
}
void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
LogPrint(eLogDebug,"--- SOCKS sock recv: ", len);
if(ecode)
{
LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode);
Terminate();
return;
}
if (HandleData(m_sock_buff, len))
{
if (m_state == DONE)
{
LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
}
else
AsyncSockRead();
}
}
void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
{
if (!ecode)
Terminate();
else
{
LogPrint (eLogError,"--- SOCKS Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
}
void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode)
{
if (!ecode)
{
if (Kill()) return;
LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, m_stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (m_remaining_data,m_remaining_data_len);
Done(shared_from_this());
}
else
{
LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ());
Terminate();
}
}
void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode)
{
if (ecode)
{
LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ());
Terminate();
}
}
void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
m_stream = stream;
SocksRequestSuccess();
}
else
{
LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info.");
SocksRequestFailed(SOCKS5_HOST_UNREACH);
}
}
SOCKSServer::SOCKSServer(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> SOCKSServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
return std::make_shared<SOCKSHandler> (this, socket);
}
}
}

32
SOCKS.h
View File

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

604
SSU.cpp
View File

@@ -1,604 +0,0 @@
#include <string.h>
#include <boost/bind.hpp>
#include "Log.h"
#include "Timestamp.h"
#include "RouterContext.h"
#include "NetDb.h"
#include "SSU.h"
namespace i2p
{
namespace transport
{
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service)
{
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 ())
{
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 ()
{
}
void SSUServer::Start ()
{
m_IsRunning = true;
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
if (context.SupportsV6 ())
{
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
}
SchedulePeerTestsCleanupTimer ();
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
}
void SSUServer::Stop ()
{
DeleteAllSessions ();
m_IsRunning = false;
m_Service.stop ();
m_Socket.close ();
m_ServiceV6.stop ();
m_SocketV6.close ();
m_ReceiversService.stop ();
if (m_ReceiversThread)
{
m_ReceiversThread->join ();
delete m_ReceiversThread;
m_ReceiversThread = nullptr;
}
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
if (m_ThreadV6)
{
m_ThreadV6->join ();
delete m_ThreadV6;
m_ThreadV6 = nullptr;
}
}
void SSUServer::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: server runtime exception: ", ex.what ());
}
}
}
void SSUServer::RunV6 ()
{
while (m_IsRunning)
{
try
{
m_ServiceV6.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: v6 server runtime exception: ", ex.what ());
}
}
}
void SSUServer::RunReceivers ()
{
while (m_IsRunning)
{
try
{
m_ReceiversService.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: receivers runtime exception: ", ex.what ());
}
}
}
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
{
m_Relays[tag] = relay;
}
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
{
auto it = m_Relays.find (tag);
if (it != m_Relays.end ())
return FindSession (it->second);
return nullptr;
}
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
{
if (to.protocol () == boost::asio::ip::udp::v4())
m_Socket.send_to (boost::asio::buffer (buf, len), to);
else
m_SocketV6.send_to (boost::asio::buffer (buf, len), to);
}
void SSUServer::Receive ()
{
SSUPacket * packet = new SSUPacket ();
m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from,
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
}
void SSUServer::ReceiveV6 ()
{
SSUPacket * packet = new SSUPacket ();
m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from,
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
}
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{
if (!ecode)
{
packet->len = bytes_transferred;
std::vector<SSUPacket *> packets;
packets.push_back (packet);
boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec);
while (moreBytes && packets.size () < 25)
{
packet = new SSUPacket ();
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from);
packets.push_back (packet);
moreBytes = m_Socket.available();
}
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
Receive ();
}
else
{
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
delete packet;
}
}
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{
if (!ecode)
{
packet->len = bytes_transferred;
std::vector<SSUPacket *> packets;
packets.push_back (packet);
size_t moreBytes = m_SocketV6.available ();
while (moreBytes && packets.size () < 25)
{
packet = new SSUPacket ();
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from);
packets.push_back (packet);
moreBytes = m_SocketV6.available();
}
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
ReceiveV6 ();
}
else
{
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
delete packet;
}
}
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
{
std::shared_ptr<SSUSession> session;
for (auto it1: packets)
{
auto packet = it1;
try
{
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
{
if (session) session->FlushData ();
auto it = sessions->find (packet->from);
if (it != sessions->end ())
session = it->second;
if (!session)
{
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
(*sessions)[packet->from] = session;
LogPrint (eLogInfo, "New SSU session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
}
}
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ());
if (session) session->FlushData ();
session = nullptr;
}
delete packet;
}
if (session) session->FlushData ();
}
std::shared_ptr<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const
{
if (!router) return nullptr;
auto address = router->GetSSUAddress (true); // v4 only
if (!address) return nullptr;
auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
if (session || !context.SupportsV6 ())
return session;
// try v6
address = router->GetSSUV6Address ();
if (!address) return nullptr;
return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
}
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
{
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
auto it = sessions.find (e);
if (it != sessions.end ())
return it->second;
else
return nullptr;
}
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
{
auto address = router->GetSSUAddress (!context.SupportsV6 ());
if (address)
CreateSession (router, address->host, address->port, peerTest);
else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
}
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest)
{
if (router)
{
if (router->UsesIntroducer ())
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, peerTest)); // always V4 thread
else
{
boost::asio::ip::udp::endpoint remoteEndpoint (addr, port);
auto& s = addr.is_v6 () ? m_ServiceV6 : m_Service;
s.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
}
}
}
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
{
auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
auto it = sessions.find (remoteEndpoint);
if (it != sessions.end ())
{
auto session = it->second;
if (peerTest && session->GetState () == eSessionStateEstablished)
session->SendPeerTest ();
}
else
{
// otherwise create new session
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
sessions[remoteEndpoint] = session;
// connect
LogPrint (eLogInfo, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect ();
}
}
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
{
if (router && router->UsesIntroducer ())
{
auto address = router->GetSSUAddress (true); // v4 only for now
if (address)
{
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto it = m_Sessions.find (remoteEndpoint);
// check if session if presented alredy
if (it != m_Sessions.end ())
{
auto session = it->second;
if (peerTest && session->GetState () == eSessionStateEstablished)
session->SendPeerTest ();
return;
}
// create new session
int numIntroducers = address->introducers.size ();
if (numIntroducers > 0)
{
std::shared_ptr<SSUSession> introducerSession;
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
// we might have a session to introducer already
for (int i = 0; i < numIntroducers; i++)
{
auto intr = &(address->introducers[i]);
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
if (ep.address ().is_v4 ()) // ipv4 only
{
if (!introducer) introducer = intr; // we pick first one for now
it = m_Sessions.find (ep);
if (it != m_Sessions.end ())
{
introducerSession = it->second;
break;
}
}
}
if (!introducer)
{
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 introducers present");
return;
}
if (introducerSession) // session found
LogPrint (eLogInfo, "SSU: Session to introducer already exists");
else // create new
{
LogPrint (eLogInfo, "SSU: Creating new session to introducer");
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession;
}
// create session
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session;
// introduce
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
"] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction ();
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
{
uint8_t buf[1];
Send (buf, 0, remoteEndpoint); // send HolePunch
}
introducerSession->Introduce (*introducer, router);
}
else
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
}
else
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
}
}
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
{
if (session)
{
session->Close ();
auto& ep = session->GetRemoteEndpoint ();
if (ep.address ().is_v6 ())
m_SessionsV6.erase (ep);
else
m_Sessions.erase (ep);
}
}
void SSUServer::DeleteAllSessions ()
{
for (auto it: m_Sessions)
it.second->Close ();
m_Sessions.clear ();
for (auto it: m_SessionsV6)
it.second->Close ();
m_SessionsV6.clear ();
}
template<typename Filter>
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
{
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
for (auto s :m_Sessions)
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::GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded) // v4 only
{
return GetRandomV4Session (
[excluded](std::shared_ptr<SSUSession> session)->bool
{
return session->GetState () == eSessionStateEstablished && !session->IsV6 () &&
session != excluded;
}
);
}
std::set<SSUSession *> SSUServer::FindIntroducers (int maxNumIntroducers)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::set<SSUSession *> ret;
for (int i = 0; i < maxNumIntroducers; i++)
{
auto session = GetRandomV4Session (
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
{
return session->GetRelayTag () && !ret.count (session.get ()) &&
session->GetState () == eSessionStateEstablished &&
ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION;
}
);
if (session)
{
ret.insert (session.get ());
break;
}
}
return ret;
}
void SSUServer::ScheduleIntroducersUpdateTimer ()
{
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
this, std::placeholders::_1));
}
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
// timeout expired
if (i2p::context.GetStatus () == eRouterStatusTesting)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimer ();
return;
}
if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore
// we are firewalled
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable ();
std::list<boost::asio::ip::udp::endpoint> newList;
size_t numIntroducers = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it :m_Introducers)
{
auto session = FindSession (it);
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
{
session->SendKeepAlive ();
newList.push_back (it);
numIntroducers++;
}
else
i2p::context.RemoveIntroducer (it);
}
if (numIntroducers < SSU_MAX_NUM_INTRODUCERS)
{
// create new
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
if (introducers.size () > 0)
{
for (auto it1: introducers)
{
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;
}
}
}
}
m_Introducers = newList;
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
{
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
if (introducer)
CreateSession (introducer);
}
ScheduleIntroducersUpdateTimer ();
}
}
void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session)
{
m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session };
}
PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce)
{
auto it = m_PeerTests.find (nonce);
if (it != m_PeerTests.end ())
return it->second.role;
else
return ePeerTestParticipantUnknown;
}
std::shared_ptr<SSUSession> SSUServer::GetPeerTestSession (uint32_t nonce)
{
auto it = m_PeerTests.find (nonce);
if (it != m_PeerTests.end ())
return it->second.session;
else
return nullptr;
}
void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role)
{
auto it = m_PeerTests.find (nonce);
if (it != m_PeerTests.end ())
it->second.role = role;
}
void SSUServer::RemovePeerTest (uint32_t nonce)
{
m_PeerTests.erase (nonce);
}
void SSUServer::SchedulePeerTestsCleanupTimer ()
{
m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT));
m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer,
this, std::placeholders::_1));
}
void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
int numDeleted = 0;
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
{
if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL)
{
numDeleted++;
it = m_PeerTests.erase (it);
}
else
it++;
}
if (numDeleted > 0)
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
SchedulePeerTestsCleanupTimer ();
}
}
}
}

119
SSU.h
View File

@@ -1,119 +0,0 @@
#ifndef SSU_H__
#define SSU_H__
#include <inttypes.h>
#include <string.h>
#include <map>
#include <list>
#include <set>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include "Crypto.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "SSUSession.h"
namespace i2p
{
namespace transport
{
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
struct SSUPacket
{
i2p::crypto::AESAlignedBuffer<1500> buf;
boost::asio::ip::udp::endpoint from;
size_t len;
};
class SSUServer
{
public:
SSUServer (int port);
~SSUServer ();
void Start ();
void Stop ();
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest = false);
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
void DeleteSession (std::shared_ptr<SSUSession> session);
void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Service; };
boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; };
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest (uint32_t nonce);
private:
void Run ();
void RunV6 ();
void RunReceivers ();
void Receive ();
void ReceiveV6 ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
void SchedulePeerTestsCleanupTimer ();
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
private:
struct PeerTest
{
uint64_t creationTime;
PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice
};
bool m_IsRunning;
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread;
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService;
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer;
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
public:
// for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
};
}
}
#endif

View File

@@ -1,505 +0,0 @@
#include <stdlib.h>
#include <boost/bind.hpp>
#include "Log.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "SSU.h"
#include "SSUData.h"
namespace i2p
{
namespace transport
{
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
{
if (msg->len + fragmentSize > msg->maxLen)
{
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *msg;
msg = newMsg;
}
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
nextFragmentNum++;
}
SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ()),
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize)
{
}
SSUData::~SSUData ()
{
}
void SSUData::Start ()
{
ScheduleIncompleteMessagesCleanup ();
}
void SSUData::Stop ()
{
m_ResendTimer.cancel ();
m_DecayTimer.cancel ();
m_IncompleteMessagesCleanupTimer.cancel ();
}
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
{
if (remoteRouter) return;
auto ssuAddress = remoteRouter->GetSSUAddress ();
if (ssuAddress && ssuAddress->mtu)
{
if (m_Session.IsV6 ())
m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
else
m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
if (m_PacketSize > 0)
{
// make sure packet size multiple of 16
m_PacketSize >>= 4;
m_PacketSize <<= 4;
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
}
else
{
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu);
m_PacketSize = m_MaxPacketSize;
}
}
}
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
{
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
if (routerInfo)
AdjustPacketSize (routerInfo);
}
void SSUData::ProcessSentMessageAck (uint32_t msgID)
{
auto it = m_SentMessages.find (msgID);
if (it != m_SentMessages.end ())
{
m_SentMessages.erase (it);
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
}
}
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
{
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{
// explicit ACKs
uint8_t numAcks =*buf;
buf++;
for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (bufbe32toh (buf+i*4));
buf += numAcks*4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
{
// explicit ACK bitfields
uint8_t numBitfields =*buf;
buf++;
for (int i = 0; i < numBitfields; i++)
{
uint32_t msgID = bufbe32toh (buf);
buf += 4; // msgID
auto it = m_SentMessages.find (msgID);
// process individual Ack bitfields
bool isNonLast = false;
int fragment = 0;
do
{
uint8_t bitfield = *buf;
isNonLast = bitfield & 0x80;
bitfield &= 0x7F; // clear MSB
if (bitfield && it != m_SentMessages.end ())
{
int numSentFragments = it->second->fragments.size ();
// process bits
uint8_t mask = 0x01;
for (int j = 0; j < 7; j++)
{
if (bitfield & mask)
{
if (fragment < numSentFragments)
it->second->fragments[fragment].reset (nullptr);
}
fragment++;
mask <<= 1;
}
}
buf++;
}
while (isNonLast);
}
}
}
void SSUData::ProcessFragments (uint8_t * buf)
{
uint8_t numFragments = *buf; // number of fragments
buf++;
for (int i = 0; i < numFragments; i++)
{
uint32_t msgID = bufbe32toh (buf); // message ID
buf += 4;
uint8_t frag[4];
frag[0] = 0;
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
{
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
return;
}
// find message with msgID
auto it = m_IncompleteMessages.find (msgID);
if (it == m_IncompleteMessages.end ())
{
// create new message
auto msg = NewI2NPShortMessage ();
msg->len -= I2NP_SHORT_HEADER_SIZE;
it = m_IncompleteMessages.insert (std::make_pair (msgID,
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
}
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
// handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum)
{
// expected fragment
incompleteMessage->AttachNextFragment (buf, fragmentSize);
if (!isLast && !incompleteMessage->savedFragments.empty ())
{
// try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
{
auto& savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
{
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
isLast = savedFragment->isLast;
incompleteMessage->savedFragments.erase (it1++);
}
else
break;
}
if (isLast)
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
}
}
else
{
if (fragmentNum < incompleteMessage->nextFragmentNum)
// duplicate fragment
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
else
{
// missing fragment
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
else
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
}
isLast = false;
}
if (isLast)
{
// delete incomplete message
auto msg = incompleteMessage->msg;
incompleteMessage->msg = nullptr;
m_IncompleteMessages.erase (msgID);
// process message
SendMsgAck (msgID);
msg->FromSSU (msgID);
if (m_Session.GetState () == eSessionStateEstablished)
{
if (!m_ReceivedMessages.count (msgID))
{
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
m_ReceivedMessages.clear ();
else
ScheduleDecay ();
m_ReceivedMessages.insert (msgID);
m_Handler.PutNextMessage (msg);
}
else
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
}
else
{
// we expect DeliveryStatus
if (msg->GetTypeID () == eI2NPDeliveryStatus)
{
LogPrint (eLogDebug, "SSU: session established");
m_Session.Established ();
}
else
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
}
}
else
SendFragmentAck (msgID, fragmentNum);
buf += fragmentSize;
}
}
void SSUData::FlushReceivedMessage ()
{
m_Handler.Flush ();
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
// extended data if presented
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
{
uint8_t extendedDataSize = *buf;
buf++; // size
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
buf += extendedDataSize;
}
// process data
ProcessFragments (buf);
}
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{
uint32_t msgID = msg->ToSSU ();
if (m_SentMessages.count (msgID) > 0)
{
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
return;
}
if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
if (ret.second)
{
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
}
auto& fragments = sentMessage->fragments;
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength ();
uint8_t * msgBuf = msg->GetSSUHeader ();
uint32_t fragmentNum = 0;
while (len > 0)
{
Fragment * fragment = new Fragment;
fragment->fragmentNum = fragmentNum;
uint8_t * buf = fragment->buf;
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++;
*payload = 1; // always 1 message fragment per message
payload++;
htobe32buf (payload, msgID);
payload += 4;
bool isLast = (len <= payloadSize);
size_t size = isLast ? len : payloadSize;
uint32_t fragmentInfo = (fragmentNum << 17);
if (isLast)
fragmentInfo |= 0x010000;
fragmentInfo |= size;
fragmentInfo = htobe32 (fragmentInfo);
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
payload += 3;
memcpy (payload, msgBuf, size);
size += payload - buf;
if (size & 0x0F) // make sure 16 bytes boundary
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
fragment->len = size;
fragments.push_back (std::unique_ptr<Fragment> (fragment));
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
try
{
m_Session.Send (buf, size);
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogError, "SSU: Can't send data fragment ", ec.what ());
}
if (!isLast)
{
len -= payloadSize;
msgBuf += payloadSize;
}
else
len = 0;
fragmentNum++;
}
}
void SSUData::SendMsgAck (uint32_t msgID)
{
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACKs
payload++;
htobe32buf (payload, msgID); // msgID
payload += 4;
*payload = 0; // number of fragments
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
m_Session.Send (buf, 48);
}
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
{
if (fragmentNum > 64)
{
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACK bitfields
payload++;
// one ack
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
payload += 4;
div_t d = div (fragmentNum, 7);
memset (payload, 0x80, d.quot); // 0x80 means non-last
payload += d.quot;
*payload = 0x01 << d.rem; // set corresponding bit
payload++;
*payload = 0; // number of fragments
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send (buf, len);
}
void SSUData::ScheduleResend()
{
m_ResendTimer.cancel ();
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleResendTimer (ecode); });
}
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SentMessages.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)
{
try
{
m_Session.Send (f->buf, f->len); // resend
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogError, "SSU: Can't resend data fragment ", ec.what ());
}
}
it->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
it++;
}
else
{
LogPrint (eLogError, "SSU: message has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
it = m_SentMessages.erase (it);
}
}
else
it++;
}
ScheduleResend ();
}
}
void SSUData::ScheduleDecay ()
{
m_DecayTimer.cancel ();
m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleDecayTimer (ecode); });
}
void SSUData::HandleDecayTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
m_ReceivedMessages.clear ();
}
void SSUData::ScheduleIncompleteMessagesCleanup ()
{
m_IncompleteMessagesCleanupTimer.cancel ();
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
auto s = m_Session.shared_from_this();
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
}
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
{
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
{
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
it = m_IncompleteMessages.erase (it);
}
else
it++;
}
ScheduleIncompleteMessagesCleanup ();
}
}
}
}

128
SSUData.h
View File

@@ -1,128 +0,0 @@
#ifndef SSU_DATA_H__
#define SSU_DATA_H__
#include <inttypes.h>
#include <string.h>
#include <map>
#include <vector>
#include <set>
#include <memory>
#include <boost/asio.hpp>
#include "I2NPProtocol.h"
#include "Identity.h"
#include "RouterInfo.h"
namespace i2p
{
namespace transport
{
const size_t SSU_MTU_V4 = 1484;
const size_t SSU_MTU_V6 = 1472;
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424
const int RESEND_INTERVAL = 3; // in seconds
const int MAX_NUM_RESENDS = 5;
const int DECAY_INTERVAL = 20; // in seconds
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
struct Fragment
{
int fragmentNum;
size_t len;
bool isLast;
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
Fragment () = default;
Fragment (int n, const uint8_t * b, int l, bool last):
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); };
};
struct FragmentCmp
{
bool operator() (const std::unique_ptr<Fragment>& f1, const std::unique_ptr<Fragment>& f2) const
{
return f1->fragmentNum < f2->fragmentNum;
};
};
struct IncompleteMessage
{
std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
};
struct SentMessage
{
std::vector<std::unique_ptr<Fragment> > fragments;
uint32_t nextResendTime; // in seconds
int numResends;
};
class SSUSession;
class SSUData
{
public:
SSUData (SSUSession& session);
~SSUData ();
void Start ();
void Stop ();
void ProcessMessage (uint8_t * buf, size_t len);
void FlushReceivedMessage ();
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
private:
void SendMsgAck (uint32_t msgID);
void SendFragmentAck (uint32_t msgID, int fragmentNum);
void ProcessAcks (uint8_t *& buf, uint8_t flag);
void ProcessFragments (uint8_t * buf);
void ProcessSentMessageAck (uint32_t msgID);
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);
private:
SSUSession& m_Session;
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
std::set<uint32_t> m_ReceivedMessages;
boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer;
int m_MaxPacketSize, m_PacketSize;
i2p::I2NPMessagesHandler m_Handler;
};
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1,444 +0,0 @@
#ifndef SIGNATURE_H__
#define SIGNATURE_H__
#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"
namespace i2p
{
namespace crypto
{
class Verifier
{
public:
virtual ~Verifier () {};
virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0;
virtual size_t GetPublicKeyLen () const = 0;
virtual size_t GetSignatureLen () const = 0;
virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; };
};
class Signer
{
public:
virtual ~Signer () {};
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
};
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
const size_t DSA_SIGNATURE_LENGTH = 40;
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2;
class DSAVerifier: public Verifier
{
public:
DSAVerifier (const uint8_t * signingKey)
{
m_PublicKey = CreateDSA ();
m_PublicKey->pub_key = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
}
~DSAVerifier ()
{
DSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
// calculate SHA1 digest
uint8_t digest[20];
SHA1 (buf, len, digest);
// signature
DSA_SIG * sig = DSA_SIG_new();
sig->r = BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL);
sig->s = BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL);
// DSA verification
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; };
private:
DSA * m_PublicKey;
};
class DSASigner: public Signer
{
public:
DSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey = CreateDSA ();
m_PrivateKey->priv_key = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
}
~DSASigner ()
{
DSA_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[20];
SHA1 (buf, len, digest);
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
bn2buf (sig->r, signature, DSA_SIGNATURE_LENGTH/2);
bn2buf (sig->s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
DSA_SIG_free(sig);
}
private:
DSA * m_PrivateKey;
};
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
DSA * dsa = CreateDSA ();
DSA_generate_key (dsa);
bn2buf (dsa->priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (dsa->pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa);
}
struct SHA256Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA256 (buf, len, digest);
}
enum { hashLen = 32 };
};
struct SHA384Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA384 (buf, len, digest);
}
enum { hashLen = 48 };
};
struct SHA512Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA512 (buf, len, digest);
}
enum { hashLen = 64 };
};
template<typename Hash, int curve, size_t keyLen>
class ECDSAVerifier: public Verifier
{
public:
ECDSAVerifier (const uint8_t * signingKey)
{
m_PublicKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_set_public_key_affine_coordinates (m_PublicKey,
BN_bin2bn (signingKey, keyLen/2, NULL),
BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL));
}
~ECDSAVerifier ()
{
EC_KEY_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_SIG_new();
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return keyLen; };
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
private:
EC_KEY * m_PublicKey;
};
template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer
{
public:
ECDSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL));
}
~ECDSASigner ()
{
EC_KEY_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
// signatureLen = keyLen
bn2buf (sig->r, signature, keyLen/2);
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
ECDSA_SIG_free(sig);
}
private:
EC_KEY * m_PrivateKey;
};
inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_generate_key (signingKey);
bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2);
BIGNUM * x = BN_new(), * y = BN_new();
EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey),
EC_KEY_get0_public_key (signingKey), x, y, NULL);
bn2buf (x, signingPublicKey, keyLen/2);
bn2buf (y, signingPublicKey + keyLen/2, keyLen/2);
BN_free (x); BN_free (y);
EC_KEY_free (signingKey);
}
// ECDSA_SHA256_P256
const size_t ECDSAP256_KEY_LENGTH = 64;
typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA384_P384
const size_t ECDSAP384_KEY_LENGTH = 96;
typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA512_P521
const size_t ECDSAP521_KEY_LENGTH = 132;
typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// RSA
template<typename Hash, int type, size_t keyLen>
class RSAVerifier: public Verifier
{
public:
RSAVerifier (const uint8_t * signingKey)
{
m_PublicKey = RSA_new ();
memset (m_PublicKey, 0, sizeof (RSA));
m_PublicKey->e = BN_dup (GetRSAE ());
m_PublicKey->n = BN_bin2bn (signingKey, keyLen, NULL);
}
~RSAVerifier ()
{
RSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
return RSA_verify (type, digest, Hash::hashLen, signature, GetSignatureLen (), m_PublicKey);
}
size_t GetPublicKeyLen () const { return keyLen; }
size_t GetSignatureLen () const { return keyLen; }
size_t GetPrivateKeyLen () const { return GetSignatureLen ()*2; };
private:
RSA * m_PublicKey;
};
template<typename Hash, int type, size_t keyLen>
class RSASigner: public Signer
{
public:
RSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey = RSA_new ();
memset (m_PrivateKey, 0, sizeof (RSA));
m_PrivateKey->e = BN_dup (GetRSAE ());
m_PrivateKey->n = BN_bin2bn (signingPrivateKey, keyLen, NULL);
m_PrivateKey->d = BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL);
}
~RSASigner ()
{
RSA_free (m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
unsigned int signatureLen = keyLen;
RSA_sign (type, digest, Hash::hashLen, signature, &signatureLen, m_PrivateKey);
}
private:
RSA * m_PrivateKey;
};
inline void CreateRSARandomKeys (size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
RSA * rsa = RSA_new ();
BIGNUM * e = BN_dup (GetRSAE ()); // make it non-const
RSA_generate_key_ex (rsa, publicKeyLen*8, e, NULL);
bn2buf (rsa->n, signingPrivateKey, publicKeyLen);
bn2buf (rsa->d, signingPrivateKey + publicKeyLen, publicKeyLen);
bn2buf (rsa->n, signingPublicKey, publicKeyLen);
BN_free (e); // this e is not assigned to rsa->e
RSA_free (rsa);
}
// RSA_SHA256_2048
const size_t RSASHA2562048_KEY_LENGTH = 256;
typedef RSAVerifier<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Verifier;
typedef RSASigner<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Signer;
// RSA_SHA384_3072
const size_t RSASHA3843072_KEY_LENGTH = 384;
typedef RSAVerifier<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Verifier;
typedef RSASigner<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Signer;
// RSA_SHA512_4096
const size_t RSASHA5124096_KEY_LENGTH = 512;
typedef RSAVerifier<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Verifier;
typedef RSASigner<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Signer;
// EdDSA
struct EDDSAPoint
{
BIGNUM * x, * y;
BIGNUM * z, * t; // projective coordinates
EDDSAPoint (): x(nullptr), y(nullptr), z(nullptr), t(nullptr) {};
EDDSAPoint (const EDDSAPoint& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
{ *this = other; };
EDDSAPoint (EDDSAPoint&& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
{ *this = std::move (other); };
EDDSAPoint (BIGNUM * x1, BIGNUM * y1, BIGNUM * z1 = nullptr, BIGNUM * t1 = nullptr): x(x1), y(y1), z(z1), t(t1) {};
~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); };
EDDSAPoint& operator=(EDDSAPoint&& other)
{
if (x) BN_free (x); x = other.x; other.x = nullptr;
if (y) BN_free (y); y = other.y; other.y = nullptr;
if (z) BN_free (z); z = other.z; other.z = nullptr;
if (t) BN_free (t); t = other.t; other.t = nullptr;
return *this;
}
EDDSAPoint& operator=(const EDDSAPoint& other)
{
if (x) BN_free (x); x = other.x ? BN_dup (other.x) : nullptr;
if (y) BN_free (y); y = other.y ? BN_dup (other.y) : nullptr;
if (z) BN_free (z); z = other.z ? BN_dup (other.z) : nullptr;
if (t) BN_free (t); t = other.t ? BN_dup (other.t) : nullptr;
return *this;
}
EDDSAPoint operator-() const
{
BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL;
if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); };
if (y) y1 = BN_dup (y);
if (z) z1 = BN_dup (z);
if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); };
return EDDSAPoint {x1, y1, z1, t1};
}
};
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
class EDDSA25519Verifier: public Verifier
{
public:
EDDSA25519Verifier (const uint8_t * signingKey);
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
private:
EDDSAPoint m_PublicKey;
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
class EDDSA25519Signer: public Signer
{
public:
EDDSA25519Signer (const uint8_t * signingPrivateKey);
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; };
private:
uint8_t m_ExpandedPrivateKey[64];
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
}
}
}
#endif

View File

@@ -1,933 +0,0 @@
#include <openssl/rand.h>
#include "Log.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "Destination.h"
#include "Streaming.h"
namespace i2p
{
namespace stream
{
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service),
m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service),
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO),
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
{
RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
m_RemoteIdentity = remote->GetIdentity ();
m_CurrentRemoteLease.endDate = 0;
}
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE),
m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
{
RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
}
Stream::~Stream ()
{
Terminate ();
while (!m_ReceiveQueue.empty ())
{
auto packet = m_ReceiveQueue.front ();
m_ReceiveQueue.pop ();
delete packet;
}
for (auto it: m_SentPackets)
delete it;
m_SentPackets.clear ();
for (auto it: m_SavedPackets)
delete it;
m_SavedPackets.clear ();
LogPrint (eLogDebug, "Streaming: Stream deleted");
}
void Stream::Terminate ()
{
m_AckSendTimer.cancel ();
m_ReceiveTimer.cancel ();
m_ResendTimer.cancel ();
if (m_SendHandler)
{
auto handler = m_SendHandler;
m_SendHandler = nullptr;
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted));
}
}
void Stream::HandleNextPacket (Packet * packet)
{
m_NumReceivedBytes += packet->GetLength ();
if (!m_SendStreamID)
m_SendStreamID = packet->GetReceiveStreamID ();
if (!packet->IsNoAck ()) // ack received
ProcessAck (packet);
int32_t receivedSeqn = packet->GetSeqn ();
bool isSyn = packet->IsSYN ();
if (!receivedSeqn && !isSyn)
{
// plain ack
LogPrint (eLogDebug, "Streaming: Plain ACK received");
delete packet;
return;
}
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn);
if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
{
// we have received next in sequence message
ProcessPacket (packet);
// we should also try stored messages if any
for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();)
{
if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1))
{
Packet * savedPacket = *it;
m_SavedPackets.erase (it++);
ProcessPacket (savedPacket);
}
else
break;
}
// schedule ack for last message
if (m_Status == eStreamStatusOpen)
{
if (!m_IsAckSendScheduled)
{
m_IsAckSendScheduled = true;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT));
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1));
}
}
else if (isSyn)
// we have to send SYN back to incoming connection
SendBuffer (); // also sets m_IsOpen
}
else
{
if (receivedSeqn <= m_LastReceivedSequenceNumber)
{
// we have received duplicate
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " received");
SendQuickAck (); // resend ack for previous message again
delete packet; // packet dropped
}
else
{
LogPrint (eLogWarning, "Streaming: Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
// save message and wait for missing message again
SavePacket (packet);
if (m_LastReceivedSequenceNumber >= 0)
{
// send NACKs for missing messages ASAP
if (m_IsAckSendScheduled)
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
}
SendQuickAck ();
}
else
{
// wait for SYN
m_IsAckSendScheduled = true;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT));
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1));
}
}
}
}
void Stream::SavePacket (Packet * packet)
{
m_SavedPackets.insert (packet);
}
void Stream::ProcessPacket (Packet * packet)
{
// process flags
uint32_t receivedSeqn = packet->GetSeqn ();
uint16_t flags = packet->GetFlags ();
LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags);
const uint8_t * optionData = packet->GetOptionData ();
if (flags & PACKET_FLAG_DELAY_REQUESTED)
optionData += 2;
if (flags & PACKET_FLAG_FROM_INCLUDED)
{
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 ());
}
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
{
uint16_t maxPacketSize = bufbe16toh (optionData);
LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize);
optionData += 2;
}
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{
uint8_t signature[256];
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
memcpy (signature, optionData, signatureLen);
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
{
LogPrint (eLogError, "Streaming: Signature verification failed");
Close ();
flags |= PACKET_FLAG_CLOSE;
}
memcpy (const_cast<uint8_t *>(optionData), signature, signatureLen);
optionData += signatureLen;
}
packet->offset = packet->GetPayload () - packet->buf;
if (packet->GetLength () > 0)
{
m_ReceiveQueue.push (packet);
m_ReceiveTimer.cancel ();
}
else
delete packet;
m_LastReceivedSequenceNumber = receivedSeqn;
if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET))
{
m_Status = eStreamStatusReset;
Close ();
}
}
void Stream::ProcessAck (Packet * packet)
{
bool acknowledged = false;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t ackThrough = packet->GetAckThrough ();
int nackCount = packet->GetNACKCount ();
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
{
auto seqn = (*it)->GetSeqn ();
if (seqn <= ackThrough)
{
if (nackCount > 0)
{
bool nacked = false;
for (int i = 0; i < nackCount; i++)
if (seqn == packet->GetNACK (i))
{
nacked = true;
break;
}
if (nacked)
{
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK");
it++;
continue;
}
}
auto sentPacket = *it;
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);
m_SentPackets.erase (it++);
delete sentPacket;
acknowledged = true;
if (m_WindowSize < WINDOW_SIZE)
m_WindowSize++; // slow start
else
{
// linear growth
if (ts > m_LastWindowSizeIncreaseTime + m_RTT)
{
m_WindowSize++;
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
m_LastWindowSizeIncreaseTime = ts;
}
}
}
else
break;
}
if (m_SentPackets.empty ())
m_ResendTimer.cancel ();
if (acknowledged)
{
m_NumResendAttempts = 0;
SendBuffer ();
}
if (m_Status == eStreamStatusClosing)
Close (); // all outgoing messages have been sent
}
size_t Stream::Send (const uint8_t * buf, size_t len)
{
if (len > 0 && buf)
{
std::unique_lock<std::mutex> l(m_SendBufferMutex);
m_SendBuffer.clear ();
m_SendBuffer.write ((const char *)buf, len);
}
m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ()));
return len;
}
void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler)
{
if (m_SendHandler)
handler (boost::asio::error::make_error_code (boost::asio::error::in_progress));
else
m_SendHandler = handler;
Send (buf, len);
}
void Stream::SendBuffer ()
{
int numMsgs = m_WindowSize - m_SentPackets.size ();
if (numMsgs <= 0) return; // window is full
bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
std::vector<Packet *> packets;
{
std::unique_lock<std::mutex> l(m_SendBufferMutex);
while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.eof () && numMsgs > 0))
{
Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer ();
// TODO: implement setters
size_t size = 0;
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
if (isNoAck)
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
else
htobuf32 (packet + size, 0);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
packet[size] = m_RTO/1000;
size++; // resend delay
if (m_Status == eStreamStatusNew)
{
// initial packet
m_Status = eStreamStatusOpen;
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen ();
htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size
size += 2; // options size
m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen);
size += identityLen; // from
htobe16buf (packet + size, STREAMING_MTU);
size += 2; // max packet size
uint8_t * signature = packet + size; // set it later
memset (signature, 0, signatureLen); // zeroes for now
size += signatureLen; // signature
m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size);
size += m_SendBuffer.gcount (); // payload
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
}
else
{
// follow on packet
htobuf16 (packet + size, 0);
size += 2; // flags
htobuf16 (packet + size, 0); // no options
size += 2; // options size
m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size);
size += m_SendBuffer.gcount (); // payload
}
p->len = size;
packets.push_back (p);
numMsgs--;
}
if (m_SendBuffer.eof () && m_SendHandler)
{
m_SendHandler (boost::system::error_code ());
m_SendHandler = nullptr;
}
}
if (packets.size () > 0)
{
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
bool isEmpty = m_SentPackets.empty ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: packets)
{
it->sendTime = ts;
m_SentPackets.insert (it);
}
SendPackets (packets);
if (m_Status == eStreamStatusClosing && m_SendBuffer.eof ())
SendClose ();
if (isEmpty)
ScheduleResend ();
}
}
void Stream::SendQuickAck ()
{
int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber;
if (!m_SavedPackets.empty ())
{
int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn ();
if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn;
}
if (lastReceivedSeqn < 0)
{
LogPrint (eLogError, "Streaming: No packets have been received yet");
return;
}
Packet p;
uint8_t * packet = p.GetBuffer ();
size_t size = 0;
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
htobuf32 (packet + size, 0); // this is plain Ack message
size += 4; // sequenceNum
htobe32buf (packet + size, lastReceivedSeqn);
size += 4; // ack Through
uint8_t numNacks = 0;
if (lastReceivedSeqn > m_LastReceivedSequenceNumber)
{
// fill NACKs
uint8_t * nacks = packet + size + 1;
auto nextSeqn = m_LastReceivedSequenceNumber + 1;
for (auto it: m_SavedPackets)
{
auto seqn = it->GetSeqn ();
if (numNacks + (seqn - nextSeqn) >= 256)
{
LogPrint (eLogError, "Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
htobe32buf (packet + 12, nextSeqn); // change ack Through
break;
}
for (uint32_t i = nextSeqn; i < seqn; i++)
{
htobe32buf (nacks, i);
nacks += 4;
numNacks++;
}
nextSeqn = seqn + 1;
}
packet[size] = numNacks;
size++; // NACK count
size += numNacks*4; // NACKs
}
else
{
// No NACKs
packet[size] = 0;
size++; // NACK count
}
size++; // resend delay
htobuf16 (packet + size, 0); // nof flags set
size += 2; // flags
htobuf16 (packet + size, 0); // no options
size += 2; // options size
p.len = size;
SendPackets (std::vector<Packet *> { &p });
LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs");
}
void Stream::Close ()
{
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");
break;
case eStreamStatusReset:
SendClose ();
Terminate ();
m_LocalDestination.DeleteStream (shared_from_this ());
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);
};
}
void Stream::SendClose ()
{
Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer ();
size_t size = 0;
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
size++; // resend delay
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
size += 2; // flags
size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen ();
htobe16buf (packet + size, signatureLen); // signature only
size += 2; // options size
uint8_t * signature = packet + size;
memset (packet + size, 0, signatureLen);
size += signatureLen; // signature
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
p->len = size;
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
LogPrint (eLogDebug, "Streaming: FIN sent");
}
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
{
size_t pos = 0;
while (pos < len && !m_ReceiveQueue.empty ())
{
Packet * packet = m_ReceiveQueue.front ();
size_t l = std::min (packet->GetLength (), len - pos);
memcpy (buf + pos, packet->GetBuffer (), l);
pos += l;
packet->offset += l;
if (!packet->GetLength ())
{
m_ReceiveQueue.pop ();
delete packet;
}
}
return pos;
}
bool Stream::SendPacket (Packet * packet)
{
if (packet)
{
if (m_IsAckSendScheduled)
{
m_IsAckSendScheduled = false;
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;
return true;
}
else
return false;
}
void Stream::SendPackets (const std::vector<Packet *>& packets)
{
if (!m_RemoteLeaseSet)
{
UpdateCurrentRemoteLease ();
if (!m_RemoteLeaseSet)
{
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet");
return;
}
}
if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ())
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel)
{
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool");
return;
}
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (!m_CurrentRemoteLease.endDate || ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000)
UpdateCurrentRemoteLease (true);
if (ts < m_CurrentRemoteLease.endDate)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
for (auto it: packets)
{
auto msg = m_RoutingSession->WrapSingleMessage (CreateDataMessage (it->GetBuffer (), it->GetLength ()));
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID,
msg
});
m_NumSentBytes += it->GetLength ();
}
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
}
else
LogPrint (eLogWarning, "Streaming: All leases are expired");
}
void Stream::ScheduleResend ()
{
m_ResendTimer.cancel ();
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
shared_from_this (), std::placeholders::_1));
}
void Stream::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
// 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");
m_Status = eStreamStatusReset;
Close ();
return;
}
// collect packets to resend
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<Packet *> packets;
for (auto it : m_SentPackets)
{
if (ts >= it->sendTime + m_RTO)
{
it->sendTime = ts;
packets.push_back (it);
}
}
// select tunnels if necessary and send
if (packets.size () > 0)
{
m_NumResendAttempts++;
m_RTO *= 2;
switch (m_NumResendAttempts)
{
case 1: // congesion avoidance
m_WindowSize /= 2;
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
break;
case 2:
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time
// no break here
case 4:
UpdateCurrentRemoteLease (); // pick another lease
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream");
break;
case 3:
// pick another outbound tunnel
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream");
break;
default: ;
}
SendPackets (packets);
}
ScheduleResend ();
}
}
void Stream::HandleAckSendTimer (const boost::system::error_code& ecode)
{
if (m_IsAckSendScheduled)
{
if (m_LastReceivedSequenceNumber < 0)
{
LogPrint (eLogWarning, "Streaming: SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate");
m_Status = eStreamStatusReset;
Close ();
return;
}
if (m_Status == eStreamStatusOpen)
SendQuickAck ();
m_IsAckSendScheduled = false;
}
}
void Stream::UpdateCurrentRemoteLease (bool expired)
{
if (!m_RemoteLeaseSet)
{
m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!m_RemoteLeaseSet)
LogPrint (eLogError, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found");
}
if (m_RemoteLeaseSet)
{
if (!m_RoutingSession)
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, 32);
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first
if (leases.empty ())
{
expired = false;
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to re-request
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
}
if (!leases.empty ())
{
bool updated = false;
if (expired)
{
for (auto it: leases)
if ((it.tunnelGateway == m_CurrentRemoteLease.tunnelGateway) && (it.tunnelID != m_CurrentRemoteLease.tunnelID))
{
m_CurrentRemoteLease = it;
updated = true;
break;
}
}
if (!updated)
{
uint32_t i = rand () % leases.size ();
if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID)
// make sure we don't select previous
i = (i + 1) % leases.size (); // if so, pick next
m_CurrentRemoteLease = leases[i];
}
}
else
{
m_RemoteLeaseSet = nullptr;
m_CurrentRemoteLease.endDate = 0;
// re-request expired
}
}
else
m_CurrentRemoteLease.endDate = 0;
}
std::shared_ptr<I2NPMessage> Stream::CreateDataMessage (const uint8_t * payload, size_t len)
{
auto msg = NewI2NPShortMessage ();
if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)
m_LocalDestination.m_Deflator.SetCompressionLevel (Z_NO_COMPRESSION);
else
m_LocalDestination.m_Deflator.SetCompressionLevel (Z_DEFAULT_COMPRESSION);
uint8_t * buf = msg->GetPayload ();
buf += 4; // reserve for lengthlength
msg->len += 4;
size_t size = m_LocalDestination.m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
if (size)
{
htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, m_LocalDestination.GetLocalPort ()); // source port
htobe16buf (buf + 6, m_Port); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
msg->len += size;
msg->FillI2NPMessageHeader (eI2NPData);
}
else
msg = nullptr;
return msg;
}
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort):
m_Owner (owner), m_LocalPort (localPort), m_PendingIncomingTimer (m_Owner->GetService ())
{
}
StreamingDestination::~StreamingDestination ()
{
}
void StreamingDestination::Start ()
{
}
void StreamingDestination::Stop ()
{
ResetAcceptor ();
m_PendingIncomingTimer.cancel ();
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams.clear ();
}
}
void StreamingDestination::HandleNextPacket (Packet * packet)
{
uint32_t sendStreamID = packet->GetSendStreamID ();
if (sendStreamID)
{
auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ())
it->second->HandleNextPacket (packet);
else
{
LogPrint (eLogError, "Streaming: Unknown stream sendStreamID=", sendStreamID);
delete packet;
}
}
else
{
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{
auto incomingStream = CreateNewIncomingStream ();
incomingStream->HandleNextPacket (packet);
if (m_Acceptor != nullptr)
m_Acceptor (incomingStream);
else
{
LogPrint (eLogWarning, "Streaming: Acceptor for incoming stream is not set");
if (m_PendingIncomingStreams.size () < MAX_PENDING_INCOMING_BACKLOG)
{
m_PendingIncomingStreams.push_back (incomingStream);
m_PendingIncomingTimer.cancel ();
m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT));
m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer,
this, std::placeholders::_1));
LogPrint (eLogDebug, "Streaming: Pending incoming stream added");
}
else
{
LogPrint (eLogWarning, "Streaming: Pending incoming streams backlog exceeds ", MAX_PENDING_INCOMING_BACKLOG);
incomingStream->Close ();
}
}
}
else // follow on packet without SYN
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
for (auto it: m_Streams)
if (it.second->GetSendStreamID () == receiveStreamID)
{
// found
it.second->HandleNextPacket (packet);
return;
}
// TODO: should queue it up
LogPrint (eLogError, "Streaming: Unknown stream receiveStreamID=", receiveStreamID);
delete packet;
}
}
}
std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
}
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
{
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
return s;
}
void StreamingDestination::DeleteStream (std::shared_ptr<Stream> stream)
{
if (stream)
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
auto it = m_Streams.find (stream->GetRecvStreamID ());
if (it != m_Streams.end ())
m_Streams.erase (it);
}
}
void StreamingDestination::SetAcceptor (const Acceptor& acceptor)
{
m_Owner->GetService ().post([acceptor, this](void)
{
m_Acceptor = acceptor;
for (auto it: m_PendingIncomingStreams)
if (it->GetStatus () == eStreamStatusOpen) // still open?
m_Acceptor (it);
m_PendingIncomingStreams.clear ();
m_PendingIncomingTimer.cancel ();
});
}
void StreamingDestination::ResetAcceptor ()
{
if (m_Acceptor) m_Acceptor (nullptr);
m_Acceptor = nullptr;
}
void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired");
for (auto it: m_PendingIncomingStreams)
it->Close ();
m_PendingIncomingStreams.clear ();
}
}
void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
{
// unzip it
Packet * uncompressed = new Packet;
uncompressed->offset = 0;
uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE);
if (uncompressed->len)
HandleNextPacket (uncompressed);
else
delete uncompressed;
}
}
}

View File

@@ -1,279 +0,0 @@
#ifndef STREAMING_H__
#define STREAMING_H__
#include <inttypes.h>
#include <string>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <functional>
#include <memory>
#include <mutex>
#include <boost/asio.hpp>
#include "Base.h"
#include "I2PEndian.h"
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
#include "Garlic.h"
#include "Tunnel.h"
namespace i2p
{
namespace client
{
class ClientDestination;
}
namespace stream
{
const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001;
const uint16_t PACKET_FLAG_CLOSE = 0x0002;
const uint16_t PACKET_FLAG_RESET = 0x0004;
const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008;
const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010;
const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020;
const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040;
const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080;
const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100;
const uint16_t PACKET_FLAG_ECHO = 0x0200;
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
const size_t STREAMING_MTU = 1730;
const size_t MAX_PACKET_SIZE = 4096;
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int ACK_SEND_TIMEOUT = 200; // in milliseconds
const int MAX_NUM_RESEND_ATTEMPTS = 6;
const int WINDOW_SIZE = 6; // in messages
const int MIN_WINDOW_SIZE = 1;
const int MAX_WINDOW_SIZE = 128;
const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
struct Packet
{
size_t len, offset;
uint8_t buf[MAX_PACKET_SIZE];
uint64_t sendTime;
Packet (): len (0), offset (0), sendTime (0) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return bufbe32toh (buf); };
uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
uint32_t GetSeqn () const { return bufbe32toh (buf + 8); };
uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); };
uint8_t GetNACKCount () const { return buf[16]; };
uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); };
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); };
uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); };
const uint8_t * GetOptionData () const { return GetOption () + 2; };
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; };
bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; };
};
struct PacketCmp
{
bool operator() (const Packet * p1, const Packet * p2) const
{
return p1->GetSeqn () < p2->GetSeqn ();
};
};
enum StreamStatus
{
eStreamStatusNew = 0,
eStreamStatusOpen,
eStreamStatusReset,
eStreamStatusClosing,
eStreamStatusClosed
};
class StreamingDestination;
class Stream: public std::enable_shared_from_this<Stream>
{
public:
typedef std::function<void (const boost::system::error_code& ecode)> SendHandler;
Stream (boost::asio::io_service& service, StreamingDestination& local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
~Stream ();
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void HandleNextPacket (Packet * packet);
size_t Send (const uint8_t * buf, size_t len);
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
void Close ();
void Cancel () { m_ReceiveTimer.cancel (); };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
size_t GetSendQueueSize () const { return m_SentPackets.size (); };
size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); };
size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); };
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
private:
void Terminate ();
void SendBuffer ();
void SendQuickAck ();
void SendClose ();
bool SendPacket (Packet * packet);
void SendPackets (const std::vector<Packet *>& packets);
void SavePacket (Packet * packet);
void ProcessPacket (Packet * packet);
void ProcessAck (Packet * packet);
size_t ConcatenatePackets (uint8_t * buf, size_t len);
void UpdateCurrentRemoteLease (bool expired = false);
template<typename Buffer, typename ReceiveHandler>
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void HandleAckSendTimer (const boost::system::error_code& ecode);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len);
private:
boost::asio::io_service& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
int32_t m_LastReceivedSequenceNumber;
StreamStatus m_Status;
bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
i2p::data::Lease m_CurrentRemoteLease;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
std::set<Packet *, PacketCmp> m_SentPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port;
std::mutex m_SendBufferMutex;
std::stringstream m_SendBuffer;
int m_WindowSize, m_RTT, m_RTO;
uint64_t m_LastWindowSizeIncreaseTime;
int m_NumResendAttempts;
SendHandler m_SendHandler;
};
class StreamingDestination
{
public:
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0);
~StreamingDestination ();
void Start ();
void Stop ();
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void DeleteStream (std::shared_ptr<Stream> stream);
void SetAcceptor (const Acceptor& acceptor);
void ResetAcceptor ();
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
uint16_t GetLocalPort () const { return m_LocalPort; };
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
private:
void HandleNextPacket (Packet * packet);
std::shared_ptr<Stream> CreateNewIncomingStream ();
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
uint16_t m_LocalPort;
std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams;
Acceptor m_Acceptor;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
public:
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
// for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
};
//-------------------------------------------------
template<typename Buffer, typename ReceiveHandler>
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
{
auto s = shared_from_this();
m_Service.post ([=](void)
{
if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset)
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler);
else
{
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
{ s->HandleReceiveTimer (ecode, buffer, handler); });
}
});
}
template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
{
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
if (received > 0)
handler (boost::system::error_code (), received);
else if (ecode == boost::asio::error::operation_aborted)
{
// timeout not expired
if (m_Status == eStreamStatusReset)
handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0);
else
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
}
else
// timeout expired
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
}
}
}
#endif

16
TODO Normal file
View File

@@ -0,0 +1,16 @@
Refactoring:
- SSUSession:637, SSUSession:635 get rid of casting to SSUHeader
- Identity.cpp:156 check for self asignment
- Rely on a library for TLS and SSL.
- Move parsing code out of networking code, to allow better testing.
- Move streaming code to a separate directory.
- Separate front-end code (SAM, BOB, ...) from the back-end.
Additions:
- Write tests.
- Add documentation.
- Create a specialized IRC tunnel (instead of just a generic ClientTunnel instance).
- Ban peers with unexpected / bad behaviour.
- EdDSA support.
- Return correct error codes in HTTPProxy.
- Modify web interface layout and use AJAX to update information.

View File

@@ -1,32 +0,0 @@
#ifndef TIMESTAMP_H__
#define TIMESTAMP_H__
#include <inttypes.h>
#include <chrono>
namespace i2p
{
namespace util
{
inline uint64_t GetMillisecondsSinceEpoch ()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count ();
}
inline uint32_t GetHoursSinceEpoch ()
{
return std::chrono::duration_cast<std::chrono::hours>(
std::chrono::system_clock::now().time_since_epoch()).count ();
}
inline uint64_t GetSecondsSinceEpoch ()
{
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count ();
}
}
}
#endif

View File

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

View File

@@ -1,103 +0,0 @@
#ifndef TRANSIT_TUNNEL_H__
#define TRANSIT_TUNNEL_H__
#include <inttypes.h>
#include <vector>
#include <mutex>
#include <memory>
#include "Crypto.h"
#include "I2NPProtocol.h"
#include "TunnelEndpoint.h"
#include "TunnelGateway.h"
#include "TunnelBase.h"
namespace i2p
{
namespace tunnel
{
class TransitTunnel: public TunnelBase
{
public:
TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey);
virtual size_t GetNumTransmittedBytes () const { return 0; };
// implements TunnelBase
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
private:
i2p::crypto::TunnelEncryption m_Encryption;
};
class TransitTunnelParticipant: public TransitTunnel
{
public:
TransitTunnelParticipant (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes (0) {};
~TransitTunnelParticipant ();
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
void FlushTunnelDataMsgs ();
private:
size_t m_NumTransmittedBytes;
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
};
class TransitTunnelGateway: public TransitTunnel
{
public:
TransitTunnelGateway (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void FlushTunnelDataMsgs ();
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
private:
std::mutex m_SendMutex;
TunnelGateway m_Gateway;
};
class TransitTunnelEndpoint: public TransitTunnel
{
public:
TransitTunnelEndpoint (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
private:
TunnelEndpoint m_Endpoint;
};
TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
bool isGateway, bool isEndpoint);
}
}
#endif

View File

@@ -1,85 +0,0 @@
#ifndef TRANSPORT_SESSION_H__
#define TRANSPORT_SESSION_H__
#include <inttypes.h>
#include <iostream>
#include <memory>
#include <vector>
#include "Identity.h"
#include "Crypto.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
namespace i2p
{
namespace transport
{
class SignedData
{
public:
SignedData () {}
SignedData (const SignedData& other)
{
m_Stream << other.m_Stream.rdbuf ();
}
void Insert (const uint8_t * buf, size_t len)
{
m_Stream.write ((char *)buf, len);
}
template<typename T>
void Insert (T t)
{
m_Stream.write ((char *)&t, sizeof (T));
}
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
{
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
}
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
{
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
}
private:
std::stringstream m_Stream;
};
class TransportSession
{
public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router)
{
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
}
virtual ~TransportSession () {};
virtual void Done () = 0;
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
protected:
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
};
}
}
#endif

View File

@@ -1,568 +0,0 @@
#include <openssl/dh.h>
#include "Log.h"
#include "Crypto.h"
#include "RouterContext.h"
#include "I2NPProtocol.h"
#include "NetDb.h"
#include "Transports.h"
using namespace i2p::data;
namespace i2p
{
namespace transport
{
DHKeysPairSupplier::DHKeysPairSupplier (int size):
m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr)
{
}
DHKeysPairSupplier::~DHKeysPairSupplier ()
{
Stop ();
}
void DHKeysPairSupplier::Start ()
{
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&DHKeysPairSupplier::Run, this));
}
void DHKeysPairSupplier::Stop ()
{
m_IsRunning = false;
m_Acquired.notify_one ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
}
void DHKeysPairSupplier::Run ()
{
while (m_IsRunning)
{
int num;
while ((num = m_QueueSize - m_Queue.size ()) > 0)
CreateDHKeysPairs (num);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Acquired.wait (l); // wait for element gets aquired
}
}
void DHKeysPairSupplier::CreateDHKeysPairs (int num)
{
if (num > 0)
{
i2p::crypto::DHKeys dh;
for (int i = 0; i < num; i++)
{
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
}
}
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
{
if (!m_Queue.empty ())
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
auto pair = m_Queue.front ();
m_Queue.pop ();
m_Acquired.notify_one ();
return pair;
}
else // queue is empty, create new
{
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
return pair;
}
}
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
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_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)
{
}
Transports::~Transports ()
{
Stop ();
}
void Transports::Start ()
{
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)
{
if (!m_NTCPServer)
{
m_NTCPServer = new NTCPServer (address.port);
m_NTCPServer->Start ();
}
if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
{
if (!m_SSUServer)
{
m_SSUServer = new SSUServer (address.port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address.port);
m_SSUServer->Start ();
DetectExternalIP ();
}
else
LogPrint (eLogError, "Transports: SSU server already exists");
}
}
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
}
void Transports::Stop ()
{
m_PeerCleanupTimer.cancel ();
m_Peers.clear ();
if (m_SSUServer)
{
m_SSUServer->Stop ();
delete m_SSUServer;
m_SSUServer = nullptr;
}
if (m_NTCPServer)
{
m_NTCPServer->Stop ();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
m_DHKeysPairSupplier.Stop ();
m_IsRunning = false;
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void Transports::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Transports: runtime exception: ", ex.what ());
}
}
}
void Transports::UpdateBandwidth ()
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_LastBandwidthUpdateTime > 0)
{
auto delta = ts - m_LastBandwidthUpdateTime;
if (delta > 0)
{
m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second
m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second
}
}
m_LastBandwidthUpdateTime = ts;
m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes;
m_LastOutBandwidthUpdateBytes = m_TotalSentBytes;
}
bool Transports::IsBandwidthExceeded () const
{
if (i2p::context.GetRouterInfo ().IsExtraBandwidth ()) return false;
auto bw = std::max (m_InBandwidth, m_OutBandwidth);
return bw > (i2p::context.GetRouterInfo ().IsHighBandwidth () ? HIGH_BANDWIDTH_LIMIT : LOW_BANDWIDTH_LIMIT);
}
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
{
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
}
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
{
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
}
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs)
{
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
{
// we send it to ourself
for (auto it: msgs)
i2p::HandleI2NPMessage (it);
return;
}
auto it = m_Peers.find (ident);
if (it == m_Peers.end ())
{
bool connected = false;
try
{
auto r = netdb.FindRouter (ident);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
i2p::util::GetSecondsSinceEpoch (), {} })).first;
connected = ConnectToPeer (ident, it->second);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ());
}
if (!connected) return;
}
if (!it->second.sessions.empty ())
it->second.sessions.front ()->SendI2NPMessages (msgs);
else
{
for (auto it1: msgs)
it->second.delayedMessages.push_back (it1);
}
}
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
{
if (peer.router) // we have RI already
{
if (!peer.numAttempts) // NTCP
{
peer.numAttempts++;
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
if (address)
{
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
#else
boost::system::error_code ecode;
address->host.to_string (ecode);
if (!ecode)
#endif
{
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address->host, address->port, s);
return true;
}
}
else // we don't have address
{
if (address->addressString.length () > 0) // trying to resolve
{
LogPrint (eLogDebug, "Transports: Resolving NTCP ", address->addressString);
NTCPResolve (address->addressString, ident);
return true;
}
}
}
else
LogPrint (eLogWarning, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
}
if (peer.numAttempts == 1)// SSU
{
peer.numAttempts++;
if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ()))
{
auto address = peer.router->GetSSUAddress (!context.SupportsV6 ());
#if BOOST_VERSION >= 104900
if (!address->host.is_unspecified ()) // we have address now
#else
boost::system::error_code ecode;
address->host.to_string (ecode);
if (!ecode)
#endif
{
m_SSUServer->CreateSession (peer.router, address->host, address->port);
return true;
}
else // we don't have address
{
if (address->addressString.length () > 0) // trying to resolve
{
LogPrint (eLogDebug, "Transports: Resolving SSU ", address->addressString);
SSUResolve (address->addressString, ident);
return true;
}
}
}
}
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
peer.Done ();
m_Peers.erase (ident);
return false;
}
else // otherwise request RI
{
LogPrint (eLogInfo, "Transports: RouterInfo for ", ident.ToBase64 (), " not found, requested");
i2p::data::netdb.RequestDestination (ident, std::bind (
&Transports::RequestComplete, this, std::placeholders::_1, ident));
}
return true;
}
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
{
m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
}
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident)
{
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
if (r)
{
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, Trying to connect");
it->second.router = r;
ConnectToPeer (ident, it->second);
}
else
{
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
m_Peers.erase (it);
}
}
}
void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident)
{
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
std::bind (&Transports::HandleNTCPResolve, this,
std::placeholders::_1, std::placeholders::_2, ident, resolver));
}
void Transports::HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
{
auto it1 = m_Peers.find (ident);
if (it1 != m_Peers.end ())
{
auto& peer = it1->second;
if (!ecode && peer.router)
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetNTCPAddress ();
if (addr)
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address, addr->port, s);
return;
}
}
LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ());
m_Peers.erase (it1);
}
}
void Transports::SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident)
{
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
std::bind (&Transports::HandleSSUResolve, this,
std::placeholders::_1, std::placeholders::_2, ident, resolver));
}
void Transports::HandleSSUResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
{
auto it1 = m_Peers.find (ident);
if (it1 != m_Peers.end ())
{
auto& peer = it1->second;
if (!ecode && peer.router)
{
auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetSSUAddress (!context.SupportsV6 ());;
if (addr)
{
m_SSUServer->CreateSession (peer.router, address, addr->port);
return;
}
}
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
m_Peers.erase (it1);
}
}
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
if (ssuSession) // try SSU first
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint (eLogDebug, "Transports: SSU session closed");
}
// TODO: delete NTCP
}
void Transports::DetectExternalIP ()
{
if (m_SSUServer)
{
i2p::context.SetStatus (eRouterStatusTesting);
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
if (router && router->IsSSU (!context.SupportsV6 ()))
m_SSUServer->CreateSession (router, true); // peer test
else
{
// if not peer test capable routers found pick any
router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU ())
m_SSUServer->CreateSession (router); // no peer test
}
}
}
else
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
}
void Transports::PeerTest ()
{
if (m_SSUServer)
{
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
if (router && router->IsSSU (!context.SupportsV6 ()))
{
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, true); // peer test
}
}
}
}
std::shared_ptr<i2p::crypto::DHKeys> Transports::GetNextDHKeysPair ()
{
return m_DHKeysPairSupplier.Acquire ();
}
void Transports::ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
m_DHKeysPairSupplier.Return (pair);
}
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
if (!session->GetRemoteIdentity ()) return;
auto ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
it->second.sessions.push_back (session);
session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear ();
}
else // incoming connection
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
});
}
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
if (!session->GetRemoteIdentity ()) return;
auto ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
it->second.sessions.remove (session);
if (it->second.sessions.empty ()) // TODO: why?
{
if (it->second.delayedMessages.size () > 0)
ConnectToPeer (ident, it->second);
else
m_Peers.erase (it);
}
}
});
}
bool Transports::IsConnected (const i2p::data::IdentHash& ident) const
{
auto it = m_Peers.find (ident);
return it != m_Peers.end ();
}
void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Peers.begin (); it != m_Peers.end (); )
{
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
{
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
it = m_Peers.erase (it);
}
else
it++;
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
DetectExternalIP ();
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
}
}
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{
if (!m_Peers.size ()) return nullptr;
auto it = m_Peers.begin ();
std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr;
}
}
}

View File

@@ -1,156 +0,0 @@
#ifndef TRANSPORTS_H__
#define TRANSPORTS_H__
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <map>
#include <vector>
#include <queue>
#include <string>
#include <memory>
#include <atomic>
#include <boost/asio.hpp>
#include "TransportSession.h"
#include "NTCPSession.h"
#include "SSU.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "Identity.h"
namespace i2p
{
namespace transport
{
class DHKeysPairSupplier
{
public:
DHKeysPairSupplier (int size);
~DHKeysPairSupplier ();
void Start ();
void Stop ();
std::shared_ptr<i2p::crypto::DHKeys> Acquire ();
void Return (std::shared_ptr<i2p::crypto::DHKeys> pair);
private:
void Run ();
void CreateDHKeysPairs (int num);
private:
const int m_QueueSize;
std::queue<std::shared_ptr<i2p::crypto::DHKeys> > m_Queue;
bool m_IsRunning;
std::thread * m_Thread;
std::condition_variable m_Acquired;
std::mutex m_AcquiredMutex;
};
struct Peer
{
int numAttempts;
std::shared_ptr<const i2p::data::RouterInfo> router;
std::list<std::shared_ptr<TransportSession> > sessions;
uint64_t creationTime;
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
void Done ()
{
for (auto it: sessions)
it->Done ();
}
};
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
const uint32_t LOW_BANDWIDTH_LIMIT = 32*1024; // 32KBs
const uint32_t HIGH_BANDWIDTH_LIMIT = 256*1024; // 256KBs
class Transports
{
public:
Transports ();
~Transports ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
void PeerConnected (std::shared_ptr<TransportSession> session);
void PeerDisconnected (std::shared_ptr<TransportSession> session);
bool IsConnected (const i2p::data::IdentHash& ident) const;
void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; };
void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; };
uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; };
uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; };
uint32_t GetInBandwidth () const { return m_InBandwidth; }; // bytes per second
uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; // bytes per second
bool IsBandwidthExceeded () const;
size_t GetNumPeers () const { return m_Peers.size (); };
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
void PeerTest ();
private:
void Run ();
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
void SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident);
void HandleSSUResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
void UpdateBandwidth ();
void DetectExternalIP ();
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_PeerCleanupTimer;
NTCPServer * m_NTCPServer;
SSUServer * m_SSUServer;
std::map<i2p::data::IdentHash, Peer> m_Peers;
DHKeysPairSupplier m_DHKeysPairSupplier;
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes;
uint32_t m_InBandwidth, m_OutBandwidth;
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
uint64_t m_LastBandwidthUpdateTime;
public:
// for HTTP only
const NTCPServer * GetNTCPServer () const { return m_NTCPServer; };
const SSUServer * GetSSUServer () const { return m_SSUServer; };
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
};
extern Transports transports;
}
}
#endif

View File

@@ -1,789 +0,0 @@
#include <string.h>
#include "I2PEndian.h"
#include <thread>
#include <algorithm>
#include <vector>
#include <openssl/rand.h>
#include "RouterContext.h"
#include "Log.h"
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "Transports.h"
#include "NetDb.h"
#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)
{
auto numHops = m_Config->GetNumHops ();
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
auto msg = NewI2NPShortMessage ();
*msg->GetPayload () = numRecords;
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
// shuffle records
std::vector<int> recordIndicies;
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
std::random_shuffle (recordIndicies.begin(), recordIndicies.end());
// create real records
uint8_t * records = msg->GetPayload () + 1;
TunnelHopConfig * hop = m_Config->GetFirstHop ();
int i = 0;
while (hop)
{
uint32_t msgID;
if (hop->next) // we set replyMsgID for last hop only
RAND_bytes ((uint8_t *)&msgID, 4);
else
msgID = replyMsgID;
int idx = recordIndicies[i];
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID);
hop->recordIndex = idx;
i++;
hop = hop->next;
}
// 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;
hop = m_Config->GetLastHop ()->prev;
while (hop)
{
decryption.SetKey (hop->replyKey);
// 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);
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;
while (hop1)
{
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);
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelBuildResponse (ret);
if (ret)
// if any of participants declined the tunnel is not established
established = false;
hop = hop->next;
}
if (established)
{
// create tunnel decryptions from layer and iv keys in reverse order
hop = m_Config->GetLastHop ();
while (hop)
{
auto tunnelHop = new TunnelHop;
tunnelHop->ident = hop->ident;
tunnelHop->decryption.SetKeys (hop->layerKey, hop->ivKey);
m_Hops.push_back (std::unique_ptr<TunnelHop>(tunnelHop));
hop = hop->prev;
}
m_Config = nullptr;
}
if (established) m_State = eTunnelStateEstablished;
return established;
}
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
{
const uint8_t * inPayload = in->GetPayload () + 4;
uint8_t * outPayload = out->GetPayload () + 4;
for (auto& it: m_Hops)
{
it->decryption.Decrypt (inPayload, outPayload);
inPayload = outPayload;
}
}
void Tunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
LogPrint (eLogInfo, "Tunnel: Can't send I2NP messages without delivery instructions");
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetPeers () const
{
auto peers = GetInvertedPeers ();
std::reverse (peers.begin (), peers.end ());
return peers;
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetInvertedPeers () const
{
// hops are in inverted order
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > ret;
for (auto& it: m_Hops)
ret.push_back (it->ident);
return ret;
}
void Tunnel::PrintHops (std::stringstream& s) const
{
for (auto& it: m_Hops)
{
s << "-->";
s << i2p::data::GetIdentHashAbbreviation (it->ident->GetIdentHash ());
}
}
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
{
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
auto newMsg = CreateEmptyTunnelDataMsg ();
EncryptTunnelMsg (msg, newMsg);
newMsg->from = shared_from_this ();
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
}
void InboundTunnel::Print (std::stringstream& s) const
{
PrintHops (s);
s << "-->" << GetTunnelID () << ":me";
}
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
{
TunnelMessageBlock block;
if (gwHash)
{
block.hash = gwHash;
if (gwTunnel)
{
block.deliveryType = eDeliveryTypeTunnel;
block.tunnelID = gwTunnel;
}
else
block.deliveryType = eDeliveryTypeRouter;
}
else
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.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, "Incoming message for outbound tunnel ", GetTunnelID ());
}
void OutboundTunnel::Print (std::stringstream& s) const
{
s << GetTunnelID () << ":me";
PrintHops (s);
s << "-->";
}
Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0)
{
}
Tunnels::~Tunnels ()
{
for (auto& it : m_TransitTunnels)
delete it.second;
m_TransitTunnels.clear ();
}
std::shared_ptr<InboundTunnel> Tunnels::GetInboundTunnel (uint32_t tunnelID)
{
auto it = m_InboundTunnels.find(tunnelID);
if (it != m_InboundTunnels.end ())
return it->second;
return nullptr;
}
TransitTunnel * Tunnels::GetTransitTunnel (uint32_t tunnelID)
{
auto it = m_TransitTunnels.find(tunnelID);
if (it != m_TransitTunnels.end ())
return it->second;
return nullptr;
}
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
{
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
}
std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID)
{
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
}
template<class TTunnel>
std::shared_ptr<TTunnel> Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels)
{
auto it = pendingTunnels.find(replyMsgID);
if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
{
it->second->SetState (eTunnelStateBuildReplyReceived);
return it->second;
}
return nullptr;
}
std::shared_ptr<InboundTunnel> Tunnels::GetNextInboundTunnel ()
{
std::shared_ptr<InboundTunnel> tunnel;
size_t minReceived = 0;
for (auto it : m_InboundTunnels)
{
if (!it.second->IsEstablished ()) continue;
if (!tunnel || it.second->GetNumReceivedBytes () < minReceived)
{
tunnel = it.second;
minReceived = it.second->GetNumReceivedBytes ();
}
}
return tunnel;
}
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
{
if (!m_OutboundTunnels.size ()) return nullptr;
uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0;
std::shared_ptr<OutboundTunnel> tunnel;
for (auto it: m_OutboundTunnels)
{
if (it->IsEstablished ())
{
tunnel = it;
i++;
}
if (i > ind && tunnel) break;
}
return tunnel;
}
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops,
int numOutboundHops, int numInboundTunnels, int numOutboundTunnels)
{
auto pool = std::make_shared<TunnelPool> (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels);
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)
{
if (pool)
{
pool->SetActive (false);
pool->DetachTunnels ();
}
}
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
{
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
if (!m_TransitTunnels.insert (std::make_pair (tunnel->GetTunnelID (), tunnel)).second)
{
LogPrint (eLogError, "Transit tunnel ", tunnel->GetTunnelID (), " already exists");
delete tunnel;
}
}
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;
TunnelBase * prevTunnel = nullptr;
do
{
TunnelBase * tunnel = nullptr;
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 && typeID == eI2NPTunnelData)
tunnel = GetInboundTunnel (tunnelID).get ();
if (!tunnel)
tunnel = GetTransitTunnel (tunnelID);
if (tunnel)
{
if (typeID == eI2NPTunnelData)
tunnel->HandleTunnelDataMsg (msg);
else // tunnel gateway assumed
HandleTunnelGatewayMsg (tunnel, msg);
}
else
LogPrint (eLogWarning, "Tunnel ", tunnelID, " not found");
break;
}
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
break;
default:
LogPrint (eLogError, "Tunnel: Unexpected messsage type ", (int)typeID);
}
msg = m_Queue.Get ();
if (msg)
{
prevTunnelID = tunnelID;
prevTunnel = tunnel;
}
else if (tunnel)
tunnel->FlushTunnelDataMsgs ();
}
while (msg);
}
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastTs >= 15) // manage tunnels every 15 seconds
{
ManageTunnels ();
lastTs = ts;
}
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Tunnel: runtime exception: ", ex.what ());
}
}
}
void Tunnels::HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr<I2NPMessage> msg)
{
if (!tunnel)
{
LogPrint (eLogError, "Missing tunnel for TunnelGateway");
return;
}
const uint8_t * payload = msg->GetPayload ();
uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET);
// we make payload as new I2NP message to send
msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->len = msg->offset + len;
auto typeID = msg->GetTypeID ();
LogPrint (eLogDebug, "TunnelGateway of ", (int)len, " bytes for tunnel ", tunnel->GetTunnelID (), ". Msg type ", (int)typeID);
if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply)
// transit DatabaseStore my contain new/updated RI
// or DatabaseSearchReply with new routers
i2p::data::netdb.PostI2NPMsg (msg);
tunnel->SendTunnelDataMsg (msg);
}
void Tunnels::ManageTunnels ()
{
ManagePendingTunnels ();
ManageInboundTunnels ();
ManageOutboundTunnels ();
ManageTransitTunnels ();
ManageTunnelPools ();
}
void Tunnels::ManagePendingTunnels ()
{
ManagePendingTunnels (m_PendingInboundTunnels);
ManagePendingTunnels (m_PendingOutboundTunnels);
}
template<class PendingTunnels>
void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels)
{
// check pending tunnel. delete failed or timeout
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
{
auto tunnel = it->second;
switch (tunnel->GetState ())
{
case eTunnelStatePending:
if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT)
{
LogPrint (eLogError, "Tunnel: Pending build request ", it->first, " timeout, deleted");
// update stats
auto config = tunnel->GetTunnelConfig ();
if (config)
{
auto hop = config->GetFirstHop ();
while (hop)
{
if (hop->ident)
{
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelNonReplied ();
}
hop = hop->next;
}
}
// delete
it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++;
}
else
it++;
break;
case eTunnelStateBuildFailed:
LogPrint (eLogError, "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;
default:
// success
it = pendingTunnels.erase (it);
m_NumSuccesiveTunnelCreations++;
}
}
}
void Tunnels::ManageOutboundTunnels ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
{
for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();)
{
auto tunnel = *it;
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint (eLogDebug, "Tunnel: ", tunnel->GetTunnelID (), " expired");
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
it = m_OutboundTunnels.erase (it);
}
else
{
if (tunnel->IsEstablished ())
{
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
tunnel->SetIsRecreated ();
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->RecreateOutboundTunnel (tunnel);
}
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
}
it++;
}
}
}
if (m_OutboundTunnels.size () < 5)
{
// trying to create one more oubound tunnel
auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::data::netdb.GetRandomRouter ();
if (!inboundTunnel || !router) return;
LogPrint (eLogDebug, "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 ())
);
}
}
void Tunnels::ManageInboundTunnels ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
{
for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();)
{
auto tunnel = it->second;
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
LogPrint (eLogDebug, "Tunnel: ", tunnel->GetTunnelID (), " expired");
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->TunnelExpired (tunnel);
it = m_InboundTunnels.erase (it);
}
else
{
if (tunnel->IsEstablished ())
{
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
tunnel->SetIsRecreated ();
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->RecreateInboundTunnel (tunnel);
}
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
}
it++;
}
}
}
if (m_InboundTunnels.empty ())
{
LogPrint (eLogDebug, "Creating zero hops inbound tunnel...");
CreateZeroHopsInboundTunnel ();
if (!m_ExploratoryPool)
{
m_ExploratoryPool = CreateTunnelPool (2, 2, 5, 5); // 2-hop exploratory, 5 tunnels
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
}
return;
}
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
{
// trying to create one more inbound tunnel
auto router = i2p::data::netdb.GetRandomRouter ();
LogPrint (eLogDebug, "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 ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
{
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
auto tmp = it->second;
LogPrint (eLogDebug, "Transit tunnel ", tmp->GetTunnelID (), " expired");
{
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
it = m_TransitTunnels.erase (it);
}
delete tmp;
}
else
it++;
}
}
void Tunnels::ManageTunnelPools ()
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
for (auto it: m_Pools)
{
auto pool = it;
if (pool && pool->IsActive ())
{
pool->CreateTunnels ();
pool->TestTunnels ();
}
}
}
void Tunnels::PostTunnelData (std::shared_ptr<I2NPMessage> 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)
{
auto newTunnel = std::make_shared<TTunnel> (config);
uint32_t replyMsgID;
RAND_bytes ((uint8_t *)&replyMsgID, 4);
AddPendingTunnel (replyMsgID, newTunnel);
newTunnel->Build (replyMsgID, outboundTunnel);
return newTunnel;
}
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
{
m_PendingInboundTunnels[replyMsgID] = tunnel;
}
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel)
{
m_PendingOutboundTunnels[replyMsgID] = tunnel;
}
void Tunnels::AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel)
{
m_OutboundTunnels.push_back (newTunnel);
auto pool = newTunnel->GetTunnelPool ();
if (pool && pool->IsActive ())
pool->TunnelCreated (newTunnel);
else
newTunnel->SetTunnelPool (nullptr);
}
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel)
{
m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel;
auto pool = newTunnel->GetTunnelPool ();
if (!pool)
{
// build symmetric outbound tunnel
CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (),
newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()),
GetNextOutboundTunnel ());
}
else
{
if (pool->IsActive ())
pool->TunnelCreated (newTunnel);
else
newTunnel->SetTunnelPool (nullptr);
}
}
void Tunnels::CreateZeroHopsInboundTunnel ()
{
CreateTunnel<InboundTunnel> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >
{
i2p::context.GetIdentity ()
}));
}
int Tunnels::GetTransitTunnelsExpirationTimeout ()
{
int timeout = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
for (auto it: m_TransitTunnels)
{
int t = it.second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
if (t > timeout) timeout = t;
}
return timeout;
}
}
}

216
Tunnel.h
View File

@@ -1,216 +0,0 @@
#ifndef TUNNEL_H__
#define TUNNEL_H__
#include <inttypes.h>
#include <map>
#include <list>
#include <vector>
#include <string>
#include <thread>
#include <mutex>
#include <memory>
#include "Queue.h"
#include "Crypto.h"
#include "TunnelConfig.h"
#include "TunnelPool.h"
#include "TransitTunnel.h"
#include "TunnelEndpoint.h"
#include "TunnelGateway.h"
#include "TunnelBase.h"
#include "I2NPProtocol.h"
namespace i2p
{
namespace tunnel
{
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes
const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message
enum TunnelState
{
eTunnelStatePending,
eTunnelStateBuildReplyReceived,
eTunnelStateBuildFailed,
eTunnelStateEstablished,
eTunnelStateTestFailed,
eTunnelStateFailed,
eTunnelStateExpiring
};
class OutboundTunnel;
class InboundTunnel;
class Tunnel: public TunnelBase
{
struct TunnelHop
{
std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::crypto::TunnelDecryption decryption;
};
public:
Tunnel (std::shared_ptr<const TunnelConfig> config);
~Tunnel ();
void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; }
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
TunnelState GetState () const { return m_State; };
void SetState (TunnelState state) { m_State = state; };
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; };
bool IsRecreated () const { return m_IsRecreated; };
void SetIsRecreated () { m_IsRecreated = true; };
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
// implements TunnelBase
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
protected:
void PrintHops (std::stringstream& s) const;
private:
std::shared_ptr<const TunnelConfig> m_Config;
std::vector<std::unique_ptr<TunnelHop> > m_Hops;
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State;
bool m_IsRecreated;
};
class OutboundTunnel: public Tunnel
{
public:
OutboundTunnel (std::shared_ptr<const TunnelConfig> config):
Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; };
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
void Print (std::stringstream& s) const;
// implements TunnelBase
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
private:
std::mutex m_SendMutex;
TunnelGateway m_Gateway;
i2p::data::IdentHash m_EndpointIdentHash;
};
class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel>
{
public:
InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
void Print (std::stringstream& s) const;
private:
TunnelEndpoint m_Endpoint;
};
class Tunnels
{
public:
Tunnels ();
~Tunnels ();
void Start ();
void Stop ();
std::shared_ptr<InboundTunnel> GetInboundTunnel (uint32_t tunnelID);
std::shared_ptr<InboundTunnel> GetPendingInboundTunnel (uint32_t replyMsgID);
std::shared_ptr<OutboundTunnel> GetPendingOutboundTunnel (uint32_t replyMsgID);
std::shared_ptr<InboundTunnel> GetNextInboundTunnel ();
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel ();
std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
TransitTunnel * GetTransitTunnel (uint32_t tunnelID);
int GetTransitTunnelsExpirationTimeout ();
void AddTransitTunnel (TransitTunnel * tunnel);
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
template<class TTunnel>
std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
int numOuboundHops, int numInboundTunnels, int numOutboundTunnels);
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
private:
template<class TTunnel>
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
void HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr<I2NPMessage> msg);
void Run ();
void ManageTunnels ();
void ManageOutboundTunnels ();
void ManageInboundTunnels ();
void ManageTransitTunnels ();
void ManagePendingTunnels ();
template<class PendingTunnels>
void ManagePendingTunnels (PendingTunnels& pendingTunnels);
void ManageTunnelPools ();
void CreateZeroHopsInboundTunnel ();
private:
bool m_IsRunning;
std::thread * m_Thread;
std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_PendingInboundTunnels; // by replyMsgID
std::map<uint32_t, std::shared_ptr<OutboundTunnel> > m_PendingOutboundTunnels; // by replyMsgID
std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_InboundTunnels;
std::list<std::shared_ptr<OutboundTunnel> > m_OutboundTunnels;
std::mutex m_TransitTunnelsMutex;
std::map<uint32_t, TransitTunnel *> m_TransitTunnels;
std::mutex m_PoolsMutex;
std::list<std::shared_ptr<TunnelPool>> m_Pools;
std::shared_ptr<TunnelPool> m_ExploratoryPool;
i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
// some stats
int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations;
public:
// for HTTP only
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; };
int GetQueueSize () { return m_Queue.GetSize (); };
int GetTunnelCreationSuccessRate () const // in percents
{
int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations;
return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0;
}
};
extern Tunnels tunnels;
}
}
#endif

View File

@@ -1,72 +0,0 @@
#ifndef TUNNEL_BASE_H__
#define TUNNEL_BASE_H__
#include <inttypes.h>
#include <memory>
#include "Timestamp.h"
#include "I2NPProtocol.h"
#include "Identity.h"
namespace i2p
{
namespace tunnel
{
const size_t TUNNEL_DATA_MSG_SIZE = 1028;
const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008;
const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003;
enum TunnelDeliveryType
{
eDeliveryTypeLocal = 0,
eDeliveryTypeTunnel = 1,
eDeliveryTypeRouter = 2
};
struct TunnelMessageBlock
{
TunnelDeliveryType deliveryType;
i2p::data::IdentHash hash;
uint32_t tunnelID;
std::shared_ptr<I2NPMessage> data;
};
class TunnelBase
{
public:
TunnelBase (uint32_t tunnelID, uint32_t nextTunnelID, i2p::data::IdentHash nextIdent):
m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {};
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
virtual void FlushTunnelDataMsgs () {};
virtual void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) = 0;
uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
virtual uint32_t GetTunnelID () const { return m_TunnelID; }; // as known at our side
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
private:
uint32_t m_TunnelID, m_NextTunnelID;
i2p::data::IdentHash m_NextIdent;
uint32_t m_CreationTime; // seconds since epoch
};
struct TunnelCreationTimeCmp
{
bool operator() (std::shared_ptr<const TunnelBase> t1, std::shared_ptr<const TunnelBase> t2) const
{
if (t1->GetCreationTime () != t2->GetCreationTime ())
return t1->GetCreationTime () > t2->GetCreationTime ();
else
return t1 < t2;
};
};
}
}
#endif

View File

@@ -1,229 +0,0 @@
#ifndef TUNNEL_CONFIG_H__
#define TUNNEL_CONFIG_H__
#include <inttypes.h>
#include <sstream>
#include <vector>
#include <memory>
#include <openssl/rand.h>
#include "Crypto.h"
#include "Identity.h"
#include "RouterContext.h"
#include "Timestamp.h"
namespace i2p
{
namespace tunnel
{
struct TunnelHopConfig
{
std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::data::IdentHash nextIdent;
uint32_t tunnelID, nextTunnelID;
uint8_t layerKey[32];
uint8_t ivKey[32];
uint8_t replyKey[32];
uint8_t replyIV[16];
bool isGateway, isEndpoint;
TunnelHopConfig * next, * prev;
int recordIndex; // record # in tunnel build message
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r)
{
RAND_bytes (layerKey, 32);
RAND_bytes (ivKey, 32);
RAND_bytes (replyKey, 32);
RAND_bytes (replyIV, 16);
RAND_bytes ((uint8_t *)&tunnelID, 4);
isGateway = true;
isEndpoint = true;
ident = r;
//nextRouter = nullptr;
nextTunnelID = 0;
next = nullptr;
prev = nullptr;
}
void SetNextIdent (const i2p::data::IdentHash& ident)
{
nextIdent = ident;
isEndpoint = false;
RAND_bytes ((uint8_t *)&nextTunnelID, 4);
}
void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent)
{
nextIdent = replyIdent;
nextTunnelID = replyTunnelID;
isEndpoint = true;
}
void SetNext (TunnelHopConfig * n)
{
next = n;
if (next)
{
next->prev = this;
next->isGateway = false;
isEndpoint = false;
nextIdent = next->ident->GetIdentHash ();
nextTunnelID = next->tunnelID;
}
}
void SetPrev (TunnelHopConfig * p)
{
prev = p;
if (prev)
{
prev->next = this;
prev->isEndpoint = false;
isGateway = false;
}
}
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) const
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32);
htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
uint8_t flag = 0;
if (isGateway) flag |= 0x80;
if (isEndpoint) flag |= 0x40;
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ());
elGamalEncryption.Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
}
};
class TunnelConfig: public std::enable_shared_from_this<TunnelConfig>
{
public:
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers) // inbound
{
CreatePeers (peers);
m_LastHop->SetNextIdent (i2p::context.GetIdentHash ());
}
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers,
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound
{
CreatePeers (peers);
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelID, replyIdent);
}
~TunnelConfig ()
{
TunnelHopConfig * hop = m_FirstHop;
while (hop)
{
auto tmp = hop;
hop = hop->next;
delete tmp;
}
}
TunnelHopConfig * GetFirstHop () const
{
return m_FirstHop;
}
TunnelHopConfig * GetLastHop () const
{
return m_LastHop;
}
int GetNumHops () const
{
int num = 0;
TunnelHopConfig * hop = m_FirstHop;
while (hop)
{
num++;
hop = hop->next;
}
return num;
}
bool IsInbound () const { return m_FirstHop->isGateway; }
uint32_t GetTunnelID () const
{
if (!m_FirstHop) return 0;
return IsInbound () ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID;
}
uint32_t GetNextTunnelID () const
{
if (!m_FirstHop) return 0;
return m_FirstHop->tunnelID;
}
const i2p::data::IdentHash& GetNextIdentHash () const
{
return m_FirstHop->ident->GetIdentHash ();
}
const i2p::data::IdentHash& GetLastIdentHash () const
{
return m_LastHop->ident->GetIdentHash ();
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const
{
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
TunnelHopConfig * hop = m_FirstHop;
while (hop)
{
peers.push_back (hop->ident);
hop = hop->next;
}
return peers;
}
private:
// this constructor can't be called from outside
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
{
}
template<class Peers>
void CreatePeers (const Peers& peers)
{
TunnelHopConfig * prev = nullptr;
for (auto it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
}
private:
TunnelHopConfig * m_FirstHop, * m_LastHop;
};
}
}
#endif

View File

@@ -1,256 +0,0 @@
#include "I2PEndian.h"
#include <string.h>
#include <openssl/sha.h>
#include "Log.h"
#include "NetDb.h"
#include "I2NPProtocol.h"
#include "Transports.h"
#include "RouterContext.h"
#include "TunnelEndpoint.h"
namespace i2p
{
namespace tunnel
{
TunnelEndpoint::~TunnelEndpoint ()
{
}
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg)
{
m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE;
uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16
uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum
if (zero)
{
uint8_t * fragment = zero + 1;
// verify checksum
memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
uint8_t hash[32];
SHA256(fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16, hash); // payload + iv
if (memcmp (hash, decrypted, 4))
{
LogPrint (eLogError, "TunnelMessage: checksum verification failed");
return;
}
// process fragments
while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
{
uint8_t flag = fragment[0];
fragment++;
bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
uint32_t msgID = 0;
int fragmentNum = 0;
TunnelMessageBlockEx m;
if (!isFollowOnFragment)
{
// first fragment
m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
switch (m.deliveryType)
{
case eDeliveryTypeLocal: // 0
break;
case eDeliveryTypeTunnel: // 1
m.tunnelID = bufbe32toh (fragment);
fragment += 4; // tunnelID
m.hash = i2p::data::IdentHash (fragment);
fragment += 32; // hash
break;
case eDeliveryTypeRouter: // 2
m.hash = i2p::data::IdentHash (fragment);
fragment += 32; // to hash
break;
default:
;
}
bool isFragmented = flag & 0x08;
if (isFragmented)
{
// Message ID
msgID = bufbe32toh (fragment);
fragment += 4;
isLastFragment = false;
}
}
else
{
// follow on
msgID = bufbe32toh (fragment); // MessageID
fragment += 4;
fragmentNum = (flag >> 1) & 0x3F; // 6 bits
isLastFragment = flag & 0x01;
}
uint16_t size = bufbe16toh (fragment);
fragment += 2;
msg->offset = fragment - msg->buf;
msg->len = msg->offset + size;
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
{
// this is not last message. we have to copy it
m.data = NewI2NPShortMessage ();
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
*(m.data) = *msg;
}
else
m.data = msg;
if (!isFollowOnFragment && isLastFragment)
HandleNextMessage (m);
else
{
if (msgID) // msgID is presented, assume message is fragmented
{
if (!isFollowOnFragment) // create new incomlete message
{
m.nextFragmentNum = 1;
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
if (ret.second)
HandleOutOfSequenceFragment (msgID, ret.first->second);
else
LogPrint (eLogError, "Incomplete message ", msgID, "already exists");
}
else
{
m.nextFragmentNum = fragmentNum;
HandleFollowOnFragment (msgID, isLastFragment, m);
}
}
else
LogPrint (eLogError, "Message is fragmented, but msgID is not presented");
}
fragment += size;
}
}
else
LogPrint (eLogError, "TunnelMessage: zero not found");
}
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
{
auto fragment = m.data->GetBuffer ();
auto size = m.data->GetLength ();
auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end())
{
auto& msg = it->second;
if (m.nextFragmentNum == msg.nextFragmentNum)
{
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
{
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (fragment, size) < size) // concatenate fragment
LogPrint (eLogError, "Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
if (isLastFragment)
{
// message complete
HandleNextMessage (msg);
m_IncompleteMessages.erase (it);
}
else
{
msg.nextFragmentNum++;
HandleOutOfSequenceFragment (msgID, msg);
}
}
else
{
LogPrint (eLogError, "Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
m_IncompleteMessages.erase (it);
}
}
else
{
LogPrint (eLogInfo, "Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
}
}
else
{
LogPrint (eLogInfo, "First fragment of message ", msgID, " not found. Saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
}
}
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
{
auto it = m_OutOfSequenceFragments.find (msgID);
if (it == m_OutOfSequenceFragments.end ())
m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (msgID, {fragmentNum, isLastFragment, data}));
}
void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
{
auto it = m_OutOfSequenceFragments.find (msgID);
if (it != m_OutOfSequenceFragments.end ())
{
if (it->second.fragmentNum == msg.nextFragmentNum)
{
LogPrint (eLogInfo, "Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
auto size = it->second.data->GetLength ();
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
LogPrint (eLogError, "Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
if (it->second.isLastFragment)
{
// message complete
HandleNextMessage (msg);
m_IncompleteMessages.erase (msgID);
}
else
msg.nextFragmentNum++;
m_OutOfSequenceFragments.erase (it);
}
}
}
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
{
auto typeID = msg.data->GetTypeID ();
LogPrint (eLogInfo, "TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)typeID);
switch (msg.deliveryType)
{
case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data);
break;
case eDeliveryTypeTunnel:
if (!m_IsInbound) // outbound transit tunnel
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
else
LogPrint (eLogError, "Delivery type tunnel arrived from an inbound tunnel. Dropped");
break;
case eDeliveryTypeRouter:
if (!m_IsInbound) // outbound transit tunnel
i2p::transport::transports.SendMessage (msg.hash, msg.data);
else // we shouldn't send this message. possible leakage
LogPrint (eLogError, "Delivery type router arrived from an inbound tunnel. Dropped");
break;
default:
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
};
// catch RI or reply with new list of routers
if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) &&
!m_IsInbound && msg.deliveryType != eDeliveryTypeLocal)
i2p::data::netdb.PostI2NPMsg (msg.data);
}
}
}

View File

@@ -1,54 +0,0 @@
#ifndef TUNNEL_ENDPOINT_H__
#define TUNNEL_ENDPOINT_H__
#include <inttypes.h>
#include <map>
#include <string>
#include "I2NPProtocol.h"
#include "TunnelBase.h"
namespace i2p
{
namespace tunnel
{
class TunnelEndpoint
{
struct TunnelMessageBlockEx: public TunnelMessageBlock
{
uint8_t nextFragmentNum;
};
struct Fragment
{
uint8_t fragmentNum;
bool isLastFragment;
std::shared_ptr<I2NPMessage> data;
};
public:
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
private:
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m);
void HandleNextMessage (const TunnelMessageBlock& msg);
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg);
private:
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
std::map<uint32_t, Fragment> m_OutOfSequenceFragments;
bool m_IsInbound;
size_t m_NumReceivedBytes;
};
}
}
#endif

View File

@@ -1,212 +0,0 @@
#include <string.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "I2PEndian.h"
#include "Log.h"
#include "RouterContext.h"
#include "Transports.h"
#include "TunnelGateway.h"
namespace i2p
{
namespace tunnel
{
TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0)
{
RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE);
for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++)
if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1;
}
TunnelGatewayBuffer::~TunnelGatewayBuffer ()
{
}
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block)
{
bool messageCreated = false;
if (!m_CurrentTunnelDataMsg)
{
CreateCurrentTunnelDataMessage ();
messageCreated = true;
}
// create delivery instructions
uint8_t di[43]; // max delivery instruction length is 43 for tunnel
size_t diLen = 1;// flag
if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router
{
if (block.deliveryType == eDeliveryTypeTunnel)
{
htobe32buf (di + diLen, block.tunnelID);
diLen += 4; // tunnelID
}
memcpy (di + diLen, block.hash, 32);
diLen += 32; //len
}
di[0] = block.deliveryType << 5; // set delivery type
// create fragments
std::shared_ptr<I2NPMessage> msg = block.data;
auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
if (fullMsgLen <= m_RemainingSize)
{
// message fits. First and last fragment
htobe16buf (di + diLen, msg->GetLength ());
diLen += 2; // size
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ());
m_CurrentTunnelDataMsg->len += diLen + msg->GetLength ();
m_RemainingSize -= diLen + msg->GetLength ();
if (!m_RemainingSize)
CompleteCurrentTunnelDataMessage ();
}
else
{
if (!messageCreated) // check if we should complete previous message
{
auto 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;
if (!nonFit || nonFit > m_RemainingSize)
{
CompleteCurrentTunnelDataMessage ();
CreateCurrentTunnelDataMessage ();
}
}
if (diLen + 6 <= m_RemainingSize)
{
// delivery instructions fit
uint32_t msgID;
memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order
size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size)
// first fragment
di[0] |= 0x08; // fragmented
htobuf32 (di + diLen, msgID);
diLen += 4; // Message ID
htobe16buf (di + diLen, size);
diLen += 2; // size
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size);
m_CurrentTunnelDataMsg->len += diLen + size;
CompleteCurrentTunnelDataMessage ();
// follow on fragments
int fragmentNumber = 1;
while (size < msg->GetLength ())
{
CreateCurrentTunnelDataMessage ();
uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer ();
buf[0] = 0x80 | (fragmentNumber << 1); // frag
bool isLastFragment = false;
size_t s = msg->GetLength () - size;
if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions
s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7;
else // last fragment
{
buf[0] |= 0x01;
isLastFragment = true;
}
htobuf32 (buf + 1, msgID); //Message ID
htobe16buf (buf + 5, s); // size
memcpy (buf + 7, msg->GetBuffer () + size, s);
m_CurrentTunnelDataMsg->len += s+7;
if (isLastFragment)
{
m_RemainingSize -= s+7;
if (!m_RemainingSize)
CompleteCurrentTunnelDataMessage ();
}
else
CompleteCurrentTunnelDataMessage ();
size += s;
fragmentNumber++;
}
}
else
{
// delivery instructions don't fit. Create new message
CompleteCurrentTunnelDataMessage ();
PutI2NPMsg (block);
// don't delete msg because it's taken care inside
}
}
}
void TunnelGatewayBuffer::ClearTunnelDataMsgs ()
{
m_TunnelDataMsgs.clear ();
}
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
{
m_CurrentTunnelDataMsg = NewI2NPShortMessage ();
m_CurrentTunnelDataMsg->Align (12);
// we reserve space for padding
m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE;
m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset;
m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
}
void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage ()
{
if (!m_CurrentTunnelDataMsg) return;
uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer ();
size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset;
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE;
uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
htobe32buf (buf, m_TunnelID);
RAND_bytes (buf + 4, 16); // original IV
memcpy (payload + size, buf + 4, 16); // copy IV for checksum
uint8_t hash[32];
SHA256(payload, size+16, hash);
memcpy (buf+20, hash, 4); // checksum
payload[-1] = 0; // zero
ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1
if (paddingSize > 0)
{
// non-zero padding
auto randomOffset = rand () % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1);
memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize);
}
// we can't fill message header yet because encryption is required
m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg);
m_CurrentTunnelDataMsg = nullptr;
}
void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block)
{
if (block.data)
{
PutTunnelDataMsg (block);
SendBuffer ();
}
}
void TunnelGateway::PutTunnelDataMsg (const TunnelMessageBlock& block)
{
if (block.data)
m_Buffer.PutI2NPMsg (block);
}
void TunnelGateway::SendBuffer ()
{
m_Buffer.CompleteCurrentTunnelDataMessage ();
auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto tunnelMsg : tunnelMsgs)
{
m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg);
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData);
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
}
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs);
m_Buffer.ClearTunnelDataMsgs ();
}
}
}

View File

@@ -1,57 +0,0 @@
#ifndef TUNNEL_GATEWAY_H__
#define TUNNEL_GATEWAY_H__
#include <inttypes.h>
#include <vector>
#include <memory>
#include "I2NPProtocol.h"
#include "TunnelBase.h"
namespace i2p
{
namespace tunnel
{
class TunnelGatewayBuffer
{
public:
TunnelGatewayBuffer (uint32_t tunnelID);
~TunnelGatewayBuffer ();
void PutI2NPMsg (const TunnelMessageBlock& block);
const std::vector<std::shared_ptr<I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
void ClearTunnelDataMsgs ();
void CompleteCurrentTunnelDataMessage ();
private:
void CreateCurrentTunnelDataMessage ();
private:
uint32_t m_TunnelID;
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelDataMsgs;
std::shared_ptr<I2NPMessage> m_CurrentTunnelDataMsg;
size_t m_RemainingSize;
uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE];
};
class TunnelGateway
{
public:
TunnelGateway (TunnelBase * tunnel):
m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {};
void SendTunnelDataMsg (const TunnelMessageBlock& block);
void PutTunnelDataMsg (const TunnelMessageBlock& block);
void SendBuffer ();
size_t GetNumSentBytes () const { return m_NumSentBytes; };
private:
TunnelBase * m_Tunnel;
TunnelGatewayBuffer m_Buffer;
size_t m_NumSentBytes;
};
}
}
#endif

View File

@@ -1,437 +0,0 @@
#include <algorithm>
#include <openssl/rand.h>
#include "I2PEndian.h"
#include "Crypto.h"
#include "Tunnel.h"
#include "NetDb.h"
#include "Timestamp.h"
#include "Garlic.h"
#include "Transports.h"
#include "Log.h"
#include "TunnelPool.h"
namespace i2p
{
namespace tunnel
{
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true)
{
}
TunnelPool::~TunnelPool ()
{
DetachTunnels ();
}
void TunnelPool::SetExplicitPeers (std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers)
{
m_ExplicitPeers = explicitPeers;
if (m_ExplicitPeers)
{
int size = m_ExplicitPeers->size ();
if (m_NumInboundHops > size)
{
m_NumInboundHops = size;
LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers");
}
if (m_NumOutboundHops > size)
{
m_NumOutboundHops = size;
LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers");
}
m_NumInboundTunnels = 1;
m_NumOutboundTunnels = 1;
}
}
void TunnelPool::DetachTunnels ()
{
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it: m_InboundTunnels)
it->SetTunnelPool (nullptr);
m_InboundTunnels.clear ();
}
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels)
it->SetTunnelPool (nullptr);
m_OutboundTunnels.clear ();
}
m_Tests.clear ();
}
void TunnelPool::TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel)
{
if (!m_IsActive) return;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.insert (createdTunnel);
}
if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated ();
}
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.erase (expiredTunnel);
}
}
void TunnelPool::TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel)
{
if (!m_IsActive) return;
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
}
//CreatePairedInboundTunnel (createdTunnel);
}
void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
expiredTunnel->SetTunnelPool (nullptr);
for (auto it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.erase (expiredTunnel);
}
}
std::vector<std::shared_ptr<InboundTunnel> > TunnelPool::GetInboundTunnels (int num) const
{
std::vector<std::shared_ptr<InboundTunnel> > v;
int i = 0;
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
{
if (i >= num) break;
if (it->IsEstablished ())
{
v.push_back (it);
i++;
}
}
return v;
}
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
return GetNextTunnel (m_OutboundTunnels, excluded);
}
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
return GetNextTunnel (m_InboundTunnels, excluded);
}
template<class TTunnels>
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const
{
if (tunnels.empty ()) return nullptr;
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
typename TTunnels::value_type tunnel = nullptr;
for (auto it: tunnels)
{
if (it->IsEstablished () && it != excluded)
{
tunnel = it;
i++;
}
if (i > ind && tunnel) break;
}
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
return tunnel;
}
std::shared_ptr<OutboundTunnel> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const
{
if (old && old->IsEstablished ()) return old;
std::shared_ptr<OutboundTunnel> tunnel;
if (old)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels)
if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
{
tunnel = it;
break;
}
}
if (!tunnel)
tunnel = GetNextOutboundTunnel ();
return tunnel;
}
void TunnelPool::CreateTunnels ()
{
int num = 0;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel ();
num = 0;
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it : m_OutboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumOutboundTunnels; i++)
CreateOutboundTunnel ();
}
void TunnelPool::TestTunnels ()
{
for (auto it: m_Tests)
{
LogPrint (eLogWarning, "Tunnels: test of ", (int)it.first, " failed");
// if test failed again with another tunnel we consider it failed
if (it.second.first)
{
if (it.second.first->GetState () == eTunnelStateTestFailed)
{
it.second.first->SetState (eTunnelStateFailed);
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.erase (it.second.first);
}
else
it.second.first->SetState (eTunnelStateTestFailed);
}
if (it.second.second)
{
if (it.second.second->GetState () == eTunnelStateTestFailed)
{
it.second.second->SetState (eTunnelStateFailed);
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.erase (it.second.second);
}
if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated ();
}
else
it.second.second->SetState (eTunnelStateTestFailed);
}
}
m_Tests.clear ();
// new tests
auto it1 = m_OutboundTunnels.begin ();
auto it2 = m_InboundTunnels.begin ();
while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
{
bool failed = false;
if ((*it1)->IsFailed ())
{
failed = true;
it1++;
}
if ((*it2)->IsFailed ())
{
failed = true;
it2++;
}
if (!failed)
{
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
m_Tests[msgID] = std::make_pair (*it1, *it2);
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
CreateDeliveryStatusMsg (msgID));
it1++; it2++;
}
}
}
void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
if (m_LocalDestination)
m_LocalDestination->ProcessGarlicMessage (msg);
else
LogPrint (eLogWarning, "Tunnels: local destination doesn't exist, dropped");
}
void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
uint32_t msgID = bufbe32toh (buf);
buf += 4;
uint64_t timestamp = bufbe64toh (buf);
auto it = m_Tests.find (msgID);
if (it != m_Tests.end ())
{
// restore from test failed state if any
if (it->second.first->GetState () == eTunnelStateTestFailed)
it->second.first->SetState (eTunnelStateEstablished);
if (it->second.second->GetState () == eTunnelStateTestFailed)
it->second.second->SetState (eTunnelStateEstablished);
LogPrint (eLogDebug, "Tunnels: test of ", it->first, " successful. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds");
m_Tests.erase (it);
}
else
{
if (m_LocalDestination)
m_LocalDestination->ProcessDeliveryStatusMessage (msg);
else
LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
}
}
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const
{
bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ());
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop):
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
if (!hop || hop->GetProfile ()->IsBad ())
hop = i2p::data::netdb.GetRandomRouter ();
return hop;
}
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)
{
auto r = i2p::transport::transports.GetRandomPeer ();
if (r && !r->GetProfile ()->IsBad ())
{
prevHop = r;
peers.push_back (r->GetRouterIdentity ());
numHops--;
}
}
for (int i = 0; i < numHops; i++)
{
auto hop = SelectNextHop (prevHop);
if (!hop)
{
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
return false;
}
prevHop = hop;
peers.push_back (hop->GetRouterIdentity ());
}
return true;
}
bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{
int size = m_ExplicitPeers->size ();
std::vector<int> peerIndicies;
for (int i = 0; i < size; i++) peerIndicies.push_back(i);
std::random_shuffle (peerIndicies.begin(), peerIndicies.end());
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
for (int i = 0; i < numHops; i++)
{
auto& ident = (*m_ExplicitPeers)[peerIndicies[i]];
auto r = i2p::data::netdb.FindRouter (ident);
if (r)
peers.push_back (r->GetRouterIdentity ());
else
{
LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
i2p::data::netdb.RequestDestination (ident);
return false;
}
}
return true;
}
void TunnelPool::CreateInboundTunnel ()
{
auto outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (peers, true))
{
std::reverse (peers.begin (), peers.end ());
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig> (peers), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
else
LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available");
}
void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
{
auto outboundTunnel = GetNextOutboundTunnel ();
if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(tunnel->GetPeers ()), outboundTunnel);
newTunnel->SetTunnelPool (shared_from_this());
}
void TunnelPool::CreateOutboundTunnel ()
{
auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel)
inboundTunnel = tunnels.GetNextInboundTunnel ();
if (inboundTunnel)
{
LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (peers, false))
{
auto tunnel = tunnels.CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
tunnel->SetTunnelPool (shared_from_this ());
}
else
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
}
else
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found");
}
void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
{
auto inboundTunnel = GetNextInboundTunnel ();
if (!inboundTunnel)
inboundTunnel = tunnels.GetNextInboundTunnel ();
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 ()));
newTunnel->SetTunnelPool (shared_from_this ());
}
else
LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found");
}
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
{
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
}
}

View File

@@ -1,91 +0,0 @@
#ifndef TUNNEL_POOL__
#define TUNNEL_POOL__
#include <inttypes.h>
#include <set>
#include <vector>
#include <utility>
#include <mutex>
#include <memory>
#include "Identity.h"
#include "LeaseSet.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "TunnelBase.h"
#include "RouterContext.h"
#include "Garlic.h"
namespace i2p
{
namespace tunnel
{
class Tunnel;
class InboundTunnel;
class OutboundTunnel;
class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination
{
public:
TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels);
~TunnelPool ();
std::shared_ptr<i2p::garlic::GarlicDestination> GetLocalDestination () const { return m_LocalDestination; };
void SetLocalDestination (std::shared_ptr<i2p::garlic::GarlicDestination> destination) { m_LocalDestination = destination; };
void SetExplicitPeers (std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers);
void CreateTunnels ();
void TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel);
void TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel);
void TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel);
void TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel);
void RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel);
void RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel);
std::vector<std::shared_ptr<InboundTunnel> > GetInboundTunnels (int num) const;
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr) const;
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const;
std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
void TestTunnels ();
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
bool IsActive () const { return m_IsActive; };
void SetActive (bool isActive) { m_IsActive = isActive; };
void DetachTunnels ();
private:
void CreateInboundTunnel ();
void CreateOutboundTunnel ();
void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
template<class TTunnels>
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const;
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;
bool SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound);
bool SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound);
private:
std::shared_ptr<i2p::garlic::GarlicDestination> m_LocalDestination;
int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels;
std::shared_ptr<std::vector<i2p::data::IdentHash> > m_ExplicitPeers;
mutable std::mutex m_InboundTunnelsMutex;
std::set<std::shared_ptr<InboundTunnel>, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
mutable std::mutex m_OutboundTunnelsMutex;
std::set<std::shared_ptr<OutboundTunnel>, TunnelCreationTimeCmp> m_OutboundTunnels;
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
bool m_IsActive;
public:
// for HTTP only
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
};
}
}
#endif

14
Win32/.gitignore vendored
View File

@@ -1,14 +0,0 @@
*
!*/
!*.h
!*.cpp
!*.bat
!*.sln
!*.vcproj
!*.vcxproj
!*.vcxproj.filters
!*.iss
!.gitignore

View File

@@ -1,14 +0,0 @@
@echo off
convert Itoopie.svg ^
-fuzz 90%% -fill transparent -floodfill 2x2 white -fuzz 20%% -fill #AE0E99 -opaque red ^
-fill #FBBC11 -opaque yellow ^
( -clone 0 -resize 256x256 ) ^
( -clone 0 -resize 128x128 ) ^
( -clone 0 -resize 64x64 ) ^
( -clone 0 -resize 48x48 ) ^
( -clone 0 -resize 32x32 ) ^
( -clone 0 -resize 24x24 ) ^
( -clone 0 -resize 16x16 ) ^
( -size 150x57 xc:white -clone 0 -geometry 57x57+46+0 -composite -gravity center -write BMP3:ictoopie.bmp +delete ) ^
( -clone 0 -write Itoopie_purple.png +delete ) ^
-delete 0 ictoopie.ico

View File

@@ -1,282 +0,0 @@
# NSIS Installer script. (Tested with NSIS 2.64 on Windows 7)
# Author: Mikal Villa (Meeh)
# Version: 1.1
Name PurpleI2P
RequestExecutionLevel highest
SetCompressor /SOLID lzma
ShowInstDetails show
# General Symbol Definitions
!define REGKEY "SOFTWARE\$(^Name)"
!define VERSION 0.3.0.0
!define COMPANY "The Privacy Solutions Project"
!define URL "https://i2p.io"
# MUI Symbol Definitions
!define MUI_ICON "ictoopie.ico"
#!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_RIGHT
#!define MUI_HEADERIMAGE_BITMAP "../share/pixmaps/nsis-header.bmp"
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM
!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY}
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup
!define MUI_STARTMENUPAGE_DEFAULTFOLDER PurpleI2P
!define MUI_FINISHPAGE_RUN $INSTDIR\i2pd.exe
!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\Readme.txt
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
# Included files
!include Sections.nsh
!include MUI2.nsh
!include nsDialogs.nsh
!include winmessages.nsh
!include logiclib.nsh
# Local included files
!include nsi\helper_readme.nsh
;!include nsi\servicelib.nsh
# Variables
Var StartMenuGroup
# Installer pages
# Execution flow of installer windows
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_README "../Readme.md"
!insertmacro MUI_PAGE_DIRECTORY
# Disabled for now. Use the bat
;Page custom mode_selection # Meeh's hack for installing and starting service.
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuGroup
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
# Uninstall pages
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
# Installer languages
!insertmacro MUI_LANGUAGE English
# Installer attributes
OutFile PurpleI2P-0.3.0.0-win32-setup.exe
InstallDir $PROGRAMFILES\PurpleI2P
CRCCheck on
XPStyle on
BrandingText " "
ShowInstDetails show
VIProductVersion 0.3.0.0
VIAddVersionKey ProductName PurpleI2P
VIAddVersionKey ProductVersion "${VERSION}"
VIAddVersionKey CompanyName "${COMPANY}"
VIAddVersionKey CompanyWebsite "${URL}"
VIAddVersionKey FileVersion "${VERSION}"
VIAddVersionKey FileDescription ""
VIAddVersionKey LegalCopyright ""
InstallDirRegKey HKCU "${REGKEY}" Path
ShowUninstDetails show
# Readme definitions
;--------------------------------
;Languages
;Set up install lang strings for 1st lang
${ReadmeLanguage} "${LANG_ENGLISH}" \
"Read Me" \
"Please review the following important information." \
"About $(^name):" \
"$\n Click on scrollbar arrows or press Page Down to review the entire text."
;Add 2nd language
!insertmacro MUI_LANGUAGE "Norwegian"
;set up install lang strings for second lang
${ReadmeLanguage} "${LANG_NORWEGIAN}" \
"Les meg!" \
"Vennligst les informasjonen om hvordan du skal bruke PurpleI2P." \
"Om $(^name):" \
"$\n Klikk på scrollbaren til høyre for å se hele innholdet."
;--------------------------------
# Installer sections
Section -Main SEC0000
SetOutPath $INSTDIR
SetOverwrite on
File /oname=i2pd.exe Release\i2pd.exe
File /oname=install_service.bat install_service.bat
File /oname=uninstall_service.bat uninstall_service.bat
File /oname=LICENSE.txt ..\LICENSE
File /oname=Readme.txt ..\README.md
SetOutPath $INSTDIR\src
File /r /x *.nsi /x *.rc /x *.exe /x *.obj /x *.nsh /x *.sln /x *.vcxproj /x *.tlog /x *.log /x *.res /x *.pdb /x *.suo /x *.opensdf /x *.filters /x *.sdf /x *.iss /x *.aps /x .gitignore /x *.o ../\*.*
SetOutPath $INSTDIR
RMDir /r /REBOOTOK $INSTDIR\src\.git # Remove git directory
RMDir /r /REBOOTOK $INSTDIR\src\Win32\Release # Removing release directory
RMDir /r /REBOOTOK $INSTDIR\src\Win32\nsi
WriteRegStr HKCU "${REGKEY}\Components" Main 1
SectionEnd
Section -post SEC0001
WriteRegStr HKCU "${REGKEY}" Path $INSTDIR
SetOutPath $INSTDIR
WriteUninstaller $INSTDIR\uninstall.exe
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateDirectory $SMPROGRAMS\$StartMenuGroup
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk" $INSTDIR\i2pd.exe
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk" $INSTDIR\install_service.bat
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P Service.lnk" $INSTDIR\uninstall_service.bat
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk" $INSTDIR\uninstall.exe
!insertmacro MUI_STARTMENU_WRITE_END
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
WriteRegStr HKCR "i2pd" "URL Protocol" ""
WriteRegStr HKCR "i2pd" "" "URL:i2pd" # TODO: if a instance of own is found, relaunch with a proxyfied browser to open webage. (e.g i2pd://meeh.i2p)
WriteRegStr HKCR "i2pd\DefaultIcon" "" $INSTDIR\i2pd.exe
WriteRegStr HKCR "i2pd\shell\open\command" "" '"$INSTDIR\i2pd.exe" "%1"'
SectionEnd
# Macro for selecting uninstaller sections
!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID
Push $R0
ReadRegStr $R0 HKCU "${REGKEY}\Components" "${SECTION_NAME}"
StrCmp $R0 1 0 next${UNSECTION_ID}
!insertmacro SelectSection "${UNSECTION_ID}"
GoTo done${UNSECTION_ID}
next${UNSECTION_ID}:
!insertmacro UnselectSection "${UNSECTION_ID}"
done${UNSECTION_ID}:
Pop $R0
!macroend
# Uninstaller sections
Section /o -un.Main UNSEC0000
Delete /REBOOTOK $INSTDIR\i2pd.exe
Delete /REBOOTOK $INSTDIR\LICENSE.txt
Delete /REBOOTOK $INSTDIR\Readme.txt
Delete /REBOOTOK $INSTDIR\install_service.bat
Delete /REBOOTOK $INSTDIR\uninstall_service.bat
RMDir /r /REBOOTOK $INSTDIR\src
DeleteRegValue HKCU "${REGKEY}\Components" Main
SectionEnd
Section -un.post UNSEC0001
DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk"
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk"
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk"
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\UnInstall PurpleI2P Service.lnk"
Delete /REBOOTOK "$SMSTARTUP\PurpleI2P.lnk"
Delete /REBOOTOK $INSTDIR\uninstall.exe
Delete /REBOOTOK $INSTDIR\debug.log
DeleteRegValue HKCU "${REGKEY}" StartMenuGroup
DeleteRegValue HKCU "${REGKEY}" Path
DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components"
DeleteRegKey /IfEmpty HKCU "${REGKEY}"
DeleteRegKey HKCR "i2pd"
RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup
RmDir /REBOOTOK $INSTDIR
Push $R0
StrCpy $R0 $StartMenuGroup 1
StrCmp $R0 ">" no_smgroup
no_smgroup:
Pop $R0
SectionEnd
; var hwndExecModeRadio
; var hwndRunServiceNowRadio
; Function mode_selection
; nsDialogs::Create 1018
; Pop $0
; ${NSD_CreateLabel} 0 10 75% 20u "How would you like PurpleI2P (i2pd) to run?"
; Pop $0
; ${NSD_CreateRadioButton} 20 60 80% 25u "Service Mode"
; Pop $hwndExecModeRadio
; ${NSD_AddStyle} $hwndExecModeRadio ${WS_GROUP}
; ${NSD_CreateRadioButton} 20 90 80% 25u "Command line Mode"
; Pop $0
; ${NSD_CreateButton} 20 150 -40 14u "Do it!"
; Pop $0
; ${NSD_OnClick} $0 perform_mode
; nsDialogs::Show
; FunctionEnd
; Function start_now_selection
; nsDialogs::Create 1018
; Pop $0
; ${NSD_CreateLabel} 0 10 75% 20u "Enable the service now?"
; Pop $0
; ${NSD_CreateRadioButton} 20 60 80% 25u "Yes"
; Pop $hwndRunServiceNowRadio
; ${NSD_AddStyle} $hwndRunServiceNowRadio ${WS_GROUP}
; ${NSD_CreateRadioButton} 20 90 80% 25u "No"
; Pop $0
; ${NSD_CreateButton} 20 150 -40 14u "Do it!"
; Pop $0
; ${NSD_OnClick} $0 perform_mode
; nsDialogs::Show
; FunctionEnd
; Function perform_mode
; ${NSD_GetState} $hwndExecModeRadio $0
; ${If} $0 = ${BST_CHECKED}
; Call service_mode
; ${EndIF}
; FunctionEnd
; Function start_now
; ${NSD_GetState} $hwndRunServiceNowRadio $0
; ${If} $0 = ${BST_CHECKED}
; Call start_now_selection
; ${EndIF}
; FunctionEnd
; Function service_mode
; Push "create"
; Push "PurpleI2P Service"
; Push "$INSTDIR\i2pd.exe;autostart=1;display=PurpleI2P"
; Call Service
; Pop $0 ; Actually more to write than !insertmacro, but much more fun :D
; Push "start"
; Push "PurpleI2P Service"
; Call Service
; Pop $0
; Call start_now
; !define MUI_FINISHPAGE_RUN_NOTCHECKED
; !define MUI_FINISHPAGE_RUN_TEXT "No need to run now since we already installed and launched it as a Windows service!"
; FunctionEnd
# Installer functions
Function .onInit
InitPluginsDir
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd
# Uninstaller functions
Function un.onInit
ReadRegStr $INSTDIR HKCU "${REGKEY}" Path
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup
!insertmacro SELECT_UNSECTION Main ${UNSEC0000}
!insertmacro MUI_UNGETLANGUAGE
FunctionEnd

View File

@@ -1,72 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
MAINICON ICON "ictoopie.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#include "Resource.rc2"
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@@ -1,47 +0,0 @@
//
// Resource.RC2 - resources Microsoft Visual C++ does not edit directly
//
#ifdef APSTUDIO_INVOKED
#error this file is not editable by Microsoft Visual C++
#endif //APSTUDIO_INVOKED
#include "../version.h"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION I2PD_VERSION_MAJOR,I2PD_VERSION_MINOR,I2PD_VERSION_MICRO,I2PD_VERSION_PATCH
PRODUCTVERSION I2P_VERSION_MAJOR,I2P_VERSION_MINOR,I2P_VERSION_MICRO,I2P_VERSION_PATCH
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Purple I2P"
VALUE "FileDescription", "C++ I2P daemon"
VALUE "FileVersion", I2PD_VERSION
VALUE "InternalName", CODENAME
VALUE "LegalCopyright", "Copyright (C) 2013-2015, The PurpleI2P Project"
VALUE "OriginalFilename", "i2pd"
VALUE "ProductName", "Purple I2P"
VALUE "ProductVersion", I2P_VERSION
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -1,30 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30723.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "i2pd", "i2pd.vcxproj", "{930568EC-31C9-406A-AD1C-9636DF5D8FAA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.ActiveCfg = Debug|Win32
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.Build.0 = Debug|Win32
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.Deploy.0 = Debug|Win32
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|x64.ActiveCfg = Debug|x64
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|x64.Build.0 = Debug|x64
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.ActiveCfg = Release|Win32
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.Build.0 = Release|Win32
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.Deploy.0 = Release|Win32
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|x64.ActiveCfg = Release|x64
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -1,292 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\AddressBook.cpp" />
<ClCompile Include="..\aes.cpp" />
<ClCompile Include="..\base64.cpp" />
<ClCompile Include="..\BOB.cpp" />
<ClCompile Include="..\CryptoConst.cpp" />
<ClCompile Include="..\Daemon.cpp" />
<ClCompile Include="..\DaemonWin32.cpp" />
<ClCompile Include="..\Garlic.cpp" />
<ClCompile Include="..\HTTPProxy.cpp" />
<ClCompile Include="..\HTTPServer.cpp" />
<ClCompile Include="..\I2NPProtocol.cpp" />
<ClCompile Include="..\i2p.cpp" />
<ClCompile Include="..\I2PEndian.cpp" />
<ClCompile Include="..\I2PService.cpp" />
<ClCompile Include="..\Identity.cpp" />
<ClCompile Include="..\LeaseSet.cpp" />
<ClCompile Include="..\Log.cpp" />
<ClCompile Include="..\NetDb.cpp" />
<ClCompile Include="..\NetDbRequests.cpp" />
<ClCompile Include="..\NTCPSession.cpp" />
<ClCompile Include="..\Profiling.cpp" />
<ClCompile Include="..\Reseed.cpp" />
<ClCompile Include="..\RouterContext.cpp" />
<ClCompile Include="..\RouterInfo.cpp" />
<ClCompile Include="..\Signature.cpp" />
<ClCompile Include="..\SAM.cpp" />
<ClCompile Include="..\SSU.cpp" />
<ClCompile Include="..\SSUData.cpp" />
<ClCompile Include="..\SSUSession.cpp" />
<ClCompile Include="..\Streaming.cpp" />
<ClCompile Include="..\Datagram.cpp" />
<ClCompile Include="..\Destination.cpp" />
<ClCompile Include="..\TransitTunnel.cpp" />
<ClCompile Include="..\Transports.cpp" />
<ClCompile Include="..\Tunnel.cpp" />
<ClCompile Include="..\TunnelEndpoint.cpp" />
<ClCompile Include="..\TunnelGateway.cpp" />
<ClCompile Include="..\TunnelPool.cpp" />
<ClCompile Include="..\UPnP.cpp" />
<ClCompile Include="..\util.cpp" />
<ClCompile Include="..\SOCKS.cpp" />
<ClCompile Include="..\I2PTunnel.cpp" />
<ClCompile Include="..\I2PControl.cpp" />
<ClCompile Include="..\ClientContext.cpp" />
<ClCompile Include="Win32Service.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\AddressBook.h" />
<ClInclude Include="..\base64.h" />
<ClInclude Include="..\BOB.h" />
<ClInclude Include="..\CryptoConst.h" />
<ClInclude Include="..\Daemon.h" />
<ClInclude Include="..\ElGamal.h" />
<ClInclude Include="..\Garlic.h" />
<ClInclude Include="..\HTTPProxy.h" />
<ClInclude Include="..\HTTPServer.h" />
<ClInclude Include="..\I2NPProtocol.h" />
<ClInclude Include="..\I2PEndian.h" />
<ClInclude Include="..\I2PService.h" />
<ClInclude Include="..\Identity.h" />
<ClInclude Include="..\LeaseSet.h" />
<ClInclude Include="..\LittleBigEndian.h" />
<ClInclude Include="..\Log.h" />
<ClInclude Include="..\NetDbRequests.h" />
<ClInclude Include="..\NetDb.h" />
<ClInclude Include="..\NTCPSession.h" />
<ClInclude Include="..\Queue.h" />
<ClInclude Include="..\Profiling.h" />
<ClInclude Include="..\Reseed.h" />
<ClInclude Include="..\RouterContext.h" />
<ClInclude Include="..\RouterInfo.h" />
<ClInclude Include="..\SAM.h" />
<ClInclude Include="..\SSU.h" />
<ClInclude Include="..\SSUData.h" />
<ClInclude Include="..\SSUSession.h" />
<ClInclude Include="..\Streaming.h" />
<ClInclude Include="..\Datagram.h" />
<ClInclude Include="..\Destination.h" />
<ClInclude Include="..\Timestamp.h" />
<ClInclude Include="..\TransitTunnel.h" />
<ClInclude Include="..\Transports.h" />
<ClInclude Include="..\Tunnel.h" />
<ClInclude Include="..\TunnelBase.h" />
<ClInclude Include="..\TunnelConfig.h" />
<ClInclude Include="..\TunnelEndpoint.h" />
<ClInclude Include="..\TunnelGateway.h" />
<ClInclude Include="..\TunnelPool.h" />
<ClInclude Include="..\UPnP.h" />
<ClInclude Include="..\util.h" />
<ClInclude Include="..\SOCKS.h" />
<ClInclude Include="..\I2PTunnel.h" />
<ClInclude Include="..\I2PControl.h" />
<ClInclude Include="..\version.h" />
<ClInclude Include="..\Signature.h" />
<ClInclude Include="..\ClientContext.h" />
<ClInclude Include="..\TransportSession.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="Win32Service.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{930568EC-31C9-406A-AD1C-9636DF5D8FAA}</ProjectGuid>
<RootNamespace>i2pd</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120_xp</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120_xp</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>./..;$(IncludePath);$(BOOST);$(CRYPTOPP);C:\build-lib\cryptopp;C:\build-lib\boost_1_57_0\</IncludePath>
<LibraryPath>$(BOOST)\stage\lib;C:\build-lib\cryptopp;C:\build-lib\boost_1_57_0\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
<TargetName>$(ProjectName)_d</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>./..;$(IncludePath);$(BOOST);$(CRYPTOPP)</IncludePath>
<LibraryPath>$(BOOST)\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
<TargetName>$(ProjectName)_d</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>./..;$(IncludePath);$(BOOST);C:\build-lib\boost_1_57_0\;C:\build-lib</IncludePath>
<LibraryPath>C:\build-lib\boost_1_57_0\stage\lib;C:\build-lib\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>./..;$(IncludePath);$(BOOST);$(CRYPTOPP)</IncludePath>
<LibraryPath>$(BOOST)\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_MBCS;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
<Version>0.2</Version>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_MBCS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
<Version>0.2</Version>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level2</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>false</OptimizeReferences>
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
<Version>
</Version>
<SubSystem>Console</SubSystem>
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
<LinkErrorReporting>NoErrorReport</LinkErrorReporting>
</Link>
<Manifest>
<AssemblyIdentity>
</AssemblyIdentity>
<ComponentFileName>
</ComponentFileName>
</Manifest>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>false</OptimizeReferences>
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
<Version>
</Version>
<SubSystem>Console</SubSystem>
<MinimumRequiredVersion>5.02</MinimumRequiredVersion>
<LinkErrorReporting>NoErrorReport</LinkErrorReporting>
</Link>
<Manifest>
<AssemblyIdentity>
</AssemblyIdentity>
<ComponentFileName>
</ComponentFileName>
</Manifest>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

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