Compare commits

..

121 Commits
0.5.0 ... 0.6.0

Author SHA1 Message Date
orignal
5444889715 Update version.h
Version updated
2015-01-06 07:52:36 -05:00
orignal
3e13a1feed long ElGamal private key 2015-01-05 19:32:46 -05:00
orignal
7e4c416bc1 Merge pull request #136 from klondi/master
Improve SOCKS, handle  .b32.i2p conversions into address book
2015-01-05 15:09:16 -05:00
Francisco Blas (klondike) Izquierdo Riera
882b559d3a Reduce the parsing to a single state machine, automatic removal of objects and destruction of sockets/streams 2015-01-05 20:17:07 +01:00
Francisco Blas (klondike) Izquierdo Riera
610fd2ac67 Reduce memory usage a bit 2015-01-05 20:17:07 +01:00
Francisco Blas (klondike) Izquierdo Riera
f383ebb718 Return adequate SOCKS replies 2015-01-05 20:17:07 +01:00
Francisco Blas (klondike) Izquierdo Riera
cb94d43092 Use the address book for conversion into .b32.i2p 2015-01-05 20:17:07 +01:00
orignal
8812a45607 DatabaseLookup for RouterInfo 2015-01-04 17:25:16 -05:00
orignal
37a374000c elimimated cast to ClearText 2015-01-04 09:33:19 -05:00
orignal
ac17f116be Merge pull request #134 from klondi/httpproxy
More SOCKS Proxy cleanups
2015-01-03 23:14:38 -05:00
orignal
ecf709cbba fill clear text buffer of BuildRequestRecord 2015-01-03 23:13:07 -05:00
Francisco Blas (klondike) Izquierdo Riera
225aa7fa6a Merge SOCKS4a and SOCKS5 request parsers, add stub support for SOCKS4 and IPv4 and IPv6 addresses on SOCKS5 2015-01-04 04:47:03 +01:00
Francisco Blas (klondike) Izquierdo Riera
46b16237b6 Use enums instead of magic numbers on SOCKS 2015-01-04 04:47:03 +01:00
Mikal Villa
7a387b9a9f Updating UPnP code for windows. (untested) 2015-01-04 00:06:43 +01:00
orignal
3c5e9ddd4e eliminated cast to ecnryptted build record 2015-01-03 16:31:44 -05:00
orignal
02851d7587 Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2015-01-03 16:30:07 -05:00
orignal
1b3652e135 Merge pull request #133 from klondi/httpproxy
SOCKS5 support
2015-01-03 16:29:41 -05:00
Francisco Blas (klondike) Izquierdo Riera
17ccaab792 Clean up SOCKS a bit 2015-01-03 22:18:05 +01:00
Francisco Blas (klondike) Izquierdo Riera
c9576dcdbe Add Initial SOCKS5 support 2015-01-03 22:10:03 +01:00
Mikal Villa
7acdc0a606 Libminiupnpc library support (v1.5 and v1.6), dynamic runtime linking. 2015-01-03 21:38:48 +01:00
Mikal Villa
f552f24e6e Following BSD standards 2015-01-03 21:35:30 +01:00
Mikal Villa
79d13eb6cb Fix Mac OSX build error. 2015-01-03 21:33:27 +01:00
orignal
41974b8c75 fixed race condition 2015-01-03 15:20:11 -05:00
orignal
729cc4330e Merge pull request #132 from klondi/httpproxy
SOCKSProxy
2015-01-03 10:30:29 -05:00
Francisco Blas (klondike) Izquierdo Riera
27d510d1b7 Handle port correctly 2015-01-03 16:16:27 +01:00
orignal
8a293f45fa eliminated cast to BuildResponse Record 2015-01-03 09:47:24 -05:00
Francisco Blas (klondike) Izquierdo Riera
44bc09b007 Rewrite SOCKS proxy parsing to allow for SOCKS5, also fix any remaining logs 2015-01-03 15:40:01 +01:00
orignal
b58d58ef29 Merge pull request #131 from klondi/httpproxy
Get SOCKS proxy back in working state
2015-01-03 08:03:28 -05:00
Francisco Blas (klondike) Izquierdo Riera
ba12331a11 Rewrite SOCKS proxy so it uses I2PTunnelConnection and I2PTunnel 2015-01-03 06:44:21 +01:00
Francisco Blas (klondike) Izquierdo Riera
b88b04515e Remove unnecessary RemoteLeaseSet 2015-01-03 06:44:21 +01:00
Francisco Blas (klondike) Izquierdo Riera
ba9a0c0b2e Fix strange indentation. 2015-01-03 06:44:21 +01:00
orignal
9237174026 request failed in destination is not ready 2015-01-02 22:37:46 -05:00
orignal
fd9a8fd2b1 eliminated cast to DatabaseStoreMsg 2015-01-02 21:11:40 -05:00
orignal
7b59ce61bb Merge pull request #130 from klondi/httpproxy
Clean up  I2PClientTunnel
2015-01-02 21:06:44 -05:00
Francisco Blas (klondike) Izquierdo Riera
1ae55e5872 Use the new asynchronous API on I2PClientTunnel and clean up after ourselves 2015-01-03 02:43:59 +01:00
Francisco Blas (klondike) Izquierdo Riera
6489230e68 Simplify and merge the identHash caching codepath on I2PClientTunnel 2015-01-03 02:17:01 +01:00
Francisco Blas (klondike) Izquierdo Riera
c05f411ba0 Fix a memory leak in ClientConnection 2015-01-03 02:07:55 +01:00
orignal
b30de01b12 eliminated cast to DeliveryStatus struct 2015-01-02 17:39:35 -05:00
orignal
79e1096eca process follow-on packets with zero send stream id 2015-01-02 10:04:57 -05:00
orignal
f1ae5817eb Merge pull request #129 from klondi/httpproxy
Asynchronous stream creation
2015-01-02 08:12:54 -05:00
Francisco Blas (klondike) Izquierdo Riera
a906d7f02f Allow for asynchronous creation of streams 2015-01-02 13:37:26 +01:00
Francisco Blas (klondike) Izquierdo Riera
50fb373655 Use i2p::data::SigningKeyType instead of uint16_t 2015-01-02 13:37:26 +01:00
orignal
29d1aa0146 more fixes of misalignment 2015-01-01 23:00:33 -05:00
orignal
fa4009821e Merge pull request #128 from klondi/httpproxy
Httpproxy
2015-01-01 19:12:11 -05:00
Francisco Blas (klondike) Izquierdo Riera
e5503c51b4 Allow for easy stream creation at LocalDestination (for now it's only synchronous) 2015-01-02 00:56:26 +01:00
Francisco Blas (klondike) Izquierdo Riera
ccb68088a8 Allow for I2PTunnels without LocalDestination 2015-01-02 00:56:21 +01:00
orignal
0f07b04627 eliminated TunnelGatewayHeader 2015-01-01 18:53:44 -05:00
orignal
bfc6274cd8 methods for I2NP header access 2015-01-01 16:51:15 -05:00
orignal
c5c0d2060c rollback 2015-01-01 11:18:24 -05:00
orignal
dde2b4a879 destroy local destination on 'clear' 2015-01-01 09:54:15 -05:00
orignal
df21a067ff print out number of NACKs 2015-01-01 09:53:30 -05:00
orignal
8c49f76534 copy request to buffer if LeaseSet is requested 2014-12-31 21:47:49 -05:00
orignal
96a9575049 Merge pull request #127 from klondi/alignmentfixes
Fix even more alignment problems
2014-12-31 14:06:53 -05:00
orignal
0a9368fc70 Merge pull request #126 from klondi/cmakefixes
Cmakefixes
2014-12-31 14:03:22 -05:00
Francisco Blas (klondike) Izquierdo Riera
84f7966a0b Fix even more alignment problems 2014-12-31 19:48:46 +01:00
Francisco Blas (klondike) Izquierdo Riera
8f7dea698e Allow disabling binary compilation (useful for multilib builds in Gentoo) 2014-12-31 19:46:09 +01:00
Francisco Blas (klondike) Izquierdo Riera
9e7e0a456d Use better install paths 2014-12-31 19:46:09 +01:00
Francisco Blas (klondike) Izquierdo Riera
64b47a29cf Fix library compilation 2014-12-31 19:46:09 +01:00
orignal
cbfe8b8232 invoke accepter after receiving remote identity 2014-12-30 22:37:14 -05:00
orignal
583838e2c2 fixed misalignment 2014-12-30 15:33:11 -05:00
orignal
7bfb73dacf LeaseSet database lookup 2014-12-30 12:25:08 -05:00
orignal
350e942b6a fixed windows build 2014-12-30 10:30:45 -05:00
orignal
ed82f388e6 Merge pull request #123 from klondi/master
Fix any remaning aliasing rules with propper memcpy wrappers
2014-12-30 09:50:28 -05:00
Francisco Blas (klondike) Izquierdo Riera
b5f624a10f Use htobuf16 for 0 value instead of htobe16buf 2014-12-30 15:42:24 +01:00
Francisco Blas (klondike) Izquierdo Riera
700c53e60a Fix any remaning aliasing rules with propper memcpy wrappers 2014-12-30 15:37:24 +01:00
Kill Your TV
2ed99ba245 fix calling individual targets
(broken in 82af922b40)

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

Note: I'm an OS noob :)
2014-12-29 17:12:05 +01:00
orignal
446e5fd665 report about termination after actual processing 2014-12-28 15:45:58 -05:00
orignal
7847982a57 fixed crash 2014-12-27 22:43:00 -05:00
orignal
86a7f96a46 take tunnels from exploratory pool only 2014-12-27 20:36:32 -05:00
orignal
da01ea997d wait from LeaseSet request comlete 2014-12-27 10:09:55 -05:00
orignal
59aa40e2b0 wait until LeaseSet request complete instead timeout 2014-12-26 21:06:24 -05:00
orignal
6fb5fa1c52 LeaseSet request complete callback 2014-12-26 19:09:44 -05:00
orignal
64df22def8 read reamaining data from closed stream 2014-12-26 08:55:24 -05:00
orignal
bbe403fb40 main netdb can request router info only 2014-12-25 21:13:51 -05:00
orignal
3547a4042c request and handle LeaseSets through local destination only 2014-12-25 16:47:15 -05:00
orignal
46ea2291fe read incoming data util it's available 2014-12-25 10:42:49 -05:00
orignal
66c2c7f789 wait until download complete 2014-12-25 08:41:21 -05:00
orignal
78c06bdd22 manage non-reponded database requests 2014-12-24 11:20:38 -05:00
orignal
afd69e4afd send if-none-match 2014-12-24 09:45:25 -05:00
orignal
ff7ff3b55b check status code 304 2014-12-24 07:48:18 -05:00
orignal
14e2c76799 Merge pull request #118 from hagen-i2p/make-deps
* new makefile target: deps
2014-12-24 07:19:24 -05:00
hagen
677e3585c9 * new makefile target: deps 2014-12-24 05:08:43 +00:00
orignal
e349facd65 HTTP 1.1 for addressbook 2014-12-23 21:03:00 -05:00
orignal
7b5e8a9661 check and update addressbook subscriptions 2014-12-23 13:57:09 -05:00
orignal
3f314d8355 merge chunked responses 2014-12-22 21:20:39 -05:00
orignal
325117114a fixed typo 2014-12-22 21:02:32 -05:00
orignal
e1d445ab50 parse HTTP header fields 2014-12-22 16:45:50 -05:00
orignal
b1b72d2d33 rolled back to HTTP 1.0 since chunked is not implemented 2014-12-22 16:10:32 -05:00
orignal
bb5e520a79 reduce minimal # of routers to 50 2014-12-22 15:14:49 -05:00
orignal
fd0069cb0e fixed merge conflict 2014-12-22 15:09:50 -05:00
orignal
52ee861d3a load subscriptions 2014-12-22 15:06:54 -05:00
Kill Your TV
42075e74ad fetch with HTTP/1.1 2014-12-22 18:01:59 +00:00
orignal
1e87aedbb8 Merge branch 'master' of https://github.com/PrivacySolutions/i2pd 2014-12-21 21:33:00 -05:00
orignal
5221e09b67 use VerifyDigest for verification 2014-12-21 21:32:21 -05:00
orignal
0972782553 delete useless field 2014-12-21 17:27:46 -05:00
Kill Your TV
425c746b87 typo fix 2014-12-21 21:48:30 +00:00
orignal
db2d0df2c4 download hosts.txt without proxy 2014-12-21 09:33:02 -05:00
orignal
da1397ff76 fixed build error 2014-12-20 21:21:43 -05:00
orignal
c009fc5d72 look for LeaseSet in shared local destination 2014-12-20 20:16:45 -05:00
Kill Your TV
993b4c92b0 slightly stricter use of mkdir/rm -rf 2014-12-20 20:11:55 +00:00
Kill Your TV
b26bc5c7f4 Add api target 2014-12-20 20:10:44 +00:00
Kill Your TV
e4cc15d19e Don't build the lib when making static binaries
Without this, building on 64-bit systems will fail.
2014-12-20 19:48:53 +00:00
Kill Your TV
e5c2022f71 Fix building in kFreeBSD
broken in 8c218bd5
2014-12-20 16:57:41 +00:00
Kill Your TV
b343c24a9f add explanatory comments to Makefiles 2014-12-20 16:57:41 +00:00
Kill Your TV
21e3778e69 Revert the 'clean-up' commits so that custom build-time flags can be set
These commits removed the 'NEEDED*' vars which were added so that CXX*
and LDFLAGS could be specified at build time. By doing away with these
and using solely CXXFLAGS and LDFLAGS, special flags cannot be added.
Indeed, specifying your own CXXFLAGS would cause the build to fail. We
want the build flags to be APPENDED, not overwritten.
2014-12-20 16:57:41 +00:00
orignal
f4f6e74ea2 process addressbook request response 2014-12-19 22:03:34 -05:00
orignal
c887f54740 fixed crash 2014-12-19 17:41:08 -05:00
orignal
1179731959 send request to addressbook link directly without proxy 2014-12-19 14:40:02 -05:00
orignal
91e833cdaf fixed crash at shutdown 2014-12-19 12:07:54 -05:00
orignal
84e5f30c70 delete local destination on session close 2014-12-18 18:48:36 -05:00
orignal
b007b66b15 store SAMSession by pointer 2014-12-17 19:02:16 -05:00
orignal
8b05455545 Cancel added 2014-12-17 15:31:13 -05:00
orignal
a44ca91409 terminate acceptor and sessions on stop 2014-12-17 15:21:50 -05:00
orignal
84235fe479 fixed typo 2014-12-17 11:42:16 -05:00
52 changed files with 2488 additions and 1278 deletions

View File

@@ -3,11 +3,17 @@
#include <string>
#include <map>
#include <fstream>
#include <chrono>
#include <condition_variable>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <cryptopp/osrng.h>
#include "base64.h"
#include "util.h"
#include "Identity.h"
#include "Log.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
namespace i2p
@@ -137,7 +143,7 @@ namespace client
f << it.first << "," << it.second.ToBase32 () << std::endl;
num++;
}
LogPrint (eLogInfo, num, " addresses save");
LogPrint (eLogInfo, num, " addresses saved");
}
else
LogPrint (eLogError, "Can't open file ", filename);
@@ -145,17 +151,36 @@ namespace client
}
//---------------------------------------------------------------------
AddressBook::AddressBook (): m_IsLoaded (false), m_IsDowloading (false)
AddressBook::AddressBook (): m_IsLoaded (false), m_IsDownloading (false),
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{
}
AddressBook::~AddressBook ()
{
{
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");
}
if (m_Storage)
{
m_Storage->Save (m_Addresses);
delete m_Storage;
}
delete m_DefaultSubscription;
for (auto it: m_Subscriptions)
delete it;
delete m_SubscriptionsUpdateTimer;
}
AddressBookStorage * AddressBook::CreateStorage ()
@@ -215,7 +240,7 @@ namespace client
m_Storage = CreateStorage ();
m_Storage->AddAddress (ident);
m_Addresses[address] = ident.GetIdentHash ();
LogPrint (address,"->",ident.GetIdentHash ().ToBase32 (), ".b32.i2p added");
LogPrint (address,"->", ToAddress(ident.GetIdentHash ()), " added");
}
void AddressBook::InsertAddress (const i2p::data::IdentityEx& address)
@@ -234,54 +259,42 @@ namespace client
return m_Storage->GetAddress (ident, identity);
}
void AddressBook::LoadHostsFromI2P ()
{
std::string content;
int http_code = i2p::util::http::httpRequestViaI2pProxy("http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt", content);
if (http_code == 200)
{
std::ofstream f_save(i2p::util::filesystem::GetFullPath("hosts.txt").c_str(), std::ofstream::out);
if (f_save.is_open())
{
f_save << content;
f_save.close();
}
else
LogPrint("Can't write hosts.txt");
m_IsLoaded = false;
}
else
LogPrint ("Failed to download hosts.txt");
m_IsDowloading = false;
return;
}
void AddressBook::LoadHosts ()
{
if (!m_Storage)
m_Storage = CreateStorage ();
int numAddresses = m_Storage->Load (m_Addresses);
if (numAddresses > 0)
if (m_Storage->Load (m_Addresses) > 0)
{
m_IsLoaded = true;
return;
}
// otherwise try hosts.txt
// 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 ())
if (f.is_open ())
{
LogPrint ("hosts.txt not found. Try to load...");
if (!m_IsDowloading)
{
m_IsDowloading = true;
std::thread load_hosts(&AddressBook::LoadHostsFromI2P, this);
load_hosts.detach();
}
return;
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 ())
{
@@ -298,17 +311,237 @@ namespace client
std::string addr = s.substr(pos);
i2p::data::IdentityEx ident;
ident.FromBase64(addr);
m_Addresses[name] = ident.GetIdentHash ();
m_Storage->AddAddress (ident);
numAddresses++;
if (ident.FromBase64(addr))
{
m_Addresses[name] = ident.GetIdentHash ();
m_Storage->AddAddress (ident);
numAddresses++;
}
else
LogPrint (eLogError, "Malformed address ", addr, " for ", name);
}
}
LogPrint (numAddresses, " addresses loaded");
m_Storage->Save (m_Addresses);
m_IsLoaded = true;
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;
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 ())
{
// pick random subscription
CryptoPP::AutoSeededRandomPool rnd;
auto ind = rnd.GenerateWord32 (0, m_Subscriptions.size() - 1);
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 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;
const i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (ident);
if (!leaseSet)
{
bool found = false;
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident,
[&newDataReceived, &found](bool success)
{
found = success;
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
LogPrint (eLogError, "Subscription LeseseSet request timeout expired");
if (found)
leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (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[4095];
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_);
LogPrint (eLogInfo, "Download complete ", success ? "Success" : "Failed");
m_Book.DownloadComplete (success);
}
}
}

View File

@@ -4,6 +4,10 @@
#include <string.h>
#include <string>
#include <map>
#include <vector>
#include <iostream>
#include <mutex>
#include <boost/asio.hpp>
#include "base64.h"
#include "util.h"
#include "Identity.h"
@@ -13,6 +17,13 @@ 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 = 240; // in minutes
const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes
const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second
class AddressBookStorage // interface for storage
{
public:
@@ -26,6 +37,7 @@ namespace client
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
};
class AddressBookSubscription;
class AddressBook
{
public:
@@ -37,19 +49,48 @@ namespace client
const i2p::data::IdentHash * FindAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (const i2p::data::IdentityEx& address);
void StartSubscriptions ();
void StopSubscriptions ();
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 ident.ToBase32().append(".b32.i2p"); }
std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); }
private:
AddressBookStorage * CreateStorage ();
void LoadHosts ();
void LoadSubscriptions ();
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
private:
void LoadHosts ();
void LoadHostsFromI2P ();
std::mutex m_AddressBookMutex;
std::map<std::string, i2p::data::IdentHash> m_Addresses;
AddressBookStorage * m_Storage;
bool m_IsLoaded, m_IsDowloading;
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;
};
}
}

29
BOB.cpp
View File

@@ -1,7 +1,6 @@
#include <string.h>
#include <boost/lexical_cast.hpp>
#include "Log.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "BOB.h"
@@ -9,10 +8,9 @@ namespace i2p
{
namespace client
{
BOBI2PInboundTunnel::BOBI2PInboundTunnel (boost::asio::io_service& service, int port, ClientDestination * localDestination):
BOBI2PTunnel (service, localDestination),
m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (service)
BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, ClientDestination * localDestination):
BOBI2PTunnel (localDestination),
m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Timer (localDestination->GetService ())
{
}
@@ -97,7 +95,7 @@ namespace client
CreateConnection (receiver, leaseSet);
else
{
i2p::data::netdb.RequestDestination (ident, true, GetLocalDestination ()->GetTunnelPool ());
GetLocalDestination ()->RequestDestination (ident);
m_Timer.expires_from_now (boost::posix_time::seconds (I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT));
m_Timer.async_wait (std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestTimer,
this, std::placeholders::_1, receiver, ident));
@@ -143,8 +141,8 @@ namespace client
delete receiver;
}
BOBI2POutboundTunnel::BOBI2POutboundTunnel (boost::asio::io_service& service, const std::string& address, int port,
ClientDestination * localDestination, bool quiet): BOBI2PTunnel (service, localDestination),
BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port,
ClientDestination * localDestination, bool quiet): BOBI2PTunnel (localDestination),
m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet)
{
}
@@ -178,8 +176,8 @@ namespace client
}
}
BOBDestination::BOBDestination (boost::asio::io_service& service, ClientDestination& localDestination):
m_Service (service), m_LocalDestination (localDestination),
BOBDestination::BOBDestination (ClientDestination& localDestination):
m_LocalDestination (localDestination),
m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr)
{
}
@@ -188,6 +186,7 @@ namespace client
{
delete m_OutboundTunnel;
delete m_InboundTunnel;
i2p::client::context.DeleteLocalDestination (&m_LocalDestination);
}
void BOBDestination::Start ()
@@ -221,13 +220,13 @@ namespace client
void BOBDestination::CreateInboundTunnel (int port)
{
if (!m_InboundTunnel)
m_InboundTunnel = new BOBI2PInboundTunnel (m_Service, port, &m_LocalDestination);
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 (m_Service, address, port, &m_LocalDestination, quiet);
m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, &m_LocalDestination, quiet);
}
BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
@@ -385,8 +384,7 @@ namespace client
LogPrint (eLogDebug, "BOB: start ", m_Nickname);
if (!m_CurrentDestination)
{
m_CurrentDestination = new BOBDestination (m_Owner.GetService (),
*context.CreateNewLocalDestination (m_Keys, true, &m_Options));
m_CurrentDestination = new BOBDestination (*i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
m_Owner.AddDestination (m_Nickname, m_CurrentDestination);
}
if (m_InPort)
@@ -580,9 +578,10 @@ namespace client
void BOBCommandChannel::Stop ()
{
m_IsRunning = false;
for (auto it: m_Destinations)
it.second->Stop ();
m_IsRunning = false;
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{

12
BOB.h
View File

@@ -45,8 +45,8 @@ namespace client
{
public:
BOBI2PTunnel (boost::asio::io_service& service, ClientDestination * localDestination):
I2PTunnel (service, localDestination) {};
BOBI2PTunnel (ClientDestination * localDestination):
I2PTunnel (localDestination) {};
virtual void Start () {};
virtual void Stop () {};
@@ -66,7 +66,7 @@ namespace client
public:
BOBI2PInboundTunnel (boost::asio::io_service& service, int port, ClientDestination * localDestination);
BOBI2PInboundTunnel (int port, ClientDestination * localDestination);
~BOBI2PInboundTunnel ();
void Start ();
@@ -95,8 +95,7 @@ namespace client
{
public:
BOBI2POutboundTunnel (boost::asio::io_service& service, const std::string& address, int port,
ClientDestination * localDestination, bool quiet);
BOBI2POutboundTunnel (const std::string& address, int port, ClientDestination * localDestination, bool quiet);
void Start ();
void Stop ();
@@ -119,7 +118,7 @@ namespace client
{
public:
BOBDestination (boost::asio::io_service& service, ClientDestination& localDestination);
BOBDestination (ClientDestination& localDestination);
~BOBDestination ();
void Start ();
@@ -131,7 +130,6 @@ namespace client
private:
boost::asio::io_service& m_Service;
ClientDestination& m_LocalDestination;
BOBI2POutboundTunnel * m_OutboundTunnel;
BOBI2PInboundTunnel * m_InboundTunnel;

View File

@@ -48,8 +48,7 @@ namespace client
std::string ircKeys = i2p::util::config::GetArg("-irckeys", "");
if (ircKeys.length () > 0)
localDestination = LoadLocalDestination (ircKeys, false);
m_IrcTunnel = new I2PClientTunnel (m_SocksProxy->GetService (), ircDestination,
i2p::util::config::GetArg("-ircport", 6668), localDestination);
m_IrcTunnel = new I2PClientTunnel (ircDestination, i2p::util::config::GetArg("-ircport", 6668), localDestination);
m_IrcTunnel->Start ();
LogPrint("IRC tunnel started");
}
@@ -57,9 +56,8 @@ namespace client
if (eepKeys.length () > 0) // eepkeys file is presented
{
auto localDestination = LoadLocalDestination (eepKeys, true);
m_ServerTunnel = new I2PServerTunnel (m_SocksProxy->GetService (),
i2p::util::config::GetArg("-eephost", "127.0.0.1"), i2p::util::config::GetArg("-eepport", 80),
localDestination);
m_ServerTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"),
i2p::util::config::GetArg("-eepport", 80), localDestination);
m_ServerTunnel->Start ();
LogPrint("Server tunnel started");
}
@@ -77,10 +75,12 @@ namespace client
m_BOBCommandChannel->Start ();
LogPrint("BOB command channel started");
}
m_AddressBook.StartSubscriptions ();
}
void ClientContext::Stop ()
{
m_AddressBook.StopSubscriptions ();
m_HttpProxy->Stop();
delete m_HttpProxy;
m_HttpProxy = nullptr;
@@ -141,7 +141,7 @@ namespace client
s.read ((char *)buf, len);
keys.FromBuffer (buf, len);
delete[] buf;
LogPrint ("Local address ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p loaded");
LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded");
}
else
{
@@ -154,7 +154,7 @@ namespace client
f.write ((char *)buf, len);
delete[] buf;
LogPrint ("New private keys file ", fullPath, " for ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p created");
LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created");
}
auto localDestination = new ClientDestination (keys, isPublic);
@@ -197,7 +197,7 @@ namespace client
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint ("Local destination ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p exists");
LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists");
if (!it->second->IsRunning ())
{
it->second->Start ();

View File

@@ -18,6 +18,11 @@
#include "HTTPServer.h"
#include "ClientContext.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
namespace i2p
{
namespace util
@@ -89,11 +94,11 @@ namespace i2p
if (isDaemon)
{
std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string();
#ifndef _WIN32
#ifndef _WIN32
logfile_path.append("/i2pd.log");
#else
#else
logfile_path.append("\\i2pd.log");
#endif
#endif
StartLog (logfile_path);
}
else
@@ -111,7 +116,10 @@ namespace i2p
LogPrint("Tunnels started");
i2p::client::context.Start ();
LogPrint("Client started");
#ifdef USE_UPNP
i2p::UPnP::upnpc.Start();
LogPrint("UPnP module loaded");
#endif
return true;
}
@@ -128,6 +136,9 @@ namespace i2p
LogPrint("NetDB stoped");
d.httpServer->Stop();
LogPrint("HTTP Server stoped");
#ifdef USE_UPNP
i2p::UPnP::upnpc.Stop();
#endif
StopLog ();
delete d.httpServer; d.httpServer = nullptr;

View File

@@ -76,11 +76,7 @@ namespace datagram
bool verified = false;
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, buf + headerLen, len - headerLen);
verified = identity.Verify (hash, 32, signature);
}
verified = CryptoPP::SHA256().VerifyDigest (signature, buf + headerLen, len - headerLen);
else
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
@@ -121,7 +117,7 @@ namespace datagram
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload ();
*(uint32_t *)buf = htobe32 (size); // length
htobe32buf (buf, size); // length
buf += 4;
compressor.Get (buf, size);
memset (buf + 4, 0, 4); // source and destination are zeroes

View File

@@ -1,9 +1,12 @@
#include <algorithm>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <cryptopp/dh.h>
#include "Log.h"
#include "util.h"
#include "ElGamal.h"
#include "Timestamp.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "Destination.h"
namespace i2p
@@ -16,8 +19,7 @@ namespace client
m_Keys (keys), m_LeaseSet (nullptr), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service)
{
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
if (params)
@@ -45,17 +47,24 @@ namespace client
}
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen);
if (m_IsPublic)
LogPrint (eLogInfo, "Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p created");
LogPrint (eLogInfo, "Local address ", i2p::client::context.GetAddressBook ().ToAddress(GetIdentHash()), " created");
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
}
ClientDestination::~ClientDestination ()
{
Stop ();
if (m_IsRunning)
Stop ();
for (auto it: m_LeaseSetRequests)
delete it.second;
for (auto it: m_RemoteLeaseSets)
delete it.second;
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
if (m_StreamingDestination)
delete m_StreamingDestination;
if (m_DatagramDestination)
delete m_DatagramDestination;
}
void ClientDestination::Run ()
@@ -75,34 +84,40 @@ namespace client
void ClientDestination::Start ()
{
m_Pool->SetLocalDestination (this);
m_Pool->SetActive (true);
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
m_StreamingDestination->Start ();
if (!m_IsRunning)
{
m_IsRunning = true;
m_Pool->SetLocalDestination (this);
m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
m_StreamingDestination->Start ();
}
}
void ClientDestination::Stop ()
{
m_StreamingDestination->Stop ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
if (m_Pool)
if (m_IsRunning)
{
m_Pool->SetLocalDestination (nullptr);
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
}
m_IsRunning = false;
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
m_IsRunning = false;
m_StreamingDestination->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;
}
}
}
@@ -116,7 +131,7 @@ namespace client
else
{
LogPrint ("All leases of remote LeaseSet expired. Request it");
i2p::data::netdb.RequestDestination (ident, true, m_Pool);
RequestDestination (ident);
}
}
else
@@ -180,15 +195,17 @@ namespace client
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
{
I2NPHeader * header = (I2NPHeader *)buf;
switch (header->typeID)
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID)
{
case eI2NPData:
HandleDataMessage (buf + sizeof (I2NPHeader), be16toh (header->size));
HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (buf + sizeof (I2NPHeader), be16toh (header->size));
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); // TODO: remove
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break;
default:
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
@@ -197,14 +214,14 @@ namespace client
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
{
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
size_t offset = sizeof (I2NPDatabaseStoreMsg);
if (msg->replyToken) // TODO:
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
if (replyToken) // TODO:
offset += 36;
if (msg->type == 1) // LeaseSet
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
{
LogPrint (eLogDebug, "Remote LeaseSet");
auto it = m_RemoteLeaseSets.find (msg->key);
auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET);
if (it != m_RemoteLeaseSets.end ())
{
it->second->Update (buf + offset, len - offset);
@@ -213,17 +230,69 @@ namespace client
else
{
LogPrint (eLogDebug, "New remote LeaseSet added");
m_RemoteLeaseSets[msg->key] = new i2p::data::LeaseSet (buf + offset, len - offset);
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = new i2p::data::LeaseSet (buf + offset, len - offset);
}
}
else
LogPrint (eLogError, "Unexpected client's DatabaseStore type ", msg->type, ". Dropped");
}
LogPrint (eLogError, "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 (true);
delete it1->second;
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 ("DatabaseSearchReply for ", key.ToBase64 (), " num=", num);
auto it = m_LeaseSetRequests.find (key);
if (it != m_LeaseSetRequests.end ())
{
LeaseSetRequest * 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, "Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
else
{
LogPrint (eLogInfo, "Found new floodfill. Request it");
i2p::data::netdb.RequestDestination (peerHash);
}
}
if (!found)
LogPrint (eLogError, "Suggested floodfills are not presented in netDb");
}
else
LogPrint (eLogInfo, key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST," floodfills");
if (!found)
{
if (request->requestComplete) request->requestComplete (false);
delete request;
m_LeaseSetRequests.erase (key);
}
}
else
LogPrint ("Request for ", key.ToBase64 (), " not found");
}
void ClientDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
{
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
uint32_t msgID = be32toh (deliveryStatus->msgID);
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken)
{
LogPrint (eLogDebug, "Publishing confirmed");
@@ -242,7 +311,7 @@ namespace client
if (m_IsPublic)
Publish ();
}
void ClientDestination::Publish ()
{
if (!m_LeaseSet || !m_Pool)
@@ -294,7 +363,7 @@ namespace client
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = be32toh (*(uint32_t *)buf);
uint32_t length = bufbe32toh (buf);
buf += 4;
// we assume I2CP payload
switch (buf[9])
@@ -318,12 +387,49 @@ namespace client
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
assert(streamRequestComplete);
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
CreateStream (streamRequestComplete, identHash, port);
else
{
LogPrint (eLogWarning, "Remote destination ", dest, " not found");
streamRequestComplete (nullptr);
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) {
assert(streamRequestComplete);
const i2p::data::LeaseSet * leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (*leaseSet, port));
else
{
RequestDestination (dest,
[this, streamRequestComplete, dest, port](bool success)
{
if (!success)
streamRequestComplete (nullptr);
else
{
const i2p::data::LeaseSet * leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (*leaseSet, port));
else
streamRequestComplete (nullptr);
}
});
}
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::LeaseSet& remote, int port)
{
if (m_StreamingDestination)
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
return nullptr;
}
else
return nullptr;
}
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
{
@@ -350,5 +456,111 @@ namespace client
m_DatagramDestination = new i2p::datagram::DatagramDestination (*this);
return m_DatagramDestination;
}
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
if (!m_Pool || !IsReady ())
{
if (requestComplete) requestComplete (false);
return false;
}
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, this, dest, requestComplete));
return true;
}
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)
{
LeaseSetRequest * request = new LeaseSetRequest (m_Service);
request->requestComplete = requestComplete;
m_LeaseSetRequests[dest] = request;
if (!SendLeaseSetRequest (dest, floodfill, request))
{
// request failed
if (request->requestComplete) request->requestComplete (false);
delete request;
m_LeaseSetRequests.erase (dest);
}
}
else
LogPrint (eLogError, "No floodfills found");
}
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, LeaseSetRequest * request)
{
auto replyTunnel = m_Pool->GetNextInboundTunnel ();
if (!replyTunnel) LogPrint (eLogError, "No inbound tunnels found");
auto outboundTunnel = m_Pool->GetNextOutboundTunnel ();
if (!outboundTunnel) LogPrint (eLogError, "No outbound tunnels found");
if (replyTunnel && outboundTunnel)
{
request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
request->requestTimeoutTimer.cancel ();
CryptoPP::AutoSeededRandomPool rnd;
uint8_t replyKey[32], replyTag[32];
rnd.GenerateBlock (replyKey, 32); // random session key
rnd.GenerateBlock (replyTag, 32); // random session tag
AddSessionKey (replyKey, replyTag);
I2NPMessage * msg = WrapMessage (*nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
replyTunnel, 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)
SendLeaseSetRequest (dest, floodfill, it->second);
else
done = true;
}
else
{
LogPrint (eLogInfo, dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
done = true;
}
if (done)
{
if (it->second->requestComplete) it->second->requestComplete (false);
delete it->second;
m_LeaseSetRequests.erase (it);
}
}
}
}
}
}

View File

@@ -5,12 +5,16 @@
#include <mutex>
#include <memory>
#include <map>
#include <set>
#include <string>
#include <functional>
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
#include "CryptoConst.h"
#include "LeaseSet.h"
#include "Garlic.h"
#include "NetDb.h"
#include "Streaming.h"
#include "Datagram.h"
@@ -22,15 +26,31 @@ namespace client
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18;
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
const 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 int STREAM_REQUEST_TIMEOUT = 60; //in seconds
class ClientDestination: public i2p::garlic::GarlicDestination
{
typedef std::function<void (bool success)> RequestComplete;
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;
};
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
public:
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
@@ -43,9 +63,12 @@ namespace client
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); };
const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
// streaming
i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; };
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::LeaseSet& remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
@@ -79,18 +102,24 @@ namespace client
void UpdateLeaseSet ();
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (I2NPMessage * msg);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, LeaseSetRequest * request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
private:
bool m_IsRunning;
volatile bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
i2p::data::PrivateKeys m_Keys;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
std::map<i2p::data::IdentHash, i2p::data::LeaseSet *> m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, LeaseSetRequest *> m_LeaseSetRequests;
i2p::tunnel::TunnelPool * m_Pool;
i2p::data::LeaseSet * m_LeaseSet;

View File

@@ -4,6 +4,7 @@
#include <inttypes.h>
#include <cryptopp/integer.h>
#include <cryptopp/osrng.h>
#include <cryptopp/dh.h>
#include <cryptopp/sha.h>
#include "CryptoConst.h"
#include "Log.h"
@@ -51,7 +52,6 @@ namespace crypto
CryptoPP::AutoSeededRandomPool rnd;
CryptoPP::Integer y, k, a, b1;
bool m_ZeroPadding;
};
inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
@@ -71,6 +71,17 @@ namespace crypto
memcpy (data, m + 33, 222);
return true;
}
inline void GenerateElGamalKeyPair (CryptoPP::RandomNumberGenerator& rnd, uint8_t * priv, uint8_t * pub)
{
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
rnd.GenerateBlock (priv, 256);
a_exp_b_mod_c (elgg, CryptoPP::Integer (priv, 256), elgp).Encode (pub, 256);
#else
CryptoPP::DH dh (elgp, elgg);
dh.GenerateKeyPair(rnd, priv, pub);
#endif
}
}
}

View File

@@ -138,7 +138,7 @@ namespace garlic
}
// AES block
len += CreateAESBlock (buf, msg);
*(uint32_t *)(m->GetPayload ()) = htobe32 (len);
htobe32buf (m->GetPayload (), len);
m->len += len + 4;
FillI2NPMessageHeader (m, eI2NPGarlic);
if (msg)
@@ -151,7 +151,7 @@ namespace garlic
size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags/2);
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
*(uint16_t *)buf = newTags ? htobe16 (newTags->numTags) : 0; // tag count
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
blockSize += 2;
if (newTags) // session tags recreated
{
@@ -168,7 +168,7 @@ namespace garlic
buf[blockSize] = 0; // flag
blockSize++;
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
*payloadSize = htobe32 (len);
htobe32buf (payloadSize, len);
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
blockSize += len;
size_t rem = blockSize % 16;
@@ -220,9 +220,9 @@ namespace garlic
memset (payload + size, 0, 3); // certificate of message
size += 3;
*(uint32_t *)(payload + size) = htobe32 (msgID); // MessageID
htobe32buf (payload + size, msgID); // MessageID
size += 4;
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message
htobe64buf (payload + size, ts); // Expiration of message
size += 8;
return size;
}
@@ -246,9 +246,9 @@ namespace garlic
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
size += 4;
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
memset (buf + size, 0, 3); // certificate of clove
size += 3;
@@ -269,7 +269,7 @@ namespace garlic
// hash and tunnelID sequence is reversed for Garlic
memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash
size += 32;
*(uint32_t *)(buf + size) = htobe32 (leases[i].tunnelID); // tunnelID
htobe32buf (buf + size, leases[i].tunnelID); // tunnelID
size += 4;
// create msg
I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
@@ -288,9 +288,9 @@ namespace garlic
DeleteI2NPMessage (msg);
// fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
size += 4;
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
htobe64buf (buf + size, ts); // Expiration of clove
size += 8;
memset (buf + size, 0, 3); // certificate of clove
size += 3;
@@ -331,7 +331,7 @@ namespace garlic
void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg)
{
uint8_t * buf = msg->GetPayload ();
uint32_t length = be32toh (*(uint32_t *)buf);
uint32_t length = bufbe32toh (buf);
buf += 4; // length
auto it = m_Tags.find (SessionTag(buf));
if (it != m_Tags.end ())
@@ -389,7 +389,7 @@ namespace garlic
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
i2p::tunnel::InboundTunnel * from)
{
uint16_t tagCount = be16toh (*(uint16_t *)buf);
uint16_t tagCount = bufbe16toh (buf);
buf += 2; len -= 2;
if (tagCount > 0)
{
@@ -404,7 +404,7 @@ namespace garlic
}
buf += tagCount*32;
len -= tagCount*32;
uint32_t payloadSize = be32toh (*(uint32_t *)buf);
uint32_t payloadSize = bufbe32toh (buf);
if (payloadSize > len)
{
LogPrint (eLogError, "Unexpected payload size ", payloadSize);
@@ -418,9 +418,7 @@ namespace garlic
buf++; // flag
// payload
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, buf, payloadSize);
if (memcmp (hash, payloadHash, 32)) // payload hash doesn't match
if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match
{
LogPrint ("Wrong payload hash");
return;
@@ -462,7 +460,7 @@ namespace garlic
// gwHash and gwTunnel sequence is reverted
uint8_t * gwHash = buf;
buf += 32;
uint32_t gwTunnel = be32toh (*(uint32_t *)buf);
uint32_t gwTunnel = bufbe32toh (buf);
buf += 4;
i2p::tunnel::OutboundTunnel * tunnel = nullptr;
if (from && from->GetTunnelPool ())
@@ -528,8 +526,7 @@ namespace garlic
void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
{
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
uint32_t msgID = be32toh (deliveryStatus->msgID);
uint32_t msgID = bufbe32toh (msg->GetPayload ());
{
auto it = m_CreatedSessions.find (msgID);
if (it != m_CreatedSessions.end ())

View File

@@ -485,17 +485,17 @@ namespace util
{
switch (status)
{
case 105: buffers.push_back(boost::asio::buffer("HTTP/1.0 105 Name Not Resolved\r\n")); break;
case 200: buffers.push_back(boost::asio::buffer("HTTP/1.0 200 OK\r\n")); break;
case 400: buffers.push_back(boost::asio::buffer("HTTP/1.0 400 Bad Request\r\n")); break;
case 404: buffers.push_back(boost::asio::buffer("HTTP/1.0 404 Not Found\r\n")); break;
case 408: buffers.push_back(boost::asio::buffer("HTTP/1.0 408 Request Timeout\r\n")); break;
case 500: buffers.push_back(boost::asio::buffer("HTTP/1.0 500 Internal Server Error\r\n")); break;
case 502: buffers.push_back(boost::asio::buffer("HTTP/1.0 502 Bad Gateway\r\n")); break;
case 503: buffers.push_back(boost::asio::buffer("HTTP/1.0 503 Not Implemented\r\n")); break;
case 504: buffers.push_back(boost::asio::buffer("HTTP/1.0 504 Gateway Timeout\r\n")); break;
case 105: buffers.push_back(boost::asio::buffer("HTTP/1.1 105 Name Not Resolved\r\n")); break;
case 200: buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); break;
case 400: buffers.push_back(boost::asio::buffer("HTTP/1.1 400 Bad Request\r\n")); break;
case 404: buffers.push_back(boost::asio::buffer("HTTP/1.1 404 Not Found\r\n")); break;
case 408: buffers.push_back(boost::asio::buffer("HTTP/1.1 408 Request Timeout\r\n")); break;
case 500: buffers.push_back(boost::asio::buffer("HTTP/1.1 500 Internal Server Error\r\n")); break;
case 502: buffers.push_back(boost::asio::buffer("HTTP/1.1 502 Bad Gateway\r\n")); break;
case 503: buffers.push_back(boost::asio::buffer("HTTP/1.1 503 Not Implemented\r\n")); break;
case 504: buffers.push_back(boost::asio::buffer("HTTP/1.1 504 Gateway Timeout\r\n")); break;
default:
buffers.push_back(boost::asio::buffer("HTTP/1.0 200 OK\r\n"));
buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n"));
}
for (std::size_t i = 0; i < headers.size(); ++i)
@@ -792,7 +792,7 @@ namespace util
std::string b32 = it.first.ToBase32 ();
s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION;
s << "&" << HTTP_PARAM_BASE32_ADDRESS << "=" << b32 << ">";
s << b32 << ".b32.i2p</a><br>" << std::endl;
s << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetIdentHash()) << "</a><br>" << std::endl;
}
}
@@ -832,7 +832,7 @@ namespace util
s << "<br><b>Streams:</b><br>";
for (auto it: dest->GetStreamingDestination ()->GetStreams ())
{
s << it.first << "->" << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase32 () << ".b32.i2p ";
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 << "<br>"<< std::endl;
@@ -874,10 +874,12 @@ namespace util
SendToDestination (leaseSet, port, buf, len);
else
{
i2p::data::netdb.RequestDestination (destination, true, i2p::client::context.GetSharedLocalDestination ()->GetTunnelPool ());
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 (boost::bind (&HTTPConnection::HandleDestinationRequestTimeout,
this, boost::asio::placeholders::error, destination, port, buf, len));
this, boost::asio::placeholders::error, destination, port, m_Buffer, m_BufferLen));
}
}

View File

@@ -1,7 +1,6 @@
#include <string.h>
#include <atomic>
#include "I2PEndian.h"
#include <cryptopp/sha.h>
#include <cryptopp/gzip.h>
#include "ElGamal.h"
#include "Timestamp.h"
@@ -40,31 +39,26 @@ namespace i2p
static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID)
{
I2NPHeader * header = msg->GetHeader ();
header->typeID = msgType;
msg->SetTypeID (msgType);
if (replyMsgID) // for tunnel creation
header->msgID = htobe32 (replyMsgID);
msg->SetMsgID (replyMsgID);
else
{
header->msgID = htobe32 (I2NPmsgID);
msg->SetMsgID (I2NPmsgID);
I2NPmsgID++;
}
header->expiration = htobe64 (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
int len = msg->GetLength () - sizeof (I2NPHeader);
header->size = htobe16 (len);
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, msg->GetPayload (), len);
header->chks = hash[0];
}
msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
msg->UpdateSize ();
msg->UpdateChks ();
}
void RenewI2NPMessageHeader (I2NPMessage * msg)
{
if (msg)
{
I2NPHeader * header = msg->GetHeader ();
header->msgID = htobe32 (I2NPmsgID);
msg->SetMsgID (I2NPmsgID);
I2NPmsgID++;
header->expiration = htobe64 (i2p::util::GetMillisecondsSinceEpoch () + 5000);
msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000);
}
}
@@ -88,23 +82,25 @@ namespace i2p
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
{
I2NPDeliveryStatusMsg msg;
I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload ();
if (msgID)
{
msg.msgID = htobe32 (msgID);
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ());
}
else // for SSU establishment
{
msg.msgID = htobe32 (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
msg.timestamp = htobe64 (2); // netID = 2
}
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
}
m->len += DELIVERY_STATUS_SIZE;
FillI2NPMessageHeader (m, eI2NPDeliveryStatus);
return m;
}
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers,
bool encryption, i2p::tunnel::TunnelPool * pool)
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{
I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload ();
@@ -112,65 +108,81 @@ namespace i2p
buf += 32;
memcpy (buf, from, 32); // from
buf += 32;
uint8_t flag = exploratory ? 0x0C : 0x08; // 1000 - RI, 1100 -exporatory
if (replyTunnelID)
{
*buf = encryption ? 0x03: 0x01; // set delivery flag
*(uint32_t *)(buf+1) = htobe32 (replyTunnelID);
*buf = flag | 0x01; // set delivery flag
htobe32buf (buf+1, replyTunnelID);
buf += 5;
}
else
{
encryption = false; // encryption can we set for tunnels only
*buf = 0; // flag
*buf = flag; // flag
buf++;
}
if (exploratory)
if (excludedPeers)
{
*(uint16_t *)buf = htobe16 (1); // one exlude record
int cnt = excludedPeers->size ();
htobe16buf (buf, cnt);
buf += 2;
// reply with non-floodfill routers only
memset (buf, 0, 32);
buf += 32;
for (auto& it: *excludedPeers)
{
memcpy (buf, it, 32);
buf += 32;
}
}
else
{
if (excludedPeers)
{
int cnt = excludedPeers->size ();
*(uint16_t *)buf = htobe16 (cnt);
buf += 2;
for (auto& it: *excludedPeers)
{
memcpy (buf, it, 32);
buf += 32;
}
}
else
{
// nothing to exclude
*(uint16_t *)buf = htobe16 (0);
buf += 2;
}
}
if (encryption)
{
// session key and tag for reply
auto& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (buf, 32); // key
buf[32] = 1; // 1 tag
rnd.GenerateBlock (buf + 33, 32); // tag
if (pool && pool->GetLocalDestination ())
pool->GetLocalDestination ()->SubmitSessionKey (buf, buf + 33); // introduce new key-tag to garlic engine
else
LogPrint ("Destination for encrypteed reply not specified");
buf += 65;
}
{
// nothing to exclude
htobuf16 (buf, 0);
buf += 2;
}
m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
return m;
}
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
{
I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload ();
memcpy (buf, dest, 32); // key
buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32;
*buf = 7; // flags (01 - tunnel, 10 - encrypted, 0100 - LS lookup
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 5;
// excluded
int cnt = excludedFloodfills.size ();
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 ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
return m;
}
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
const i2p::data::RouterInfo * floodfill)
{
@@ -199,22 +211,22 @@ namespace i2p
router = &context.GetRouterInfo ();
I2NPMessage * m = NewI2NPShortMessage ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
uint8_t * payload = m->GetPayload ();
memcpy (msg->key, router->GetIdentHash (), 32);
msg->type = 0;
msg->replyToken = 0;
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 0;
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
CryptoPP::Gzip compressor;
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
compressor.MessageEnd();
auto size = compressor.MaxRetrievable ();
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
*(uint16_t *)buf = htobe16 (size); // size
uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
htobe16buf (buf, size); // size
buf += 2;
// TODO: check if size doesn't exceed buffer
compressor.Get (buf, size);
m->len += sizeof (I2NPDatabaseStoreMsg) + 2 + size; // payload size
m->len += DATABASE_STORE_HEADER_SIZE + 2 + size; // payload size
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m;
@@ -225,23 +237,22 @@ namespace i2p
if (!leaseSet) return nullptr;
I2NPMessage * m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)payload;
memcpy (msg->key, leaseSet->GetIdentHash (), 32);
msg->type = 1; // LeaseSet
msg->replyToken = htobe32 (replyToken);
size_t size = sizeof (I2NPDatabaseStoreMsg);
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
auto leases = leaseSet->GetNonExpiredLeases ();
if (leases.size () > 0)
{
*(uint32_t *)(payload + size) = htobe32 (leases[0].tunnelID);
htobe32buf (payload + size, leases[0].tunnelID);
size += 4; // reply tunnelID
memcpy (payload + size, leases[0].tunnelGateway, 32);
size += 32; // reply tunnel gateway
}
else
msg->replyToken = 0;
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
}
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen ();
@@ -249,73 +260,46 @@ namespace i2p
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m;
}
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
const uint8_t * ourIdent, uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
const uint8_t * replyKey, const uint8_t * replyIV, uint32_t nextMessageID,
bool isGateway, bool isEndpoint)
{
I2NPBuildRequestRecordClearText clearText;
clearText.receiveTunnel = htobe32 (receiveTunnelID);
clearText.nextTunnel = htobe32(nextTunnelID);
memcpy (clearText.layerKey, layerKey, 32);
memcpy (clearText.ivKey, ivKey, 32);
memcpy (clearText.replyKey, replyKey, 32);
memcpy (clearText.replyIV, replyIV, 16);
clearText.flag = 0;
if (isGateway) clearText.flag |= 0x80;
if (isEndpoint) clearText.flag |= 0x40;
memcpy (clearText.ourIdent, ourIdent, 32);
memcpy (clearText.nextIdent, nextIdent, 32);
clearText.requestTime = htobe32 (i2p::util::GetHoursSinceEpoch ());
clearText.nextMessageID = htobe32(nextMessageID);
return clearText;
}
void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router,
const I2NPBuildRequestRecordClearText& clearText,
I2NPBuildRequestRecordElGamalEncrypted& record)
{
router.GetElGamalEncryption ()->Encrypt ((uint8_t *)&clearText, sizeof(clearText), record.encrypted);
memcpy (record.toPeer, (const uint8_t *)router.GetIdentHash (), 16);
}
bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText)
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
{
for (int i = 0; i < num; i++)
{
if (!memcmp (records[i].toPeer, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint ("Record ",i," is ours");
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), records[i].encrypted, (uint8_t *)&clearText);
// replace record to reply
I2NPBuildResponseRecord * reply = (I2NPBuildResponseRecord *)(records + i);
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
// replace record to reply
if (i2p::context.AcceptsTunnels ())
{
i2p::tunnel::TransitTunnel * transitTunnel =
i2p::tunnel::CreateTransitTunnel (
be32toh (clearText.receiveTunnel),
clearText.nextIdent, be32toh (clearText.nextTunnel),
clearText.layerKey, clearText.ivKey,
clearText.flag & 0x80, clearText.flag & 0x40);
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
reply->ret = 0;
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
}
else
reply->ret = 30; // always reject with bandwidth reason (30)
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
//TODO: fill filler
CryptoPP::SHA256().CalculateDigest(reply->hash, reply->padding, sizeof (reply->padding) + 1); // + 1 byte of ret
CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET,
record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret
// encrypt reply
i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++)
{
encryption.SetKey (clearText.replyKey);
encryption.SetIV (clearText.replyIV);
encryption.Encrypt((uint8_t *)(records + j), sizeof (records[j]), (uint8_t *)(records + j));
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
return true;
}
@@ -347,41 +331,42 @@ namespace i2p
}
else
{
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(buf+1);
I2NPBuildRequestRecordClearText clearText;
if (HandleBuildRequestRecords (num, records, clearText))
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText.flag & 0x40) // we are endpoint of outboud tunnel
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText.nextIdent,
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel),
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
be32toh (clearText.nextMessageID)));
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText.nextIdent,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, be32toh (clearText.nextMessageID)));
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
}
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
I2NPBuildRequestRecordClearText clearText;
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, (I2NPBuildRequestRecordElGamalEncrypted *)buf, clearText))
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
{
if (clearText.flag & 0x40) // we are endpoint of outbound tunnel
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText.nextIdent,
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel),
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPTunnelBuildReply, buf, len,
be32toh (clearText.nextMessageID)));
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText.nextIdent,
CreateI2NPMessage (eI2NPTunnelBuild, buf, len, be32toh (clearText.nextMessageID)));
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
@@ -422,7 +407,7 @@ namespace i2p
{
I2NPMessage * msg = NewI2NPMessage ();
memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
*(uint32_t *)(msg->GetPayload ()) = htobe32 (tunnelID);
htobe32buf (msg->GetPayload (), tunnelID);
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
FillI2NPMessageHeader (msg, eI2NPTunnelData);
return msg;
@@ -431,26 +416,26 @@ namespace i2p
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
{
I2NPMessage * msg = NewI2NPMessage (len);
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
header->tunnelID = htobe32 (tunnelID);
header->length = htobe16 (len);
memcpy (msg->GetPayload () + sizeof (TunnelGatewayHeader), buf, len);
msg->len += sizeof (TunnelGatewayHeader) + len;
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len);
msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
return msg;
}
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg)
{
if (msg->offset >= sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader))
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
{
// message is capable to be used without copying
TunnelGatewayHeader * header = (TunnelGatewayHeader *)(msg->GetBuffer () - sizeof (TunnelGatewayHeader));
header->tunnelID = htobe32 (tunnelID);
uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE;
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
int len = msg->GetLength ();
header->length = htobe16 (len);
msg->offset -= (sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader));
msg->len = msg->offset + sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader) +len;
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
return msg;
}
@@ -466,7 +451,7 @@ namespace i2p
const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
I2NPMessage * msg = NewI2NPMessage (len);
size_t gatewayMsgOffset = sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->offset += gatewayMsgOffset;
msg->len += gatewayMsgOffset;
memcpy (msg->GetPayload (), buf, len);
@@ -474,24 +459,25 @@ namespace i2p
FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message
len = msg->GetLength ();
msg->offset -= gatewayMsgOffset;
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
header->tunnelID = htobe32 (tunnelID);
header->length = htobe16 (len);
uint8_t * payload = msg->GetPayload ();
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message
return msg;
}
void HandleTunnelGatewayMsg (I2NPMessage * msg)
{
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
uint32_t tunnelID = be32toh(header->tunnelID);
uint16_t len = be16toh(header->length);
const uint8_t * payload = msg->GetPayload ();
uint32_t tunnelID = bufbe32toh(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET);
uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET);
// we make payload as new I2NP message to send
msg->offset += sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->len = msg->offset + len;
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID);
if (msg->GetHeader()->typeID == eI2NPDatabaseStore ||
msg->GetHeader()->typeID == eI2NPDatabaseSearchReply)
auto typeID = msg->GetTypeID ();
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)typeID);
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply)
{
// transit DatabaseStore my contain new/updated RI
// or DatabaseSearchReply with new routers
@@ -511,19 +497,18 @@ namespace i2p
size_t GetI2NPMessageLength (const uint8_t * msg)
{
I2NPHeader * header = (I2NPHeader *)msg;
return be16toh (header->size) + sizeof (I2NPHeader);
return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
}
void HandleI2NPMessage (uint8_t * msg, size_t len)
{
I2NPHeader * header = (I2NPHeader *)msg;
uint32_t msgID = be32toh (header->msgID);
LogPrint ("I2NP msg received len=", len,", type=", (int)header->typeID, ", msgID=", (unsigned int)msgID);
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
uint8_t * buf = msg + sizeof (I2NPHeader);
int size = be16toh (header->size);
switch (header->typeID)
uint8_t * buf = msg + I2NP_HEADER_SIZE;
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
switch (typeID)
{
case eI2NPVariableTunnelBuild:
LogPrint ("VariableTunnelBuild");
@@ -542,7 +527,7 @@ namespace i2p
// TODO:
break;
default:
LogPrint ("Unexpected message ", (int)header->typeID);
LogPrint ("Unexpected message ", (int)typeID);
}
}
@@ -550,7 +535,7 @@ namespace i2p
{
if (msg)
{
switch (msg->GetHeader ()->typeID)
switch (msg->GetTypeID ())
{
case eI2NPTunnelData:
LogPrint ("TunnelData");

View File

@@ -3,80 +3,71 @@
#include <inttypes.h>
#include <set>
#include <cryptopp/sha.h>
#include <string.h>
#include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "LeaseSet.h"
namespace i2p
{
#pragma pack (1)
{
// I2NP header
const size_t I2NP_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4;
const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8;
const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2;
const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1;
struct I2NPHeader
{
uint8_t typeID;
uint32_t msgID;
uint64_t expiration;
uint16_t size;
uint8_t chks;
};
struct I2NPHeaderShort
{
uint8_t typeID;
uint32_t shortExpiration;
};
struct I2NPDatabaseStoreMsg
{
uint8_t key[32];
uint8_t type;
uint32_t replyToken;
};
struct I2NPDeliveryStatusMsg
{
uint32_t msgID;
uint64_t timestamp;
};
// I2NP short header
const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1;
const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4;
struct I2NPBuildRequestRecordClearText
{
uint32_t receiveTunnel;
uint8_t ourIdent[32];
uint32_t nextTunnel;
uint8_t nextIdent[32];
uint8_t layerKey[32];
uint8_t ivKey[32];
uint8_t replyKey[32];
uint8_t replyIV[16];
uint8_t flag;
uint32_t requestTime;
uint32_t nextMessageID;
uint8_t filler[29];
};
// Tunnel Gateway header
const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0;
const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4;
const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2;
struct I2NPBuildResponseRecord
{
uint8_t hash[32];
uint8_t padding[495];
uint8_t ret;
};
// DeliveryStatus
const size_t DELIVERY_STATUS_MSGID_OFFSET = 0;
const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4;
const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8;
// DatabaseStore
const size_t DATABASE_STORE_KEY_OFFSET = 0;
const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32;
const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1;
const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4;
// TunnelBuild
const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
//BuildRequestRecordClearText
const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222;
struct I2NPBuildRequestRecordElGamalEncrypted
{
uint8_t toPeer[16];
uint8_t encrypted[512];
};
struct TunnelGatewayHeader
{
uint32_t tunnelID;
uint16_t length;
};
// BuildRequestRecordEncrypted
const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16;
#pragma pack ()
// BuildResponseRecord
const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0;
const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32;
const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495;
const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE;
enum I2NPMessageType
{
@@ -110,14 +101,36 @@ namespace tunnel
size_t len, offset, maxLen;
i2p::tunnel::InboundTunnel * from;
I2NPMessage (): buf (nullptr),len (sizeof (I2NPHeader) + 2),
offset(2), maxLen (0), from (nullptr) {};
// reserve 2 bytes for NTCP header
I2NPHeader * GetHeader () { return (I2NPHeader *)GetBuffer (); };
uint8_t * GetPayload () { return GetBuffer () + sizeof(I2NPHeader); };
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
// header accessors
uint8_t * GetHeader () { return GetBuffer (); };
const uint8_t * GetHeader () const { return GetBuffer (); };
void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; };
void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); };
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); };
uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); };
void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); };
uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); };
void UpdateSize () { SetSize (GetPayloadLength ()); };
void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; };
void UpdateChks ()
{
uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ());
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
}
// payload
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; };
uint8_t * GetBuffer () { return buf + offset; };
const uint8_t * GetBuffer () const { return buf + offset; };
size_t GetLength () const { return len - offset; };
size_t GetLength () const { return len - offset; };
size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; };
void Align (size_t alignment)
{
size_t rem = ((size_t)GetBuffer ()) % alignment;
@@ -137,25 +150,25 @@ namespace tunnel
}
// for SSU only
uint8_t * GetSSUHeader () { return buf + offset + sizeof(I2NPHeader) - sizeof(I2NPHeaderShort); };
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
{
I2NPHeaderShort ssu = *(I2NPHeaderShort *)GetSSUHeader ();
I2NPHeader * header = GetHeader ();
header->typeID = ssu.typeID;
header->msgID = htobe32 (msgID);
header->expiration = htobe64 (be32toh (ssu.shortExpiration)*1000LL);
header->size = htobe16 (len - offset - sizeof (I2NPHeader));
header->chks = 0;
const uint8_t * ssu = GetSSUHeader ();
GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID (msgID);
SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL);
SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0);
}
uint32_t ToSSU () // return msgID
{
I2NPHeader header = *GetHeader ();
I2NPHeaderShort * ssu = (I2NPHeaderShort *)GetSSUHeader ();
ssu->typeID = header.typeID;
ssu->shortExpiration = htobe32 (be64toh (header.expiration)/1000LL);
len = offset + sizeof (I2NPHeaderShort) + be16toh (header.size);
return be32toh (header.msgID);
uint8_t header[I2NP_HEADER_SIZE];
memcpy (header, GetHeader (), I2NP_HEADER_SIZE);
uint8_t * ssu = GetSSUHeader ();
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
}
};
@@ -176,26 +189,17 @@ namespace tunnel
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from = nullptr);
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false,
std::set<i2p::data::IdentHash> * excludedPeers = nullptr, bool encryption = false,
i2p::tunnel::TunnelPool * pool = nullptr);
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0);
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
const uint8_t * ourIdent, uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
const uint8_t * replyKey, const uint8_t * replyIV, uint32_t nextMessageID,
bool isGateway, bool isEndpoint);
void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router,
const I2NPBuildRequestRecordClearText& clearText,
I2NPBuildRequestRecordElGamalEncrypted& record);
bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText);
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);

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
#include "base64.h"
#include "Log.h"
#include "NetDb.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PTunnel.h"
@@ -17,6 +16,13 @@ namespace client
m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet);
}
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
boost::asio::ip::tcp::socket * socket, std::shared_ptr<i2p::stream::Stream> stream):
m_Socket (socket), m_Stream (stream), m_Owner (owner),
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
{
}
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream,
boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
m_Socket (socket), m_Stream (stream), m_Owner (owner), m_RemoteEndpoint (target), m_IsQuiet (quiet)
@@ -67,7 +73,7 @@ namespace client
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
{
LogPrint ("I2PTunnel read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
@@ -140,6 +146,11 @@ namespace client
}
}
I2PTunnel::I2PTunnel (ClientDestination * localDestination) :
m_LocalDestination (localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, I2P_TUNNEL_DEFAULT_KEY_TYPE))
{
}
void I2PTunnel::AddConnection (std::shared_ptr<I2PTunnelConnection> conn)
{
m_Connections.insert (conn);
@@ -155,13 +166,10 @@ namespace client
m_Connections.clear ();
}
I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination,
int port, ClientDestination * localDestination):
I2PTunnel (service, localDestination ? localDestination :
i2p::client::context.CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256)),
m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (service), m_Destination (destination), m_DestinationIdentHash (nullptr),
m_RemoteLeaseSet (nullptr)
I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination):
I2PTunnel (localDestination),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (GetService ()), m_Destination (destination), m_DestinationIdentHash (nullptr)
{
}
@@ -172,11 +180,7 @@ namespace client
void I2PClientTunnel::Start ()
{
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
if (!m_DestinationIdentHash)
LogPrint ("I2PTunnel unknown destination ", m_Destination);
GetIdentHash();
m_Acceptor.listen ();
Accept ();
}
@@ -186,9 +190,26 @@ namespace client
m_Acceptor.close();
m_Timer.cancel ();
ClearConnections ();
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,"Remote destination ", m_Destination, " not found");
}
return m_DestinationIdentHash;
}
void I2PClientTunnel::Accept ()
{
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
@@ -200,71 +221,43 @@ namespace client
{
if (!ecode)
{
if (!m_DestinationIdentHash)
{
i2p::data::IdentHash identHash;
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
}
if (m_DestinationIdentHash)
{
// try to get a LeaseSet
m_RemoteLeaseSet = GetLocalDestination ()->FindLeaseSet (*m_DestinationIdentHash);
if (m_RemoteLeaseSet && m_RemoteLeaseSet->HasNonExpiredLeases ())
CreateConnection (socket);
else
{
i2p::data::netdb.RequestDestination (*m_DestinationIdentHash, true, GetLocalDestination ()->GetTunnelPool ());
m_Timer.expires_from_now (boost::posix_time::seconds (I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT));
m_Timer.async_wait (std::bind (&I2PClientTunnel::HandleDestinationRequestTimer,
this, std::placeholders::_1, socket));
}
}
const i2p::data::IdentHash *identHash = GetIdentHash();
if (identHash)
GetLocalDestination ()->CreateStream (
std::bind (&I2PClientTunnel::HandleStreamRequestComplete,
this, std::placeholders::_1, socket), *identHash);
else
{
LogPrint ("Remote destination ", m_Destination, " not found");
LogPrint (eLogError,"Closing socket");
delete socket;
}
}
Accept ();
}
else
delete socket;
}
void I2PClientTunnel::HandleDestinationRequestTimer (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_DestinationIdentHash)
{
m_RemoteLeaseSet = GetLocalDestination ()->FindLeaseSet (*m_DestinationIdentHash);
CreateConnection (socket);
return;
}
LogPrint (eLogError,"Closing socket on accept because: ", ecode.message ());
delete socket;
}
delete socket;
}
void I2PClientTunnel::CreateConnection (boost::asio::ip::tcp::socket * socket)
void I2PClientTunnel::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket)
{
if (m_RemoteLeaseSet) // leaseSet found
{
LogPrint ("New I2PTunnel connection");
auto connection = std::make_shared<I2PTunnelConnection>(this, socket, m_RemoteLeaseSet);
if (stream)
{
LogPrint (eLogInfo,"New I2PTunnel connection");
auto connection = std::make_shared<I2PTunnelConnection>(this, socket, stream);
AddConnection (connection);
connection->I2PConnect ();
}
else
{
LogPrint ("LeaseSet for I2PTunnel destination not found");
LogPrint (eLogError,"Issue when creating the stream, check the previous warnings for more info.");
delete socket;
}
}
}
I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
ClientDestination * localDestination): I2PTunnel (service, localDestination),
m_Endpoint (boost::asio::ip::address::from_string (address), port)
I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination):
I2PTunnel (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port)
{
}

View File

@@ -17,6 +17,7 @@ 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
const i2p::data::SigningKeyType I2P_TUNNEL_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
class I2PTunnel;
class I2PTunnelConnection: public std::enable_shared_from_this<I2PTunnelConnection>
@@ -25,6 +26,8 @@ namespace client
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
const i2p::data::LeaseSet * leaseSet); // to I2P
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API :)
I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
~I2PTunnelConnection ();
@@ -58,8 +61,7 @@ namespace client
{
public:
I2PTunnel (boost::asio::io_service& service, ClientDestination * localDestination):
m_Service (service), m_LocalDestination (localDestination) {};
I2PTunnel (ClientDestination * localDestination = nullptr);
virtual ~I2PTunnel () { ClearConnections (); };
void AddConnection (std::shared_ptr<I2PTunnelConnection> conn);
@@ -68,11 +70,10 @@ namespace client
ClientDestination * GetLocalDestination () { return m_LocalDestination; };
void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; };
boost::asio::io_service& GetService () { return m_Service; };
boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); };
private:
boost::asio::io_service& m_Service;
ClientDestination * m_LocalDestination;
std::set<std::shared_ptr<I2PTunnelConnection> > m_Connections;
};
@@ -81,8 +82,7 @@ namespace client
{
public:
I2PClientTunnel (boost::asio::io_service& service, const std::string& destination, int port,
ClientDestination * localDestination = nullptr);
I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination = nullptr);
~I2PClientTunnel ();
void Start ();
@@ -90,10 +90,10 @@ namespace client
private:
const i2p::data::IdentHash * GetIdentHash ();
void Accept ();
void HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
void HandleDestinationRequestTimer (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
void CreateConnection (boost::asio::ip::tcp::socket * socket);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket);
private:
@@ -101,15 +101,13 @@ namespace client
boost::asio::deadline_timer m_Timer;
std::string m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
};
class I2PServerTunnel: public I2PTunnel
{
public:
I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
ClientDestination * localDestination);
I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination);
void Start ();
void Stop ();

View File

@@ -5,6 +5,7 @@
#include <cryptopp/dsa.h>
#include "base64.h"
#include "CryptoConst.h"
#include "ElGamal.h"
#include "RouterContext.h"
#include "Identity.h"
#include "I2PEndian.h"
@@ -101,8 +102,8 @@ namespace data
m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen);
// fill extended buffer
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
*(uint16_t *)m_ExtendedBuffer = htobe16 (type);
*(uint16_t *)(m_ExtendedBuffer + 2) = htobe16 (CRYPTO_KEY_TYPE_ELGAMAL);
htobe16buf (m_ExtendedBuffer, type);
htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL);
if (excessLen && excessBuf)
{
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
@@ -275,14 +276,14 @@ namespace data
SigningKeyType IdentityEx::GetSigningKeyType () const
{
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return be16toh (*(const uint16_t *)m_ExtendedBuffer); // signing key
return bufbe16toh (m_ExtendedBuffer); // signing key
return SIGNING_KEY_TYPE_DSA_SHA1;
}
CryptoKeyType IdentityEx::GetCryptoKeyType () const
{
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
return be16toh (*(const uint16_t *)(m_ExtendedBuffer + 2)); // crypto key
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
return CRYPTO_KEY_TYPE_ELGAMAL;
}
@@ -509,8 +510,7 @@ namespace data
Keys keys;
auto& rnd = i2p::context.GetRandomNumberGenerator ();
// encryption
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(rnd, keys.privateKey, keys.publicKey);
i2p::crypto::GenerateElGamalKeyPair(rnd, keys.privateKey, keys.publicKey);
// signing
i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey);
return keys;

View File

@@ -1,3 +1,4 @@
#include <string.h>
#include "I2PEndian.h"
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
@@ -41,14 +42,15 @@ namespace data
// leases
for (auto it: tunnels)
{
Lease * lease = (Lease *)(m_Buffer + m_BufferLen);
memcpy (lease->tunnelGateway, it->GetNextIdentHash (), 32);
lease->tunnelID = htobe32 (it->GetNextTunnelID ());
Lease lease;
memcpy (lease.tunnelGateway, it->GetNextIdentHash (), 32);
lease.tunnelID = htobe32 (it->GetNextTunnelID ());
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - 60; // 1 minute before expiration
ts *= 1000; // in milliseconds
lease->endDate = htobe64 (ts);
lease.endDate = htobe64 (ts);
memcpy(m_Buffer + m_BufferLen, &lease, sizeof(Lease));
m_BufferLen += sizeof (Lease);
}
}
// signature
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += localDestination->GetIdentity ().GetSignatureLen ();
@@ -79,7 +81,8 @@ namespace data
const uint8_t * leases = m_Buffer + size;
for (int i = 0; i < num; i++)
{
Lease lease = *(Lease *)leases;
Lease lease;
memcpy (&lease, leases, sizeof(Lease));
lease.tunnelID = be32toh (lease.tunnelID);
lease.endDate = be64toh (lease.endDate);
m_Leases.push_back (lease);

View File

@@ -1,6 +1,8 @@
UNAME := $(shell uname -s)
SHLIB := libi2pd.so
I2PD := i2p
GREP := fgrep
DEPS := obj/make.dep
include filelist.mk
@@ -8,45 +10,64 @@ USE_AESNI := yes
USE_STATIC := no
ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp
DAEMON_SRC += DaemonLinux.cpp
include Makefile.osx
else ifeq ($(UNAME),FreeBSD)
DAEMON_SRC += DaemonLinux.cpp
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.bsd
else ifeq ($(UNAME),Linux)
DAEMON_SRC += DaemonLinux.cpp
DAEMON_SRC += DaemonLinux.cpp
include Makefile.linux
else # win32
DAEMON_SRC += DaemonWin32.cpp
DAEMON_SRC += DaemonWin32.cpp
endif
all: obj $(SHLIB) $(I2PD)
all: mk_build_dir $(SHLIB) $(I2PD)
obj:
mkdir -p obj
mk_build_dir:
test -d obj || mkdir obj
obj/%.o : %.cpp %.h
$(CXX) $(CXXFLAGS) $(INCFLAGS) -c -o $@ $<
api: $(SHLIB)
## 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:
@test -d obj || mkdir obj
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
# weaker rule for building files without headers
obj/%.o : %.cpp
$(CXX) $(CXXFLAGS) $(INCFLAGS) -c -o $@ $<
@test -d obj || mkdir 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))
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS)
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS) -shared
ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
endif
clean:
rm -fr obj $(I2PD) $(SHLIB)
rm -rf obj
$(RM) $(I2PD) $(SHLIB)
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: mk_build_dir

View File

@@ -1,5 +1,12 @@
CXX = g++
CXXFLAGS = -g -Wall -O2 -std=c++11
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
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread

View File

@@ -1,18 +1,25 @@
CXXFLAGS = -g -Wall -fPIC
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 gcc
CXXVER := $(shell $(CXX) -dumpversion)
ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # >= 4.10
CXXFLAGS += -std=c++11
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
CXXFLAGS += -std=c++11
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
CXXFLAGS += -std=c++0x
NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match $(CXX) 'clang'),5)
CXXFLAGS += -std=c++11
NEEDED_CXXFLAGS += -std=c++11
else # not supported
$(error Compiler too old)
$(error Compiler too old)
endif
ifeq ($(USE_STATIC),yes)
@@ -29,15 +36,18 @@ else
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
endif
GREP = fgrep
# 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)
CXXFLAGS += -maes -DAESNI
else
$(warning "AESNI support enabled requested but not supported by this CPU")
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
CPU_FLAGS = -maes -DAESNI
endif
endif
endif

View File

@@ -1,9 +1,15 @@
CXX = clang++
CXXFLAGS = -g -Wall -std=c++11 -lstdc++ -DCRYPTOPP_DISABLE_ASM
CXXFLAGS = -g -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM -DMAC_OSX
#CXXFLAGS = -g -O2 -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM
INCFLAGS = -I/usr/local/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
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
@@ -12,6 +18,8 @@ ifeq ($(USE_AESNI),yes)
CXXFLAGS += -maes -DAESNI
endif
install: all
mkdir -p ${PREFIX}/
cp -r i2p ${PREFIX}/
# 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

@@ -255,11 +255,10 @@ namespace transport
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
// verify
uint8_t xy[512], hxy[32];
uint8_t xy[512];
memcpy (xy, m_DHKeysPair->publicKey, 256);
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
CryptoPP::SHA256().CalculateDigest(hxy, xy, 512);
if (memcmp (hxy, m_Establisher->phase2.encrypted.hxy, 32))
if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512))
{
LogPrint (eLogError, "Incorrect hash");
transports.ReuseDHKeysPair (m_DHKeysPair);
@@ -275,11 +274,11 @@ namespace transport
{
auto keys = i2p::context.GetPrivateKeys ();
uint8_t * buf = m_ReceiveBuffer;
*(uint16_t *)buf = htobe16 (keys.GetPublic ().GetFullLen ());
htobe16buf (buf, keys.GetPublic ().GetFullLen ());
buf += 2;
buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE);
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
*(uint32_t *)buf = tsA;
htobuf32(buf,tsA);
buf += 4;
size_t signatureLen = keys.GetPublic ().GetSignatureLen ();
size_t len = (buf - m_ReceiveBuffer) + signatureLen;
@@ -339,7 +338,7 @@ namespace transport
LogPrint (eLogDebug, "Phase 3 received: ", bytes_transferred);
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
uint8_t * buf = m_ReceiveBuffer;
uint16_t size = be16toh (*(uint16_t *)buf);
uint16_t size = bufbe16toh (buf);
m_RemoteIdentity.FromBuffer (buf + 2, size);
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen ();
size_t paddingLen = expectedSize & 0x0F;
@@ -377,7 +376,7 @@ namespace transport
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
{
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/;
uint32_t tsA = *(uint32_t *)buf;
uint32_t tsA = buf32toh(buf);
buf += 4;
buf += paddingLen;
@@ -527,7 +526,7 @@ namespace transport
m_NextMessageOffset = 0;
m_Decryption.Decrypt (encrypted, m_NextMessage->buf);
uint16_t dataSize = be16toh (*(uint16_t *)m_NextMessage->buf);
uint16_t dataSize = bufbe16toh (m_NextMessage->buf);
if (dataSize)
{
// new message
@@ -581,15 +580,15 @@ namespace transport
}
sendBuffer = msg->GetBuffer () - 2;
len = msg->GetLength ();
*((uint16_t *)sendBuffer) = htobe16 (len);
htobe16buf (sendBuffer, len);
}
else
{
// prepare timestamp
sendBuffer = m_TimeSyncBuffer;
len = 4;
*((uint16_t *)sendBuffer) = 0;
*((uint32_t *)(sendBuffer + 2)) = htobe32 (time (0));
htobuf16(sendBuffer, 0);
htobe32buf (sendBuffer + 2, time (0));
}
int rem = (len + 6) & 0x0F; // %16
int padding = 0;

193
NetDb.cpp
View File

@@ -1,3 +1,4 @@
#include <string.h>
#include "I2PEndian.h"
#include <fstream>
#include <vector>
@@ -24,16 +25,9 @@ namespace data
I2NPMessage * RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
const i2p::tunnel::InboundTunnel * replyTunnel)
{
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
&m_ExcludedPeers, m_IsLeaseSet, m_Pool);
if (m_IsLeaseSet) // wrap lookup message into garlic
{
if (m_Pool && m_Pool->GetLocalDestination ())
msg = m_Pool->GetLocalDestination ()->WrapMessage (*router, msg);
else
LogPrint ("Can't create garlic message without destination");
}
&m_ExcludedPeers);
m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRouter = router;
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
@@ -42,7 +36,7 @@ namespace data
I2NPMessage * RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
{
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill);
m_LastRouter = nullptr;
@@ -62,7 +56,7 @@ namespace data
#endif
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (0)
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr)
{
}
@@ -78,14 +72,14 @@ namespace data
void NetDb::Start ()
{
Load (m_NetDbPath);
if (m_RouterInfos.size () < 100) // reseed if # of router less than 100
if (m_RouterInfos.size () < 50) // reseed if # of router less than 50
{
Reseeder reseeder;
reseeder.LoadCertificates (); // we need certificates for SU3 verification
// try SU3 first
int reseedRetries = 0;
while (m_RouterInfos.size () < 100 && reseedRetries < 10)
while (m_RouterInfos.size () < 50 && reseedRetries < 10)
{
reseeder.ReseedNowSU3();
reseedRetries++;
@@ -93,7 +87,7 @@ namespace data
// if still not enough download .dat files
reseedRetries = 0;
while (m_RouterInfos.size () < 100 && reseedRetries < 10)
while (m_RouterInfos.size () < 50 && reseedRetries < 10)
{
reseeder.reseedNow();
reseedRetries++;
@@ -128,7 +122,7 @@ namespace data
{
while (msg)
{
switch (msg->GetHeader ()->typeID)
switch (msg->GetTypeID ())
{
case eI2NPDatabaseStore:
LogPrint ("DatabaseStore");
@@ -143,7 +137,7 @@ namespace data
HandleDatabaseLookupMsg (msg);
break;
default: // WTF?
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
LogPrint ("NetDb: unexpected message type ", msg->GetTypeID ());
i2p::HandleI2NPMessage (msg);
}
msg = m_Queue.Get ();
@@ -153,6 +147,7 @@ namespace data
{
if (!m_IsRunning) break;
// if no new DatabaseStore coming, explore it
ManageRequests ();
auto numRouters = m_RouterInfos.size ();
Explore (numRouters < 1500 ? 5 : 1);
}
@@ -412,66 +407,37 @@ namespace data
}
}
void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet, i2p::tunnel::TunnelPool * pool)
void NetDb::RequestDestination (const IdentHash& destination)
{
if (isLeaseSet) // we request LeaseSet through tunnels
{
i2p::tunnel::OutboundTunnel * outbound = pool ? pool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
if (outbound)
{
i2p::tunnel::InboundTunnel * inbound = pool ? pool->GetNextInboundTunnel () :i2p::tunnel::tunnels.GetNextInboundTunnel ();
if (inbound)
{
RequestedDestination * dest = CreateRequestedDestination (destination, true, false, pool);
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
{
// DatabaseLookup message
outbound->SendTunnelDataMsg (
{
i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound)
}
});
}
else
LogPrint ("No more floodfills found");
}
else
LogPrint ("No inbound tunnels found");
}
else
LogPrint ("No outbound tunnels found");
}
else // RouterInfo is requested directly
// request RouterInfo directly
RequestedDestination * dest = CreateRequestedDestination (destination, false);
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
else
{
RequestedDestination * dest = CreateRequestedDestination (destination, false, false, pool);
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
LogPrint (eLogError, "No floodfills found");
DeleteRequestedDestination (dest);
}
}
void NetDb::HandleDatabaseStoreMsg (I2NPMessage * m)
{
const uint8_t * buf = m->GetPayload ();
size_t len = be16toh (m->GetHeader ()->size);
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
size_t offset = sizeof (I2NPDatabaseStoreMsg);
if (msg->replyToken)
size_t len = m->GetSize ();
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE;
if (replyToken)
offset += 36;
if (msg->type)
if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
{
LogPrint ("LeaseSet");
AddLeaseSet (msg->key, buf + offset, len - offset, m->from);
AddLeaseSet (buf + DATABASE_STORE_KEY_OFFSET, buf + offset, len - offset, m->from);
}
else
{
LogPrint ("RouterInfo");
size_t size = be16toh (*(uint16_t *)(buf + offset));
size_t size = bufbe16toh (buf + offset);
if (size > 2048)
{
LogPrint ("Invalid RouterInfo length ", (int)size);
@@ -484,7 +450,7 @@ namespace data
uint8_t uncompressed[2048];
size_t uncomressedSize = decompressor.MaxRetrievable ();
decompressor.Get (uncompressed, uncomressedSize);
AddRouterInfo (msg->key, uncompressed, uncomressedSize);
AddRouterInfo (buf + DATABASE_STORE_KEY_OFFSET, uncompressed, uncomressedSize);
}
i2p::DeleteI2NPMessage (m);
}
@@ -504,9 +470,9 @@ namespace data
bool deleteDest = true;
if (num > 0)
{
auto pool = dest ? dest->GetTunnelPool () : nullptr;
auto outbound = pool ? pool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
auto inbound = pool ? pool->GetNextInboundTunnel () : i2p::tunnel::tunnels.GetNextInboundTunnel ();
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool->GetNextInboundTunnel ();
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
if (!dest->IsExploratory ())
{
@@ -518,17 +484,14 @@ namespace data
{
auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
if (nextFloodfill)
{
if (!dest->IsLeaseSet ())
{
// tell floodfill about us
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg ()
});
}
{
// tell floodfill about us
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg ()
});
// request destination
LogPrint ("Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ());
@@ -563,7 +526,7 @@ namespace data
LogPrint ("Found new/outdated router. Requesting RouterInfo ...");
if (outbound && inbound && dest->GetLastRouter ())
{
RequestedDestination * d1 = CreateRequestedDestination (router, false, false, pool);
RequestedDestination * d1 = CreateRequestedDestination (router, false);
auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), inbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
@@ -572,7 +535,7 @@ namespace data
});
}
else
RequestDestination (router, false, pool);
RequestDestination (router);
}
else
LogPrint ("Bayan");
@@ -585,7 +548,7 @@ namespace data
{
// request router
LogPrint ("Found new floodfill. Request it");
RequestDestination (router, false, pool);
RequestDestination (router);
}
}
}
@@ -635,10 +598,10 @@ namespace data
uint32_t replyTunnelID = 0;
if (flag & 0x01) //reply to tunnel
{
replyTunnelID = be32toh (*(uint32_t *)(buf + 64));
replyTunnelID = bufbe32toh (buf + 64);
excluded += 4;
}
uint16_t numExcluded = be16toh (*(uint16_t *)excluded);
uint16_t numExcluded = bufbe16toh (excluded);
excluded += 2;
if (numExcluded > 512)
{
@@ -713,18 +676,6 @@ namespace data
void NetDb::Explore (int numDestinations)
{
// clean up previous exploratories
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{
if (it->second->IsExploratory () || ts > it->second->GetCreationTime () + 60) // no response for 1 minute
{
delete it->second;
it = m_RequestedDestinations.erase (it);
}
else
it++;
}
// new requests
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
@@ -739,7 +690,7 @@ namespace data
for (int i = 0; i < numDestinations; i++)
{
rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true, exploratoryPool);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), true);
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
{
@@ -784,14 +735,13 @@ namespace data
}
}
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory, i2p::tunnel::TunnelPool * pool)
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest, bool isExploratory)
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (dest);
if (it == m_RequestedDestinations.end ()) // not exist yet
{
RequestedDestination * d = new RequestedDestination (dest, isLeaseSet, isExploratory, pool);
RequestedDestination * d = new RequestedDestination (dest, isExploratory);
m_RequestedDestinations[dest] = d;
return d;
}
@@ -918,5 +868,54 @@ namespace data
it++;
}
}
void NetDb::ManageRequests ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{
auto dest = it->second;
bool done = false;
if (!dest->IsExploratory () && 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 (count < 7)
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool->GetNextInboundTunnel ();
auto nextFloodfill = 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
{
LogPrint (eLogWarning, dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
done = true;
}
}
}
else // delete previous exploratory
done = true;
if (done)
{
delete it->second;
it = m_RequestedDestinations.erase (it);
}
else
it++;
}
}
}
}

20
NetDb.h
View File

@@ -24,19 +24,15 @@ namespace data
{
public:
RequestedDestination (const IdentHash& destination, bool isLeaseSet,
bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr):
m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory),
m_Pool (pool), m_CreationTime (0) {};
RequestedDestination (const IdentHash& destination, bool isExploratory = false):
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers ();
std::shared_ptr<const RouterInfo> GetLastRouter () const { return m_LastRouter; };
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
bool IsExploratory () const { return m_IsExploratory; };
bool IsLeaseSet () const { return m_IsLeaseSet; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; };
I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, const i2p::tunnel::InboundTunnel * replyTunnel);
@@ -45,8 +41,7 @@ namespace data
private:
IdentHash m_Destination;
bool m_IsLeaseSet, m_IsExploratory;
i2p::tunnel::TunnelPool * m_Pool;
bool m_IsExploratory;
std::set<IdentHash> m_ExcludedPeers;
std::shared_ptr<const RouterInfo> m_LastRouter;
uint64_t m_CreationTime;
@@ -68,8 +63,7 @@ namespace data
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false,
i2p::tunnel::TunnelPool * pool = nullptr);
void RequestDestination (const IdentHash& destination);
void HandleDatabaseStoreMsg (I2NPMessage * msg);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
@@ -94,12 +88,12 @@ namespace data
void Load (const char * directory);
void SaveUpdated (const char * directory);
void Run (); // exploratory thread
void Explore (int numDestinations);
void Explore (int numDestinations);
void Publish ();
void ManageLeaseSets ();
void ManageRequests ();
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
bool isLeaseSet, bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr);
RequestedDestination * CreateRequestedDestination (const IdentHash& dest, bool isExploratory = false);
bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found
void DeleteRequestedDestination (RequestedDestination * dest);

View File

@@ -21,7 +21,7 @@ Build Statuses
- Linux x64 - [![Build Status](https://jenkins.nordcloud.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/)
- Linux ARM - To be added
- Mac OS X - To be added
- Mac OS X - Got it working, but not well tested. (Only works with clang, not GCC.)
- Microsoft VC13 - To be added

View File

@@ -372,7 +372,7 @@ namespace data
else
nextInd = 0;
}
return s;
return false;
}
const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----";

72
SAM.cpp
View File

@@ -7,7 +7,6 @@
#include "base64.h"
#include "Identity.h"
#include "Log.h"
#include "NetDb.h"
#include "Destination.h"
#include "ClientContext.h"
#include "SAM.h"
@@ -326,9 +325,8 @@ namespace client
Connect (*leaseSet);
else
{
i2p::data::netdb.RequestDestination (dest.GetIdentHash (), true, m_Session->localDestination->GetTunnelPool ());
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_CONNECT_TIMEOUT));
m_Timer.async_wait (std::bind (&SAMSocket::HandleStreamDestinationRequestTimer,
m_Session->localDestination->RequestDestination (dest.GetIdentHash (),
std::bind (&SAMSocket::HandleLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, dest.GetIdentHash ()));
}
}
@@ -346,18 +344,17 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
void SAMSocket::HandleStreamDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident)
void SAMSocket::HandleLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
{
if (!ecode) // timeout expired
const i2p::data::LeaseSet * leaseSet = nullptr;
if (success) // timeout expired
leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
Connect (*leaseSet);
else
{
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
if (leaseSet)
Connect (*leaseSet);
else
{
LogPrint ("SAM destination to connect not found");
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
}
LogPrint ("SAM destination to connect not found");
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
}
}
@@ -568,6 +565,28 @@ namespace client
LogPrint (eLogWarning, "Datagram size ", len," exceeds buffer");
}
SAMSession::SAMSession (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 (int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
@@ -577,7 +596,8 @@ namespace client
SAMBridge::~SAMBridge ()
{
Stop ();
if (m_IsRunning)
Stop ();
}
void SAMBridge::Start ()
@@ -591,6 +611,10 @@ namespace client
void SAMBridge::Stop ()
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto it: m_Sessions)
delete it.second;
m_Sessions.clear ();
m_Service.stop ();
if (m_Thread)
{
@@ -661,13 +685,11 @@ namespace client
}
if (localDestination)
{
SAMSession session;
session.localDestination = localDestination;
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession>(id, session));
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession *>(id, new SAMSession (localDestination)));
if (!ret.second)
LogPrint ("Session ", id, " already exists");
return &(ret.first->second);
return ret.first->second;
}
return nullptr;
}
@@ -678,11 +700,10 @@ namespace client
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
{
for (auto it1: it->second.sockets)
it1->CloseStream ();
it->second.sockets.clear ();
i2p::client::context.DeleteLocalDestination (it->second.localDestination);
auto session = it->second;
session->CloseStreams ();
m_Sessions.erase (it);
delete session;
}
}
@@ -691,7 +712,7 @@ namespace client
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
return &it->second;
return it->second;
return nullptr;
}
@@ -732,8 +753,7 @@ namespace client
else
{
LogPrint ("SAM datagram destination not found");
i2p::data::netdb.RequestDestination (dest.GetIdentHash (), true,
session->localDestination->GetTunnelPool ());
session->localDestination->RequestDestination (dest.GetIdentHash ());
}
}
else

21
SAM.h
View File

@@ -19,9 +19,7 @@ namespace i2p
namespace client
{
const size_t SAM_SOCKET_BUFFER_SIZE = 4096;
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
const int SAM_CONNECT_TIMEOUT = 5; // in seconds
const int SAM_NAMING_LOOKUP_TIMEOUT = 5; // in seconds
const 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";
@@ -69,7 +67,7 @@ namespace client
};
class SAMBridge;
class SAMSession;
struct SAMSession;
class SAMSocket: public std::enable_shared_from_this<SAMSocket>
{
public:
@@ -107,7 +105,7 @@ namespace client
void ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params);
void Connect (const i2p::data::LeaseSet& remote);
void HandleStreamDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident);
void HandleLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
void SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet);
void SendNamingLookupReply (const i2p::data::IdentityEx& identity);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
@@ -131,12 +129,11 @@ namespace client
{
ClientDestination * localDestination;
std::list<std::shared_ptr<SAMSocket> > sockets;
~SAMSession ()
{
for (auto it: sockets)
it->SetSocketType (eSAMSocketTypeTerminated);
}
SAMSession (ClientDestination * localDestination);
~SAMSession ();
void CloseStreams ();
};
class SAMBridge
@@ -174,7 +171,7 @@ namespace client
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_DatagramSocket;
std::mutex m_SessionsMutex;
std::map<std::string, SAMSession> m_Sessions;
std::map<std::string, SAMSession *> m_Sessions;
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
};
}

632
SOCKS.cpp
View File

@@ -3,281 +3,447 @@
#include "NetDb.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include <cstring>
#include <stdexcept>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <cassert>
namespace i2p
{
namespace proxy
{
const uint8_t socks_leaseset_timeout = 10;
const uint8_t socks_timeout = 60;
void SOCKS4AHandler::AsyncSockRead()
void SOCKSHandler::AsyncSockRead()
{
LogPrint("--- socks4a async sock read");
LogPrint(eLogDebug,"--- SOCKS async sock read");
if(m_sock) {
if (m_state == INITIAL) {
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
boost::bind(&SOCKS4AHandler::HandleSockRecv, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else {
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
boost::bind(&SOCKS4AHandler::HandleSockForward, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
std::bind(&SOCKSHandler::HandleSockRecv, this,
std::placeholders::_1, std::placeholders::_2));
} else {
LogPrint("--- socks4a no socket for read");
}
}
void SOCKS4AHandler::AsyncStreamRead()
{
LogPrint("--- socks4a async stream read");
if (m_stream) {
m_stream->AsyncReceive(
boost::asio::buffer(m_stream_buff, socks_buffer_size),
boost::bind(&SOCKS4AHandler::HandleStreamRecv, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred), socks_timeout);
} else {
LogPrint("--- socks4a no stream for read");
LogPrint(eLogError,"--- SOCKS no socket for read");
}
}
void SOCKS4AHandler::Terminate() {
CloseStream();
CloseSock();
delete this; // ew
}
void SOCKS4AHandler::SocksFailed()
{
LogPrint("--- socks4a failed");
m_sock->send(boost::asio::buffer("\x00\x5b 12345"));
Terminate();
void SOCKSHandler::Done() {
if (m_parent) m_parent->RemoveHandler (shared_from_this ());
}
void SOCKS4AHandler::CloseSock()
{
void SOCKSHandler::Terminate() {
if (dead.exchange(true)) return;
if (m_sock) {
LogPrint("--- socks4a close sock");
LogPrint(eLogDebug,"--- SOCKS close sock");
m_sock->close();
delete m_sock;
m_sock = nullptr;
}
}
void SOCKS4AHandler::CloseStream()
{
if (m_stream) {
LogPrint("--- socks4a close stream");
LogPrint(eLogDebug,"--- SOCKS close stream");
m_stream.reset ();
}
Done();
}
const size_t socks_hostname_size = 1024;
const size_t socks_ident_size = 1024;
const size_t destb32_len = 52;
void SOCKS4AHandler::HandleSockForward(const boost::system::error_code & ecode, std::size_t len)
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
{
if(ecode) {
LogPrint("--- socks4a forward got error: ", ecode);
Terminate();
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;
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, 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, 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, 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(m_parent->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, 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;
}
void 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;
}
//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;
}
//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;
}
LogPrint("--- socks4a sock forward: ", len);
m_stream->Send(m_sock_buff, len);
}
void SOCKS4AHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
{
LogPrint("--- socks4a sock recv: ", 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 (len && m_state == DONE) {
LogPrint(eLogError,"--- SOCKS rejected because we can't handle extra data");
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
}
return true;
}
void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
LogPrint(eLogDebug,"--- SOCKS sock recv: ", len);
if(ecode) {
LogPrint(" --- sock recv got error: ", ecode);
LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode);
Terminate();
return;
}
if (m_state == INITIAL) {
char hostbuff[socks_hostname_size];
char identbuff[socks_ident_size];
std::memset(hostbuff, 0, sizeof(hostbuff));
std::memset(identbuff, 0, sizeof(hostbuff));
std::string dest;
// get port
uint16_t port = 0;
uint16_t idx1 = 0;
uint16_t idx2 = 0;
LogPrint("--- socks4a state initial ", len);
// check valid request
if( m_sock_buff[0] != 4 || m_sock_buff[1] != 1 || m_sock_buff[len-1] ) {
LogPrint("--- socks4a rejected invalid");
SocksFailed();
return;
}
// get port
port = m_sock_buff[3] | m_sock_buff[2] << 8;
// read ident
do {
LogPrint("--- socks4a ", (int) m_sock_buff[9+idx1]);
identbuff[idx1] = m_sock_buff[8+idx1];
} while( identbuff[idx1++] && idx1 < socks_ident_size );
LogPrint("--- socks4a ident ", identbuff);
// read hostname
do {
hostbuff[idx2] = m_sock_buff[8+idx1+idx2];
} while( hostbuff[idx2++] && idx2 < socks_hostname_size );
LogPrint("--- socks4a requested ", hostbuff, ":" , port);
dest = std::string(hostbuff);
if(dest.find(".b32.i2p") == std::string::npos) {
LogPrint("--- socks4a invalid hostname: ", dest);
SocksFailed();
return;
}
if ( i2p::data::Base32ToByteStream(hostbuff, destb32_len, (uint8_t *) m_dest, 32) != 32 ) {
LogPrint("--- sock4a invalid b32: ", dest);
}
LogPrint("--- sock4a find lease set");
m_ls = i2p::data::netdb.FindLeaseSet(m_dest);
if (!m_ls || m_ls->HasNonExpiredLeases()) {
i2p::data::netdb.RequestDestination (m_dest, true, i2p::client::context.GetSharedLocalDestination ()->GetTunnelPool ());
m_ls_timer.expires_from_now(boost::posix_time::seconds(socks_leaseset_timeout));
m_ls_timer.async_wait(boost::bind(&SOCKS4AHandler::LeaseSetTimeout, this, boost::asio::placeholders::error));
if (HandleData(m_sock_buff, len)) {
if (m_state == DONE) {
LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
m_parent->GetLocalDestination ()->CreateStream (
std::bind (&SOCKSHandler::HandleStreamRequestComplete,
this, std::placeholders::_1), m_address.dns.ToString(), m_port);
} else {
ConnectionSuccess();
}
} else {
LogPrint("--- socks4a state?? ", m_state);
}
}
void SOCKS4AHandler::HandleStreamRecv(const boost::system::error_code & ecode, std::size_t len)
{
if(ecode) { LogPrint("--- socks4a stream recv error: ", ecode); m_state = END; }
switch(m_state) {
case INITIAL:
case END:
Terminate();
return;
case OKAY:
LogPrint("--- socks4a stream recv ", len);
boost::asio::async_write(*m_sock, boost::asio::buffer(m_stream_buff, len),
boost::bind(&SOCKS4AHandler::StreamWrote, this,
boost::asio::placeholders::error));
}
}
void SOCKS4AHandler::SockWrote(const boost::system::error_code & ecode)
{
LogPrint("--- socks4a sock wrote");
if(ecode) { LogPrint("--- socks4a SockWrote error: ",ecode); }
else { AsyncSockRead(); }
}
void SOCKS4AHandler::StreamWrote(const boost::system::error_code & ecode)
{
LogPrint("--- socks4a stream wrote");
if(ecode) { LogPrint("--- socks4a StreamWrote error: ",ecode); }
else { AsyncStreamRead(); }
}
void SOCKS4AHandler::LeaseSetTimeout(const boost::system::error_code & ecode)
{
m_ls = i2p::data::netdb.FindLeaseSet(m_dest);
if(m_ls) {
ConnectionSuccess();
} else {
LogPrint("--- socks4a ls timeout");
SocksFailed();
}
}
void SOCKS4AHandler::ConnectionSuccess()
{
LogPrint("--- socks4a connection success");
boost::asio::async_write(*m_sock, boost::asio::buffer("\x00\x5a 12345"),
boost::bind(&SOCKS4AHandler::SentConnectionSuccess, this,
boost::asio::placeholders::error));
}
void SOCKS4AHandler::SentConnectionSuccess(const boost::system::error_code & ecode)
{
LogPrint("--- socks4a making connection");
m_stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream(*m_ls);
m_state = OKAY;
LogPrint("--- socks4a state is ", m_state);
AsyncSockRead();
AsyncStreamRead();
}
void SOCKS4AServer::Run()
{
LogPrint("--- socks4a run");
m_run = true;
while(m_run) {
try {
m_ios.run();
} catch (std::runtime_error & exc) {
LogPrint("--- socks4a exception: ", exc.what());
AsyncSockRead();
}
}
}
void SOCKS4AServer::Accept()
{
m_new_sock = new boost::asio::ip::tcp::socket(m_ios);
m_acceptor.async_accept(*m_new_sock,
boost::bind(
&SOCKS4AServer::HandleAccept, this, boost::asio::placeholders::error));
}
void SOCKS4AServer::Start()
{
m_run = true;
m_thread = new std::thread(std::bind(&SOCKS4AServer::Run, this));
m_acceptor.listen();
Accept();
}
void SOCKS4AServer::Stop()
{
m_acceptor.close();
m_run = false;
m_ios.stop();
if (m_thread) {
m_thread->join();
delete m_thread;
m_thread = nullptr;
}
}
void SOCKS4AServer::HandleAccept(const boost::system::error_code & ecode)
void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
{
if (!ecode) {
LogPrint("--- socks4a accepted");
new SOCKS4AHandler(&m_ios, m_new_sock);
Accept();
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 (dead.exchange(true)) return;
LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>((i2p::client::I2PTunnel *)m_parent, m_sock, m_stream);
m_parent->AddConnection (connection);
connection->I2PConnect ();
Done();
}
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);
}
}
void SOCKSServer::Start ()
{
m_Acceptor.listen ();
Accept ();
}
void SOCKSServer::Stop ()
{
m_Acceptor.close();
m_Timer.cancel ();
ClearConnections ();
ClearHandlers();
}
void SOCKSServer::Accept ()
{
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&SOCKSServer::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void SOCKSServer::AddHandler (std::shared_ptr<SOCKSHandler> handler) {
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.insert (handler);
}
void SOCKSServer::RemoveHandler (std::shared_ptr<SOCKSHandler> handler)
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.erase (handler);
}
void SOCKSServer::ClearHandlers ()
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.clear ();
}
void SOCKSServer::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (!ecode)
{
LogPrint(eLogDebug,"--- SOCKS accepted");
AddHandler(std::make_shared<SOCKSHandler> (this, socket));
Accept();
}
else
{
LogPrint (eLogError,"--- SOCKS Closing socket on accept because: ", ecode.message ());
delete socket;
}
}
}
}

214
SOCKS.h
View File

@@ -1,13 +1,15 @@
#ifndef SOCKS4A_H__
#define SOCKS4A_H__
#ifndef SOCKS_H__
#define SOCKS_H__
#include <thread>
#include <boost/asio.hpp>
#include <vector>
#include <mutex>
#include <memory>
#include <string>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include <atomic>
#include "Identity.h"
#include "Streaming.h"
#include "I2PTunnel.h"
namespace i2p
{
@@ -15,83 +17,151 @@ namespace proxy
{
const size_t socks_buffer_size = 8192;
const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
class SOCKS4AHandler {
private:
enum state {
INITIAL,
OKAY,
END
};
void GotClientRequest(boost::system::error_code & ecode, std::string & host, uint16_t port);
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleSockForward(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void HandleStreamRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void CloseSock();
void CloseStream();
void AsyncSockRead();
void AsyncStreamRead();
void SocksFailed();
void LeaseSetTimeout(const boost::system::error_code & ecode);
void StreamWrote(const boost::system::error_code & ecode);
void SockWrote(const boost::system::error_code & ecode);
void SentConnectionSuccess(const boost::system::error_code & ecode);
void ConnectionSuccess();
uint8_t m_sock_buff[socks_buffer_size];
uint8_t m_stream_buff[socks_buffer_size];
boost::asio::io_service * m_ios;
boost::asio::ip::tcp::socket * m_sock;
boost::asio::deadline_timer m_ls_timer;
std::shared_ptr<i2p::stream::Stream> m_stream;
i2p::data::LeaseSet * m_ls;
i2p::data::IdentHash m_dest;
state m_state;
public:
SOCKS4AHandler(boost::asio::io_service * ios, boost::asio::ip::tcp::socket * sock) :
m_ios(ios), m_sock(sock), m_ls_timer(*ios),
m_stream(nullptr), m_ls(nullptr), m_state(INITIAL) { AsyncSockRead(); }
~SOCKS4AHandler() { CloseSock(); CloseStream(); }
bool isComplete() { return m_state == END; }
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 SOCKS4AServer {
class SOCKSServer;
class SOCKSHandler: 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);
void ValidateSOCKSRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Done();
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];
SOCKSServer * m_parent;
boost::asio::ip::tcp::socket * m_sock;
std::shared_ptr<i2p::stream::Stream> m_stream;
uint8_t m_response[7+max_socks_hostname_size];
address m_address; //Address
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;
std::atomic<bool> dead; //To avoid cleaning up multiple times
public:
SOCKS4AServer(int port) : m_run(false),
m_thread(nullptr),
m_work(m_ios),
m_acceptor(m_ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
m_new_sock(nullptr) { }
~SOCKS4AServer() { Stop(); }
void Start();
void Stop();
boost::asio::io_service& GetService () { return m_ios; };
SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
m_parent(parent), m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), dead(false)
{ m_address.ip = 0; AsyncSockRead(); EnterState(GET_SOCKSV); }
~SOCKSHandler() { Terminate(); }
};
class SOCKSServer: public i2p::client::I2PTunnel
{
private:
std::set<std::shared_ptr<SOCKSHandler> > m_Handlers;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
std::mutex m_HandlersMutex;
private:
void Run();
void Accept();
void HandleAccept(const boost::system::error_code& ecode);
void HandleAccept(const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
bool m_run;
std::thread * m_thread;
boost::asio::io_service m_ios;
boost::asio::io_service::work m_work;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket * m_new_sock;
public:
SOCKSServer(int port) : I2PTunnel(nullptr),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
m_Timer (GetService ()) {};
~SOCKSServer() { Stop(); }
void Start ();
void Stop ();
void AddHandler (std::shared_ptr<SOCKSHandler> handler);
void RemoveHandler (std::shared_ptr<SOCKSHandler> handler);
void ClearHandlers ();
};
typedef SOCKS4AServer SOCKSProxy;
typedef SOCKSServer SOCKSProxy;
}
}

View File

@@ -84,7 +84,7 @@ namespace transport
uint8_t numAcks =*buf;
buf++;
for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (be32toh (((uint32_t *)buf)[i]));
ProcessSentMessageAck (bufbe32toh (buf+i*4));
buf += numAcks*4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
@@ -94,7 +94,7 @@ namespace transport
buf++;
for (int i = 0; i < numBitfields; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf);
uint32_t msgID = bufbe32toh (buf);
buf += 4; // msgID
auto it = m_SentMessages.find (msgID);
// process individual Ack bitfields
@@ -137,13 +137,13 @@ namespace transport
buf++;
for (int i = 0; i < numFragments; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf); // message ID
uint32_t msgID = bufbe32toh (buf); // message ID
buf += 4;
uint8_t frag[4];
frag[0] = 0;
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = be32toh (*(uint32_t *)frag); // fragment info
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
@@ -168,7 +168,7 @@ namespace transport
{
// create new message
msg = NewI2NPMessage ();
msg->len -= sizeof (I2NPHeaderShort);
msg->len -= I2NP_SHORT_HEADER_SIZE;
incompleteMessage = new IncompleteMessage (msg);
m_IncomleteMessages[msgID] = incompleteMessage;
}
@@ -246,13 +246,13 @@ namespace transport
else
{
// we expect DeliveryStatus
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
if (msg->GetTypeID () == eI2NPDeliveryStatus)
{
LogPrint ("SSU session established");
m_Session.Established ();
}
else
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetHeader ()->typeID);
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ());
DeleteI2NPMessage (msg);
}
}

View File

@@ -121,6 +121,7 @@ namespace transport
void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
SSUHeader * header = (SSUHeader *)buf;
switch (header->GetPayloadType ())
{
@@ -210,7 +211,7 @@ namespace transport
}
s.Insert (ourAddress, addressSize); // our IP
payload += addressSize; // address
uint16_t ourPort = be16toh (*(uint16_t *)payload);
uint16_t ourPort = bufbe16toh (payload);
s.Insert (payload, 2); // our port
payload += 2; // port
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
@@ -221,13 +222,14 @@ namespace transport
s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP v6
s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port
s.Insert (payload, 8); // relayTag and signed on time
m_RelayTag = be32toh (*(uint32_t *)payload);
m_RelayTag = bufbe32toh (payload);
payload += 4; // relayTag
payload += 4; // signed on time
// decrypt signature
size_t signatureLen = m_RemoteIdentity.GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize);
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv);
m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload);
// verify
@@ -242,7 +244,7 @@ namespace transport
LogPrint (eLogDebug, "Session confirmed received");
uint8_t * payload = buf + sizeof (SSUHeader);
payload++; // identity fragment info
uint16_t identitySize = be16toh (*(uint16_t *)payload);
uint16_t identitySize = bufbe16toh (payload);
payload += 2; // size of identity fragment
m_RemoteIdentity.FromBuffer (payload, identitySize);
m_Data.UpdatePacketSize (m_RemoteIdentity.GetIdentHash ());
@@ -299,18 +301,18 @@ namespace transport
uint8_t buf[96 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
*(uint32_t *)payload = htobe32 (iTag);
htobe32buf (payload, iTag);
payload += 4;
*payload = 0; // no address
payload++;
*(uint16_t *)payload = 0; // port = 0
htobuf16(payload, 0); // port = 0
payload += 2;
*payload = 0; // challenge
payload++;
memcpy (payload, (const uint8_t *)address->key, 32);
payload += 32;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
*(uint32_t *)payload = htobe32 (rnd.GenerateWord32 ()); // nonce
htobe32buf (payload, rnd.GenerateWord32 ()); // nonce
uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv
@@ -358,7 +360,7 @@ namespace transport
s.Insert (payload, 16); // remote endpoint IP V6
payload += 16;
}
*(uint16_t *)(payload) = htobe16 (m_RemoteEndpoint.port ());
htobe16buf (payload, m_RemoteEndpoint.port ());
s.Insert (payload, 2); // remote port
payload += 2;
if (address->host.is_v4 ())
@@ -373,9 +375,9 @@ namespace transport
if (!relayTag) relayTag = 1;
m_Server.AddRelay (relayTag, m_RemoteEndpoint);
}
*(uint32_t *)(payload) = htobe32 (relayTag);
htobe32buf (payload, relayTag);
payload += 4; // relay tag
*(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time
htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time
payload += 4;
s.Insert (payload - 8, 8); // relayTag and signed on time
s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature
@@ -404,12 +406,12 @@ namespace transport
*payload = 1; // 1 fragment
payload++; // info
size_t identLen = i2p::context.GetIdentity ().GetFullLen (); // 387+ bytes
*(uint16_t *)(payload) = htobe16 (identLen);
htobe16buf (payload, identLen);
payload += 2; // cursize
i2p::context.GetIdentity ().ToBuffer (payload, identLen);
payload += identLen;
uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch ();
*(uint32_t *)(payload) = htobe32 (signedOnTime); // signed on time
htobe32buf (payload, signedOnTime); // signed on time
payload += 4;
auto signatureLen = i2p::context.GetIdentity ().GetSignatureLen ();
size_t paddingSize = ((payload - buf) + signatureLen)%16;
@@ -443,7 +445,7 @@ namespace transport
void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from)
{
uint32_t relayTag = be32toh (*(uint32_t *)buf);
uint32_t relayTag = bufbe32toh (buf);
auto session = m_Server.FindRelaySession (relayTag);
if (session)
{
@@ -457,7 +459,7 @@ namespace transport
buf += challengeSize;
uint8_t * introKey = buf;
buf += 32; // introkey
uint32_t nonce = be32toh (*(uint32_t *)buf);
uint32_t nonce = bufbe32toh (buf);
SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint);
SendRelayIntro (session.get (), from);
}
@@ -476,9 +478,9 @@ namespace transport
}
*payload = 4;
payload++; // size
*(uint32_t *)payload = htobe32 (to.address ().to_v4 ().to_ulong ()); // Charlie's IP
htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP
payload += 4; // address
*(uint16_t *)payload = htobe16 (to.port ()); // Charlie's port
htobe16buf (payload, to.port ()); // Charlie's port
payload += 2; // port
// Alice
bool isV4 = from.address ().is_v4 (); // Alice's
@@ -496,9 +498,9 @@ namespace transport
memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6
payload += 16; // address
}
*(uint16_t *)payload = htobe16 (from.port ()); // Alice's port
htobe16buf (payload, from.port ()); // Alice's port
payload += 2; // port
*(uint32_t *)payload = htobe32 (nonce);
htobe32buf (payload, nonce);
if (m_State == eSessionStateEstablished)
{
@@ -531,9 +533,9 @@ namespace transport
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 4;
payload++; // size
*(uint32_t *)payload = htobe32 (from.address ().to_v4 ().to_ulong ()); // Alice's IP
htobe32buf (payload, from.address ().to_v4 ().to_ulong ()); // Alice's IP
payload += 4; // address
*(uint16_t *)payload = htobe16 (from.port ()); // Alice's port
htobe16buf (payload, from.port ()); // Alice's port
payload += 2; // port
*payload = 0; // challenge size
uint8_t iv[16];
@@ -550,9 +552,9 @@ namespace transport
uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t remoteSize = *payload;
payload++; // remote size
//boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload)));
//boost::asio::ip::address_v4 remoteIP (bufbe32toh (payload));
payload += remoteSize; // remote address
//uint16_t remotePort = be16toh (*(uint16_t *)(payload));
//uint16_t remotePort = bufbe16toh (payload);
payload += 2; // remote port
uint8_t ourSize = *payload;
payload++; // our size
@@ -570,7 +572,7 @@ namespace transport
ourIP = boost::asio::ip::address_v6 (bytes);
}
payload += ourSize; // our address
uint16_t ourPort = be16toh (*(uint16_t *)(payload));
uint16_t ourPort = bufbe16toh (payload);
payload += 2; // our port
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP);
@@ -582,9 +584,9 @@ namespace transport
if (size == 4)
{
buf++; // size
boost::asio::ip::address_v4 address (be32toh (*(uint32_t* )buf));
boost::asio::ip::address_v4 address (bufbe32toh (buf));
buf += 4; // address
uint16_t port = be16toh (*(uint16_t *)buf);
uint16_t port = bufbe16toh (buf);
// send hole punch of 1 byte
m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (address, port));
}
@@ -600,10 +602,11 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len);
return;
}
//TODO: we are using a dirty solution here but should work for now
SSUHeader * header = (SSUHeader *)buf;
memcpy (header->iv, iv, 16);
header->flag = payloadType << 4; // MSB is 0
header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ());
htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ());
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
i2p::crypto::CBCEncryption encryption;
@@ -612,7 +615,7 @@ namespace transport
encryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, iv, 16);
*(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen);
htobe16buf (buf + len + 16, encryptedLen);
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
}
@@ -623,17 +626,18 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len);
return;
}
//TODO: we are using a dirty solution here but should work for now
SSUHeader * header = (SSUHeader *)buf;
i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv
m_SessionKeyEncryption.SetIV (header->iv);
header->flag = payloadType << 4; // MSB is 0
header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ());
htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ());
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16);
*(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen);
htobe16buf (buf + len + 16, encryptedLen);
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
}
@@ -644,6 +648,7 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len);
return;
}
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
@@ -660,6 +665,7 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len);
return;
}
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
@@ -677,12 +683,13 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len);
return false;
}
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16);
*(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen);
htobe16buf (buf + len + 16, encryptedLen);
uint8_t digest[16];
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest);
return !memcmp (header->mac, digest, 16);
@@ -844,13 +851,14 @@ namespace transport
void SSUSession::ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
uint8_t * buf1 = buf;
uint32_t nonce = be32toh (*(uint32_t *)buf);
uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce
uint8_t size = *buf;
buf++; // size
uint32_t address = (size == 4) ? *(uint32_t *)buf : 0; // use it as is
uint32_t address = (size == 4) ? buf32toh(buf) : 0; // use it as is
buf += size; // address
uint16_t port = *(uint16_t *)buf; // use it as is
uint16_t port = buf16toh(buf); // use it as is
buf += 2; // port
uint8_t * introKey = buf;
if (port && !address)
@@ -914,13 +922,13 @@ namespace transport
uint8_t buf[80 + 18];
uint8_t iv[16];
uint8_t * payload = buf + sizeof (SSUHeader);
*(uint32_t *)payload = htobe32 (nonce);
htobe32buf (payload, nonce);
payload += 4; // nonce
if (address)
{
*payload = 4;
payload++; // size
*(uint32_t *)payload = htobe32 (address);
htobe32buf (payload, address);
payload += 4; // address
}
else
@@ -928,7 +936,7 @@ namespace transport
*payload = 0;
payload++; //size
}
*(uint16_t *)payload = htobe16 (port);
htobe16buf (payload, port);
payload += 2; // port
memcpy (payload, introKey, 32); // intro key

View File

@@ -167,7 +167,7 @@ namespace stream
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
{
uint16_t maxPacketSize = be16toh (*(uint16_t *)optionData);
uint16_t maxPacketSize = bufbe16toh (optionData);
LogPrint (eLogDebug, "Max packet size ", maxPacketSize);
optionData += 2;
}
@@ -256,19 +256,20 @@ namespace stream
uint8_t * packet = p->GetBuffer ();
// TODO: implement setters
size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++);
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
if (isNoAck)
*(uint32_t *)(packet + size) = htobe32 (m_LastReceivedSequenceNumber);
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
else
*(uint32_t *)(packet + size) = 0;
htobuf32 (packet + size, 0);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
packet[size] = RESEND_TIMEOUT;
size++; // resend delay
if (!m_IsOpen)
{
@@ -277,15 +278,15 @@ namespace stream
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;
*(uint16_t *)(packet + size) = htobe16 (flags);
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
*(uint16_t *)(packet + size) = htobe16 (identityLen + signatureLen + 2); // identity + signature + packet size
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
*(uint16_t *)(packet + size) = htobe16 (STREAMING_MTU);
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
@@ -301,9 +302,9 @@ namespace stream
else
{
// follow on packet
*(uint16_t *)(packet + size) = 0;
htobuf16 (packet + size, 0);
size += 2; // flags
*(uint16_t *)(packet + size) = 0; // no options
htobuf16 (packet + size, 0); // no options
size += 2; // options size
size_t sentLen = STREAMING_MTU - size;
if (len < sentLen) sentLen = len;
@@ -338,26 +339,26 @@ namespace stream
Packet p;
uint8_t * packet = p.GetBuffer ();
size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
*(uint32_t *)(packet + size) = 0; // this is plain Ack message
htobuf32 (packet + size, 0); // this is plain Ack message
size += 4; // sequenceNum
*(uint32_t *)(packet + size) = htobe32 (lastReceivedSeqn);
htobe32buf (packet + size, lastReceivedSeqn);
size += 4; // ack Through
uint8_t numNacks = 0;
if (lastReceivedSeqn > m_LastReceivedSequenceNumber)
{
// fill NACKs
uint8_t * nacks = packet + size + 1;
uint8_t numNacks = 0;
auto nextSeqn = m_LastReceivedSequenceNumber + 1;
for (auto it: m_SavedPackets)
{
auto seqn = it->GetSeqn ();
for (uint32_t i = nextSeqn; i < seqn; i++)
{
*(uint32_t *)nacks = htobe32 (i);
htobe32buf (nacks, i);
nacks += 4;
numNacks++;
}
@@ -374,14 +375,14 @@ namespace stream
size++; // NACK count
}
size++; // resend delay
*(uint16_t *)(packet + size) = 0; // nof flags set
htobuf16 (packet + size, 0); // nof flags set
size += 2; // flags
*(uint16_t *)(packet + size) = 0; // no options
htobuf16 (packet + size, 0); // no options
size += 2; // options size
p.len = size;
SendPackets (std::vector<Packet *> { &p });
LogPrint ("Quick Ack sent");
LogPrint ("Quick Ack sent. ", (int)numNacks, " NACKs");
}
void Stream::Close ()
@@ -392,21 +393,21 @@ namespace stream
Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer ();
size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++);
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
*(uint32_t *)(packet + size) = htobe32 (m_LastReceivedSequenceNumber);
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
size += 4; // ack Through
packet[size] = 0;
size++; // NACK count
size++; // resend delay
*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
size += 2; // flags
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
*(uint16_t *)(packet + size) = htobe16 (signatureLen); // signature only
htobe16buf (packet + size, signatureLen); // signature only
size += 2; // options size
uint8_t * signature = packet + size;
memset (packet + size, 0, signatureLen);
@@ -617,11 +618,11 @@ namespace stream
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload ();
*(uint32_t *)buf = htobe32 (size); // length
htobe32buf (buf, size); // length
buf += 4;
compressor.Get (buf, size);
*(uint16_t *)(buf + 4) = 0; // source port
*(uint16_t *)(buf + 6) = htobe16 (m_Port); // destination port
htobuf16(buf + 4, 0); // source port
htobe16buf (buf + 6, m_Port); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
msg->len += size + 4;
FillI2NPMessageHeader (msg, eI2NPData);
@@ -641,7 +642,7 @@ namespace stream
m_Streams.clear ();
}
}
void StreamingDestination::HandleNextPacket (Packet * packet)
{
uint32_t sendStreamID = packet->GetSendStreamID ();
@@ -652,21 +653,38 @@ namespace stream
it->second->HandleNextPacket (packet);
else
{
LogPrint ("Unknown stream ", sendStreamID);
LogPrint ("Unknown stream sendStreamID=", sendStreamID);
delete packet;
}
}
else // new incoming stream
else
{
auto incomingStream = CreateNewIncomingStream ();
incomingStream->HandleNextPacket (packet);
if (m_Acceptor != nullptr)
m_Acceptor (incomingStream);
else
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{
auto incomingStream = CreateNewIncomingStream ();
incomingStream->HandleNextPacket (packet);
if (m_Acceptor != nullptr)
m_Acceptor (incomingStream);
else
{
LogPrint ("Acceptor for incoming stream is not set");
DeleteStream (incomingStream);
}
}
else // follow on packet without SYN
{
LogPrint ("Acceptor for incoming stream is not set");
DeleteStream (incomingStream);
}
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 ("Unknown stream receiveStreamID=", receiveStreamID);
delete packet;
}
}
}

View File

@@ -53,15 +53,15 @@ namespace stream
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return be32toh (*(uint32_t *)buf); };
uint32_t GetReceiveStreamID () const { return be32toh (*(uint32_t *)(buf + 4)); };
uint32_t GetSeqn () const { return be32toh (*(uint32_t *)(buf + 8)); };
uint32_t GetAckThrough () const { return be32toh (*(uint32_t *)(buf + 12)); };
uint32_t GetSendStreamID () const { return bufbe32toh (buf); };
uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
uint32_t GetSeqn () const { return bufbe32toh (buf + 8); };
uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); };
uint8_t GetNACKCount () const { return buf[16]; };
uint32_t GetNACK (int i) const { return be32toh (((uint32_t *)(buf + 17))[i]); };
uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); };
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags () const { return be16toh (*(uint16_t *)(GetOption () - 2)); };
uint16_t GetOptionSize () const { return be16toh (*(uint16_t *)GetOption ()); };
uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); };
uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); };
const uint8_t * GetOptionData () const { return GetOption () + 2; };
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
@@ -100,8 +100,10 @@ namespace stream
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; };
@@ -224,7 +226,7 @@ namespace stream
else
// socket closed
handler (m_IsReset ? boost::asio::error::make_error_code (boost::asio::error::connection_reset) :
boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
boost::asio::error::make_error_code (boost::asio::error::operation_aborted), received);
}
else
// timeout expired

View File

@@ -31,7 +31,7 @@ namespace tunnel
LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
*(uint32_t *)(tunnelMsg->GetPayload ()) = htobe32 (m_NextTunnelID);
htobe32buf (tunnelMsg->GetPayload (), m_NextTunnelID);
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
i2p::transport::transports.SendMessage (m_NextIdent, tunnelMsg);

View File

@@ -1,3 +1,4 @@
#include <string.h>
#include "I2PEndian.h"
#include <thread>
#include <algorithm>
@@ -33,7 +34,7 @@ namespace tunnel
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
I2NPMessage * msg = NewI2NPMessage ();
*msg->GetPayload () = numRecords;
msg->len += numRecords*sizeof (I2NPBuildRequestRecordElGamalEncrypted) + 1;
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
// shuffle records
std::vector<int> recordIndicies;
@@ -41,22 +42,14 @@ namespace tunnel
std::random_shuffle (recordIndicies.begin(), recordIndicies.end());
// create real records
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(msg->GetPayload () + 1);
uint8_t * records = msg->GetPayload () + 1;
TunnelHopConfig * hop = m_Config->GetFirstHop ();
int i = 0;
while (hop)
{
int idx = recordIndicies[i];
EncryptBuildRequestRecord (*hop->router,
CreateBuildRequestRecord (hop->router->GetIdentHash (),
hop->tunnelID,
hop->nextRouter->GetIdentHash (),
hop->nextTunnelID,
hop->layerKey, hop->ivKey,
hop->replyKey, hop->replyIV,
hop->next ? rnd.GenerateWord32 () : replyMsgID, // we set replyMsgID for last hop only
hop->isGateway, hop->isEndpoint),
records[idx]);
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE,
hop->next ? rnd.GenerateWord32 () : replyMsgID); // we set replyMsgID for last hop only
hop->recordIndex = idx;
i++;
hop = hop->next;
@@ -65,7 +58,7 @@ namespace tunnel
for (int i = numHops; i < numRecords; i++)
{
int idx = recordIndicies[i];
rnd.GenerateBlock ((uint8_t *)(records + idx), sizeof (records[idx]));
rnd.GenerateBlock (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE);
}
// decrypt real records
@@ -79,9 +72,8 @@ namespace tunnel
while (hop1)
{
decryption.SetIV (hop->replyIV);
decryption.Decrypt((uint8_t *)&records[hop1->recordIndex],
sizeof (I2NPBuildRequestRecordElGamalEncrypted),
(uint8_t *)&records[hop1->recordIndex]);
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
hop1 = hop1->next;
}
hop = hop->prev;
@@ -111,9 +103,9 @@ namespace tunnel
auto idx = hop1->recordIndex;
if (idx >= 0 && idx < msg[0])
{
uint8_t * record = msg + 1 + idx*sizeof (I2NPBuildResponseRecord);
uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE;
decryption.SetIV (hop->replyIV);
decryption.Decrypt(record, sizeof (I2NPBuildResponseRecord), record);
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
}
else
LogPrint ("Tunnel hop index ", idx, " is out of range");
@@ -126,9 +118,10 @@ namespace tunnel
hop = m_Config->GetFirstHop ();
while (hop)
{
I2NPBuildResponseRecord * record = (I2NPBuildResponseRecord *)(msg + 1 + hop->recordIndex*sizeof (I2NPBuildResponseRecord));
LogPrint ("Ret code=", (int)record->ret);
if (record->ret)
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET];
LogPrint ("Ret code=", (int)ret);
if (ret)
// if any of participants declined the tunnel is not established
established = false;
hop = hop->next;
@@ -356,7 +349,7 @@ namespace tunnel
I2NPMessage * msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
while (msg)
{
uint32_t tunnelID = be32toh (*(uint32_t *)msg->GetPayload ());
uint32_t tunnelID = bufbe32toh (msg->GetPayload ());
InboundTunnel * tunnel = GetInboundTunnel (tunnelID);
if (tunnel)
tunnel->HandleTunnelDataMsg (msg);

View File

@@ -8,6 +8,7 @@
#include "aes.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "Timestamp.h"
namespace i2p
{
@@ -82,6 +83,28 @@ namespace tunnel
isGateway = false;
}
}
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID)
{
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, router->GetIdentHash (), 32);
htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextRouter->GetIdentHash (), 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);
// TODO: fill padding
router->GetElGamalEncryption ()->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 *)router->GetIdentHash (), 16);
}
};
class TunnelConfig

View File

@@ -61,7 +61,7 @@ namespace tunnel
break;
case eDeliveryTypeTunnel: // 1
LogPrint ("Delivery type tunnel");
m.tunnelID = be32toh (*(uint32_t *)fragment);
m.tunnelID = bufbe32toh (fragment);
fragment += 4; // tunnelID
m.hash = i2p::data::IdentHash (fragment);
fragment += 32; // hash
@@ -79,7 +79,7 @@ namespace tunnel
if (isFragmented)
{
// Message ID
msgID = be32toh (*(uint32_t *)fragment);
msgID = bufbe32toh (fragment);
fragment += 4;
LogPrint ("Fragmented message ", msgID);
isLastFragment = false;
@@ -88,14 +88,14 @@ namespace tunnel
else
{
// follow on
msgID = be32toh (*(uint32_t *)fragment); // MessageID
msgID = bufbe32toh (fragment); // MessageID
fragment += 4;
fragmentNum = (flag >> 1) & 0x3F; // 6 bits
isLastFragment = flag & 0x01;
LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last");
}
uint16_t size = be16toh (*(uint16_t *)fragment);
uint16_t size = bufbe16toh (fragment);
fragment += 2;
LogPrint ("Fragment size=", (int)size);
@@ -105,8 +105,8 @@ namespace tunnel
{
// this is not last message. we have to copy it
m.data = NewI2NPMessage ();
m.data->offset += sizeof (TunnelGatewayHeader); // reserve room for TunnelGateway header
m.data->len += sizeof (TunnelGatewayHeader);
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
*(m.data) = *msg;
}
else
@@ -228,7 +228,7 @@ namespace tunnel
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
{
LogPrint ("TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetHeader()->typeID);
LogPrint ("TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ());
switch (msg.deliveryType)
{
case eDeliveryTypeLocal:
@@ -245,8 +245,8 @@ namespace tunnel
// to somebody else
if (!m_IsInbound) // outbound transit tunnel
{
if (msg.data->GetHeader()->typeID == eI2NPDatabaseStore ||
msg.data->GetHeader()->typeID == eI2NPDatabaseSearchReply )
auto typeID = msg.data->GetTypeID ();
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply )
{
// catch RI or reply with new list of routers
auto ds = NewI2NPMessage ();

View File

@@ -26,7 +26,7 @@ namespace tunnel
{
if (block.deliveryType == eDeliveryTypeTunnel)
{
*(uint32_t *)(di + diLen) = htobe32 (block.tunnelID);
htobe32buf (di + diLen, block.tunnelID);
diLen += 4; // tunnelID
}
@@ -41,7 +41,7 @@ namespace tunnel
if (fullMsgLen <= m_RemainingSize)
{
// message fits. First and last fragment
*(uint16_t *)(di + diLen) = htobe16 (msg->GetLength ());
htobe16buf (di + diLen, msg->GetLength ());
diLen += 2; // size
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ());
@@ -68,14 +68,15 @@ namespace tunnel
if (diLen + 6 <= m_RemainingSize)
{
// delivery instructions fit
uint32_t msgID = msg->GetHeader ()->msgID; // in network bytes order
uint32_t msgID;
memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order
size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size)
// first fragment
di[0] |= 0x08; // fragmented
*(uint32_t *)(di + diLen) = msgID;
htobuf32 (di + diLen, msgID);
diLen += 4; // Message ID
*(uint16_t *)(di + diLen) = htobe16 (size);
htobe16buf (di + diLen, size);
diLen += 2; // size
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size);
@@ -96,9 +97,9 @@ namespace tunnel
{
buf[0] |= 0x01;
isLastFragment = true;
}
*(uint32_t *)(buf + 1) = msgID; //Message ID
*(uint16_t *)(buf + 5) = htobe16 (s); // size
}
htobuf32 (buf + 1, msgID); //Message ID
htobe16buf (buf + 5, s); // size
memcpy (buf + 7, msg->GetBuffer () + size, s);
m_CurrentTunnelDataMsg->len += s+7;
if (isLastFragment)
@@ -134,7 +135,7 @@ namespace tunnel
m_CurrentTunnelDataMsg = NewI2NPMessage ();
m_CurrentTunnelDataMsg->Align (12);
// we reserve space for padding
m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + sizeof (I2NPHeader);
m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE;
m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset;
m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
}
@@ -145,9 +146,9 @@ namespace tunnel
uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer ();
size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset;
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - sizeof (I2NPHeader);
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE;
uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
*(uint32_t *)(buf) = htobe32 (m_TunnelID);
htobe32buf (buf, m_TunnelID);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (buf + 4, 16); // original IV
memcpy (payload + size, buf + 4, 16); // copy IV for checksum

View File

@@ -232,8 +232,12 @@ namespace tunnel
void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg)
{
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
auto it = m_Tests.find (be32toh (deliveryStatus->msgID));
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
@@ -241,7 +245,7 @@ namespace tunnel
it->second.first->SetState (eTunnelStateEstablished);
if (it->second.second->GetState () == eTunnelStateTestFailed)
it->second.second->SetState (eTunnelStateEstablished);
LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - be64toh (deliveryStatus->timestamp), " milliseconds");
LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds");
m_Tests.erase (it);
DeleteI2NPMessage (msg);
}

314
UPnP.cpp
View File

@@ -1,68 +1,270 @@
#ifdef USE_UPNP
#include <string>
#include <boost/lexical_cast.hpp>
#include <thread>
#ifdef _WIN32
#include <windows.h>
#endif
#include <boost/thread/thread.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "Log.h"
#include "RouterContext.h"
#include "UPnP.h"
#include "NetDb.h"
#include "util.h"
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#include <dlfcn.h>
#ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int);
typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
const char *, const char *, const char *, const char *);
#else
/* miniupnpc 1.6 */
typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int, int, int *);
typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
const char *, const char *, const char *, const char *, const char *);
#endif
typedef int (*upnp_UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int);
typedef int (*upnp_UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *);
typedef int (*upnp_UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *);
typedef void (*upnp_freeUPNPDevlistFunc) (struct UPNPDev *);
typedef void (*upnp_FreeUPNPUrlsFunc) (struct UPNPUrls *);
namespace i2p
{
UPnP::UPnP (): m_Timer (m_Service),
m_Endpoint (boost::asio::ip::udp::v4 (), UPNP_REPLY_PORT),
m_MulticastEndpoint (boost::asio::ip::address::from_string (UPNP_GROUP), UPNP_PORT),
m_Socket (m_Service, m_Endpoint.protocol ())
{
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
m_Socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
}
UPnP::~UPnP ()
{
}
namespace UPnP
{
UPnP upnpc;
void UPnP::Run ()
{
DiscoverRouter ();
m_Service.run ();
}
void UPnP::DiscoverRouter ()
{
m_Timer.expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
m_Timer.async_wait (boost::bind (&UPnP::HandleTimer, this, boost::asio::placeholders::error));
UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false)
{
}
std::string address = UPNP_GROUP;
address += ":" + boost::lexical_cast<std::string>(UPNP_PORT);
std::string request = "M-SEARCH * HTTP/1.1\r\n"
"HOST: " + address + "\r\n"
"ST:" + UPNP_ROUTER + "\r\n"
"MAN:\"ssdp:discover\"\r\n"
"MX:3\r\n"
"\r\n\r\n";
m_Socket.send_to (boost::asio::buffer (request.c_str (), request.length ()), m_MulticastEndpoint);
Receive ();
}
void UPnP::Stop ()
{
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void UPnP::Receive ()
{
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, UPNP_MAX_PACKET_LEN), m_SenderEndpoint,
boost::bind (&UPnP::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void UPnP::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred)
{
LogPrint ("UPnP: ", bytes_transferred, " received from ", m_SenderEndpoint.address ());
std::string str (m_ReceiveBuffer, bytes_transferred);
LogPrint (str);
m_Timer.cancel ();
}
void UPnP::Start()
{
m_Thread = new std::thread (std::bind (&UPnP::Run, this));
}
UPnP::~UPnP ()
{
}
void UPnP::Run ()
{
#ifdef MAC_OSX
m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY);
#elif _WIN32
m_Module = LoadLibrary ("libminiupnpc.dll");
if (m_Module == NULL)
{
LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!");
return;
}
else
{
m_IsModuleLoaded = true;
}
#else
m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY);
#endif
#ifndef _WIN32
if (!m_Module)
{
LogPrint ("no UPnP module available (", dlerror (), ")");
return;
}
else
{
m_IsModuleLoaded = true;
}
#endif
for (auto& address : context.GetRouterInfo ().GetAddresses ())
{
if (!address.host.is_v6 ())
{
m_Port = std::to_string (util::config::GetArg ("-port", address.port));
Discover ();
if (address.transportStyle == data::RouterInfo::eTransportSSU )
{
TryPortMapping (I2P_UPNP_UDP);
}
else if (address.transportStyle == data::RouterInfo::eTransportNTCP )
{
TryPortMapping (I2P_UPNP_TCP);
}
}
}
}
void UPnP::Discover ()
{
const char *error;
#ifdef _WIN32
upnp_upnpDiscoverFunc upnpDiscoverFunc = (upnp_upnpDiscoverFunc) GetProcAddress (m_Module, "upnpDiscover");
#else
upnp_upnpDiscoverFunc upnpDiscoverFunc = (upnp_upnpDiscoverFunc) dlsym (m_Module, "upnpDiscover");
// reinterpret_cast<upnp_upnpDiscoverFunc> (dlsym(...));
if ( (error = dlerror ()))
{
LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!");
return;
}
#endif // _WIN32
#ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0);
#else
/* miniupnpc 1.6 */
int nerror = 0;
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
#endif
int r;
#ifdef _WIN32
upnp_UPNP_GetValidIGDFunc UPNP_GetValidIGDFunc = (upnp_UPNP_GetValidIGDFunc) GetProcAddress (m_Module, "UPNP_GetValidIGD");
#else
upnp_UPNP_GetValidIGDFunc UPNP_GetValidIGDFunc = (upnp_UPNP_GetValidIGDFunc) dlsym (m_Module, "UPNP_GetValidIGD");
#endif
r = (*UPNP_GetValidIGDFunc) (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
if (r == 1)
{
upnp_UPNP_GetExternalIPAddressFunc UPNP_GetExternalIPAddressFunc = (upnp_UPNP_GetExternalIPAddressFunc) dlsym (m_Module, "UPNP_GetExternalIPAddress");
r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(r != UPNPCOMMAND_SUCCESS)
{
LogPrint ("UPnP: UPNP_GetExternalIPAddress () returned ", r);
return;
}
else
{
if (m_externalIPAddress[0])
{
LogPrint ("UPnP: ExternalIPAddress = ", m_externalIPAddress);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
return;
}
else
{
LogPrint ("UPnP: GetExternalIPAddress failed.");
return;
}
}
}
}
void UPnP::TryPortMapping (int type)
{
std::string strType;
switch (type)
{
case I2P_UPNP_TCP:
strType = "TCP";
break;
case I2P_UPNP_UDP:
default:
strType = "UDP";
}
int r;
std::string strDesc = "I2Pd";
try {
for (;;) {
#ifdef _WIN32
upnp_UPNP_AddPortMappingFunc UPNP_AddPortMappingFunc = (upnp_UPNP_AddPortMappingFunc) GetProcAddress (m_Module, "UPNP_AddPortMapping");
#else
upnp_UPNP_AddPortMappingFunc UPNP_AddPortMappingFunc = (upnp_UPNP_AddPortMappingFunc) dlsym (m_Module, "UPNP_AddPortMapping");
#endif
#ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), m_Port.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0);
#else
/* miniupnpc 1.6 */
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), m_Port.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
#endif
if (r!=UPNPCOMMAND_SUCCESS)
{
LogPrint ("AddPortMapping (", m_Port.c_str () ,", ", m_Port.c_str () ,", ", m_NetworkAddr, ") failed with code ", r);
return;
}
else
{
LogPrint ("UPnP Port Mapping successful. (", m_NetworkAddr ,":", m_Port.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", m_Port.c_str() ,")");
return;
}
sleep(20*60);
}
}
catch (boost::thread_interrupted)
{
CloseMapping(type);
Close();
throw;
}
}
void UPnP::CloseMapping (int type)
{
std::string strType;
switch (type)
{
case I2P_UPNP_TCP:
strType = "TCP";
break;
case I2P_UPNP_UDP:
default:
strType = "UDP";
}
int r = 0;
#ifdef _WIN32
upnp_UPNP_DeletePortMappingFunc UPNP_DeletePortMappingFunc = (upnp_UPNP_DeletePortMappingFunc) GetProcAddress (m_Module, "UPNP_DeletePortMapping");
#else
upnp_UPNP_DeletePortMappingFunc UPNP_DeletePortMappingFunc = (upnp_UPNP_DeletePortMappingFunc) dlsym (m_Module, "UPNP_DeletePortMapping");
#endif
r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), strType.c_str (), 0);
LogPrint ("UPNP_DeletePortMapping() returned : ", r, "\n");
}
void UPnP::Close ()
{
#ifdef _WIN32
upnp_freeUPNPDevlistFunc freeUPNPDevlistFunc = (upnp_freeUPNPDevlistFunc) GetProcAddress (m_Module, "freeUPNPDevlist");
#else
upnp_freeUPNPDevlistFunc freeUPNPDevlistFunc = (upnp_freeUPNPDevlistFunc) dlsym (m_Module, "freeUPNPDevlist");
#endif
freeUPNPDevlistFunc (m_Devlist);
m_Devlist = 0;
#ifdef _WIN32
upnp_FreeUPNPUrlsFunc FreeUPNPUrlsFunc = (upnp_FreeUPNPUrlsFunc) GetProcAddress (m_Module, "FreeUPNPUrlsFunc");
#else
upnp_FreeUPNPUrlsFunc FreeUPNPUrlsFunc = (upnp_FreeUPNPUrlsFunc) dlsym (m_Module, "FreeUPNPUrlsFunc");
#endif
FreeUPNPUrlsFunc (&m_upnpUrls);
#ifndef _WIN32
dlclose (m_Module);
#else
FreeLibrary (m_Module);
#endif
}
void UPnP::HandleTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint ("UPnP: timeout expired");
m_Service.stop ();
}
}
}
}
#endif

76
UPnP.h
View File

@@ -1,41 +1,65 @@
#ifndef UPNP_H__
#define UPNP_H__
#ifndef __UPNP_H__
#define __UPNP_H__
#ifdef USE_UPNP
#include <string>
#include <thread>
#include <miniupnpc/miniwget.h>
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/upnperrors.h>
#include <boost/asio.hpp>
#include "util.h"
#define I2P_UPNP_TCP 1
#define I2P_UPNP_UDP 2
namespace i2p
{
const int UPNP_MAX_PACKET_LEN = 1500;
const char UPNP_GROUP[] = "239.255.255.250";
const int UPNP_PORT = 1900;
const int UPNP_REPLY_PORT = 1901;
const char UPNP_ROUTER[] = "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
namespace UPnP
{
class UPnP
{
public:
public:
UPnP ();
~UPnP ();
UPnP ();
~UPnP ();
void Close ();
void Run ();
void Start ();
void Stop ();
private:
void Discover ();
void TryPortMapping (int type);
void CloseMapping (int type);
private:
void Run ();
void DiscoverRouter ();
void Receive ();
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred);
void HandleTimer (const boost::system::error_code& ecode);
private:
std::thread * m_Thread;
struct UPNPUrls m_upnpUrls;
struct IGDdatas m_upnpData;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
boost::asio::ip::udp::endpoint m_Endpoint, m_MulticastEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_Socket;
char m_ReceiveBuffer[UPNP_MAX_PACKET_LEN];
};
// For miniupnpc
char * m_MulticastIf = 0;
char * m_Minissdpdpath = 0;
struct UPNPDev * m_Devlist = 0;
char m_NetworkAddr[64];
char m_externalIPAddress[40];
bool m_IsModuleLoaded;
std::string m_Port = std::to_string (util::config::GetArg ("-port", 17070));
#ifndef _WIN32
void *m_Module;
#else
HINSTANCE *m_Module;
#endif
};
extern UPnP upnpc;
}
}
#endif
#endif

View File

@@ -66,7 +66,7 @@ namespace crypto
"movups %%xmm1, 224(%[sched]) \n"
: // output
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
: "%xmm1", "%xmm2", "%xmm3", "%xmm4" // clogged
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
);
}
@@ -94,7 +94,7 @@ namespace crypto
"movups (%[in]), %%xmm0 \n"
EncryptAES256(sched)
"movups %%xmm0, (%[out]) \n"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
);
}
@@ -122,7 +122,7 @@ namespace crypto
"movups (%[in]), %%xmm0 \n"
DecryptAES256(sched)
"movups %%xmm0, (%[out]) \n"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
);
}
@@ -150,7 +150,7 @@ namespace crypto
CallAESIMC(176)
CallAESIMC(192)
CallAESIMC(208)
: : [shed]"r"(GetKeySchedule ()) : "%xmm0"
: : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory"
);
}

View File

@@ -76,7 +76,7 @@ namespace api
void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote)
{
if (dest)
i2p::data::netdb.RequestDestination (remote, true, dest->GetTunnelPool ());
dest->RequestDestination (remote);
}
std::shared_ptr<i2p::stream::Stream> CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote)

View File

@@ -1,10 +1,11 @@
cmake_minimum_required ( VERSION 2.8 )
cmake_minimum_required ( VERSION 2.8.5 )
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)
# paths
@@ -140,25 +141,32 @@ 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 "---------------------------------------")
add_executable ( ${PROJECT_NAME} ${COMMON_SRC} ${DAEMON_SRC})
#Handle paths nicely
include(GNUInstallDirs)
if (WITH_HARDENING AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-z relro -z now" )
if (WITH_BINARY)
add_executable ( "${PROJECT_NAME}-bin" ${COMMON_SRC} ${DAEMON_SRC})
set_target_properties("${PROJECT_NAME}-bin" PROPERTIES OUTPUT_NAME "${PROJECT_NAME}")
if (WITH_HARDENING AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_target_properties("${PROJECT_NAME}-bin" PROPERTIES LINK_FLAGS "-z relro -z now" )
endif ()
if (WITH_STATIC)
set(BUILD_SHARED_LIBS OFF)
set_target_properties("${PROJECT_NAME}-bin" PROPERTIES LINK_FLAGS "-static" )
endif ()
target_link_libraries( "${PROJECT_NAME}-bin" ${Boost_LIBRARIES} ${CRYPTO++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} )
install(TARGETS "${PROJECT_NAME}-bin" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} )
endif ()
if (WITH_STATIC)
set(BUILD_SHARED_LIBS OFF)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-static" )
endif ()
target_link_libraries( ${PROJECT_NAME} ${Boost_LIBRARIES} ${CRYPTO++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} )
install(TARGETS i2pd RUNTIME DESTINATION "bin")
if (WITH_LIBRARY)
add_library("lib${PROJECT_NAME}" SHARED ${COMMON_SRC} ${LIBRARY_SRC})
install(TARGETS "lib${PROJECT_NAME}" LIBRARY DESTINATION "lib")
add_library(${PROJECT_NAME} SHARED ${COMMON_SRC} ${LIBRARY_SRC})
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
endif ()

View File

@@ -5,6 +5,16 @@ COMMON_SRC = \
TransitTunnel.cpp Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp \
TunnelGateway.cpp Destination.cpp util.cpp aes.cpp base64.cpp
ifeq ($(UNAME),Darwin)
# This is needed on OS X for some reason I don't understand (yet).
# Else will get linker error about unknown symbols. - torkel
COMMON_SRC += \
BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp SAM.cpp SOCKS.cpp \
UPnP.cpp HTTPServer.cpp HTTPProxy.cpp i2p.cpp DaemonLinux.cpp
endif
# also: Daemon{Linux,Win32}.cpp will be added later
DAEMON_SRC = $(COMMON_SRC) \
BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp SAM.cpp SOCKS.cpp UPnP.cpp \

View File

@@ -239,7 +239,7 @@ namespace http
if (site)
{
// User-Agent is needed to get the server list routerInfo files.
site << "GET " << u.path_ << " HTTP/1.0\r\nHost: " << u.host_
site << "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";
// read response
std::string version, statusMessage;
@@ -249,10 +249,25 @@ namespace http
std::getline (site, statusMessage);
if (status == 200) // OK
{
bool isChunked = false;
std::string header;
while (std::getline(site, header) && header != "\r"){}
while (!site.eof () && header != "\r")
{
std::getline(site, header);
auto colon = header.find (':');
if (colon != std::string::npos)
{
std::string field = header.substr (0, colon);
if (field == i2p::util::http::TRANSFER_ENCODING)
isChunked = (header.find ("chunked", colon + 1) != std::string::npos);
}
}
std::stringstream ss;
ss << site.rdbuf();
if (isChunked)
MergeChunkedResponse (site, ss);
else
ss << site.rdbuf();
return ss.str();
}
else
@@ -274,6 +289,24 @@ namespace http
}
}
void MergeChunkedResponse (std::istream& response, std::ostream& merged)
{
while (!response.eof ())
{
std::string hexLen;
int len;
std::getline (response, hexLen);
std::istringstream iss (hexLen);
iss >> std::hex >> len;
if (!len) break;
char * buf = new char[len];
response.read (buf, len);
merged.write (buf, len);
delete[] buf;
std::getline (response, hexLen); // read \r\n after chunk
}
}
int httpRequestViaI2pProxy(const std::string& address, std::string &content)
{
content = "";

8
util.h
View File

@@ -3,6 +3,7 @@
#include <map>
#include <string>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@@ -39,7 +40,14 @@ namespace util
namespace http
{
const char ETAG[] = "ETag";
const char IF_NONE_MATCH[] = "If-None-Match";
const char IF_MODIFIED_SINCE[] = "If-Modified-Since";
const char LAST_MODIFIED[] = "Last-Modified";
const char TRANSFER_ENCODING[] = "Transfer-Encoding";
std::string httpRequest(const std::string& address);
void MergeChunkedResponse (std::istream& response, std::ostream& merged);
int httpRequestViaI2pProxy(const std::string& address, std::string &content); // return http code
struct url {

View File

@@ -2,7 +2,7 @@
#define _VERSION_H_
#define CODENAME "Purple"
#define VERSION "0.5.0"
#define VERSION "0.6.0"
#define I2P_VERSION "0.9.17"
#endif