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 <string>
#include <map> #include <map>
#include <fstream> #include <fstream>
#include <chrono>
#include <condition_variable>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <cryptopp/osrng.h>
#include "base64.h" #include "base64.h"
#include "util.h" #include "util.h"
#include "Identity.h" #include "Identity.h"
#include "Log.h" #include "Log.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h" #include "AddressBook.h"
namespace i2p namespace i2p
@@ -137,7 +143,7 @@ namespace client
f << it.first << "," << it.second.ToBase32 () << std::endl; f << it.first << "," << it.second.ToBase32 () << std::endl;
num++; num++;
} }
LogPrint (eLogInfo, num, " addresses save"); LogPrint (eLogInfo, num, " addresses saved");
} }
else else
LogPrint (eLogError, "Can't open file ", filename); 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 () 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) if (m_Storage)
{ {
m_Storage->Save (m_Addresses); m_Storage->Save (m_Addresses);
delete m_Storage; delete m_Storage;
} }
delete m_DefaultSubscription;
for (auto it: m_Subscriptions)
delete it;
delete m_SubscriptionsUpdateTimer;
} }
AddressBookStorage * AddressBook::CreateStorage () AddressBookStorage * AddressBook::CreateStorage ()
@@ -215,7 +240,7 @@ namespace client
m_Storage = CreateStorage (); m_Storage = CreateStorage ();
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
m_Addresses[address] = ident.GetIdentHash (); 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) void AddressBook::InsertAddress (const i2p::data::IdentityEx& address)
@@ -234,54 +259,42 @@ namespace client
return m_Storage->GetAddress (ident, identity); 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 () void AddressBook::LoadHosts ()
{ {
if (!m_Storage) if (!m_Storage)
m_Storage = CreateStorage (); m_Storage = CreateStorage ();
int numAddresses = m_Storage->Load (m_Addresses); if (m_Storage->Load (m_Addresses) > 0)
if (numAddresses > 0)
{ {
m_IsLoaded = true; m_IsLoaded = true;
return; 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 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..."); LoadHostsFromStream (f);
if (!m_IsDowloading) m_IsLoaded = true;
{
m_IsDowloading = true;
std::thread load_hosts(&AddressBook::LoadHostsFromI2P, this);
load_hosts.detach();
}
return;
} }
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; std::string s;
while (!f.eof ()) while (!f.eof ())
{ {
@@ -298,17 +311,237 @@ namespace client
std::string addr = s.substr(pos); std::string addr = s.substr(pos);
i2p::data::IdentityEx ident; i2p::data::IdentityEx ident;
ident.FromBase64(addr); if (ident.FromBase64(addr))
m_Addresses[name] = ident.GetIdentHash (); {
m_Storage->AddAddress (ident); m_Addresses[name] = ident.GetIdentHash ();
numAddresses++; m_Storage->AddAddress (ident);
numAddresses++;
}
else
LogPrint (eLogError, "Malformed address ", addr, " for ", name);
} }
} }
LogPrint (numAddresses, " addresses loaded"); LogPrint (eLogInfo, numAddresses, " addresses processed");
m_Storage->Save (m_Addresses); if (numAddresses > 0)
m_IsLoaded = true; {
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.h>
#include <string> #include <string>
#include <map> #include <map>
#include <vector>
#include <iostream>
#include <mutex>
#include <boost/asio.hpp>
#include "base64.h" #include "base64.h"
#include "util.h" #include "util.h"
#include "Identity.h" #include "Identity.h"
@@ -13,6 +17,13 @@ namespace i2p
{ {
namespace client 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 class AddressBookStorage // interface for storage
{ {
public: public:
@@ -26,6 +37,7 @@ namespace client
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0; virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
}; };
class AddressBookSubscription;
class AddressBook class AddressBook
{ {
public: public:
@@ -37,19 +49,48 @@ namespace client
const i2p::data::IdentHash * FindAddress (const std::string& address); const i2p::data::IdentHash * FindAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (const i2p::data::IdentityEx& address); 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: private:
AddressBookStorage * CreateStorage (); AddressBookStorage * CreateStorage ();
void LoadHosts ();
void LoadSubscriptions ();
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
private: private:
void LoadHosts (); std::mutex m_AddressBookMutex;
void LoadHostsFromI2P ();
std::map<std::string, i2p::data::IdentHash> m_Addresses; std::map<std::string, i2p::data::IdentHash> m_Addresses;
AddressBookStorage * m_Storage; 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 <string.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "Log.h" #include "Log.h"
#include "NetDb.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "BOB.h" #include "BOB.h"
@@ -9,10 +8,9 @@ namespace i2p
{ {
namespace client namespace client
{ {
BOBI2PInboundTunnel::BOBI2PInboundTunnel (boost::asio::io_service& service, int port, ClientDestination * localDestination): BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, ClientDestination * localDestination):
BOBI2PTunnel (service, localDestination), BOBI2PTunnel (localDestination),
m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Timer (localDestination->GetService ())
m_Timer (service)
{ {
} }
@@ -97,7 +95,7 @@ namespace client
CreateConnection (receiver, leaseSet); CreateConnection (receiver, leaseSet);
else 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.expires_from_now (boost::posix_time::seconds (I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT));
m_Timer.async_wait (std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestTimer, m_Timer.async_wait (std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestTimer,
this, std::placeholders::_1, receiver, ident)); this, std::placeholders::_1, receiver, ident));
@@ -143,8 +141,8 @@ namespace client
delete receiver; delete receiver;
} }
BOBI2POutboundTunnel::BOBI2POutboundTunnel (boost::asio::io_service& service, const std::string& address, int port, BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port,
ClientDestination * localDestination, bool quiet): BOBI2PTunnel (service, localDestination), ClientDestination * localDestination, bool quiet): BOBI2PTunnel (localDestination),
m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet) 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): BOBDestination::BOBDestination (ClientDestination& localDestination):
m_Service (service), m_LocalDestination (localDestination), m_LocalDestination (localDestination),
m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr) m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr)
{ {
} }
@@ -188,6 +186,7 @@ namespace client
{ {
delete m_OutboundTunnel; delete m_OutboundTunnel;
delete m_InboundTunnel; delete m_InboundTunnel;
i2p::client::context.DeleteLocalDestination (&m_LocalDestination);
} }
void BOBDestination::Start () void BOBDestination::Start ()
@@ -221,13 +220,13 @@ namespace client
void BOBDestination::CreateInboundTunnel (int port) void BOBDestination::CreateInboundTunnel (int port)
{ {
if (!m_InboundTunnel) 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) void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet)
{ {
if (!m_OutboundTunnel) 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): BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
@@ -385,8 +384,7 @@ namespace client
LogPrint (eLogDebug, "BOB: start ", m_Nickname); LogPrint (eLogDebug, "BOB: start ", m_Nickname);
if (!m_CurrentDestination) if (!m_CurrentDestination)
{ {
m_CurrentDestination = new BOBDestination (m_Owner.GetService (), m_CurrentDestination = new BOBDestination (*i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
*context.CreateNewLocalDestination (m_Keys, true, &m_Options));
m_Owner.AddDestination (m_Nickname, m_CurrentDestination); m_Owner.AddDestination (m_Nickname, m_CurrentDestination);
} }
if (m_InPort) if (m_InPort)
@@ -580,9 +578,10 @@ namespace client
void BOBCommandChannel::Stop () void BOBCommandChannel::Stop ()
{ {
m_IsRunning = false;
for (auto it: m_Destinations) for (auto it: m_Destinations)
it.second->Stop (); it.second->Stop ();
m_IsRunning = false; m_Acceptor.cancel ();
m_Service.stop (); m_Service.stop ();
if (m_Thread) if (m_Thread)
{ {

12
BOB.h
View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,12 @@
#include <algorithm> #include <algorithm>
#include <cassert>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <cryptopp/dh.h>
#include "Log.h" #include "Log.h"
#include "util.h" #include "util.h"
#include "ElGamal.h"
#include "Timestamp.h"
#include "NetDb.h" #include "NetDb.h"
#include "ClientContext.h"
#include "Destination.h" #include "Destination.h"
namespace i2p namespace i2p
@@ -16,8 +19,7 @@ namespace client
m_Keys (keys), m_LeaseSet (nullptr), m_IsPublic (isPublic), m_PublishReplyToken (0), m_Keys (keys), m_LeaseSet (nullptr), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service) m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service)
{ {
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH; int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
if (params) if (params)
@@ -45,17 +47,24 @@ namespace client
} }
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen); m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen);
if (m_IsPublic) 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: m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
} }
ClientDestination::~ClientDestination () ClientDestination::~ClientDestination ()
{ {
Stop (); if (m_IsRunning)
Stop ();
for (auto it: m_LeaseSetRequests)
delete it.second;
for (auto it: m_RemoteLeaseSets) for (auto it: m_RemoteLeaseSets)
delete it.second; delete it.second;
if (m_Pool) if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
if (m_StreamingDestination)
delete m_StreamingDestination;
if (m_DatagramDestination)
delete m_DatagramDestination;
} }
void ClientDestination::Run () void ClientDestination::Run ()
@@ -75,34 +84,40 @@ namespace client
void ClientDestination::Start () void ClientDestination::Start ()
{ {
m_Pool->SetLocalDestination (this); if (!m_IsRunning)
m_Pool->SetActive (true); {
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this)); m_Pool->SetLocalDestination (this);
m_StreamingDestination->Start (); m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
m_StreamingDestination->Start ();
}
} }
void ClientDestination::Stop () void ClientDestination::Stop ()
{ {
m_StreamingDestination->Stop (); if (m_IsRunning)
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
if (m_Pool)
{ {
m_Pool->SetLocalDestination (nullptr); m_IsRunning = false;
i2p::tunnel::tunnels.StopTunnelPool (m_Pool); m_StreamingDestination->Stop ();
} if (m_DatagramDestination)
m_IsRunning = false; {
m_Service.stop (); auto d = m_DatagramDestination;
if (m_Thread) m_DatagramDestination = nullptr;
{ delete d;
m_Thread->join (); }
delete m_Thread; if (m_Pool)
m_Thread = 0; {
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 else
{ {
LogPrint ("All leases of remote LeaseSet expired. Request it"); LogPrint ("All leases of remote LeaseSet expired. Request it");
i2p::data::netdb.RequestDestination (ident, true, m_Pool); RequestDestination (ident);
} }
} }
else else
@@ -180,15 +195,17 @@ namespace client
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
{ {
I2NPHeader * header = (I2NPHeader *)buf; uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (header->typeID) switch (typeID)
{ {
case eI2NPData: case eI2NPData:
HandleDataMessage (buf + sizeof (I2NPHeader), be16toh (header->size)); HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break; break;
case eI2NPDatabaseStore: case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (buf + sizeof (I2NPHeader), be16toh (header->size)); HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); // TODO: remove break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
break; break;
default: default:
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
@@ -197,14 +214,14 @@ namespace client
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
{ {
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf; uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = sizeof (I2NPDatabaseStoreMsg); size_t offset = DATABASE_STORE_HEADER_SIZE;
if (msg->replyToken) // TODO: if (replyToken) // TODO:
offset += 36; offset += 36;
if (msg->type == 1) // LeaseSet if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
{ {
LogPrint (eLogDebug, "Remote 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 ()) if (it != m_RemoteLeaseSets.end ())
{ {
it->second->Update (buf + offset, len - offset); it->second->Update (buf + offset, len - offset);
@@ -213,17 +230,69 @@ namespace client
else else
{ {
LogPrint (eLogDebug, "New remote LeaseSet added"); 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 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) void ClientDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
{ {
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload (); uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
uint32_t msgID = be32toh (deliveryStatus->msgID);
if (msgID == m_PublishReplyToken) if (msgID == m_PublishReplyToken)
{ {
LogPrint (eLogDebug, "Publishing confirmed"); LogPrint (eLogDebug, "Publishing confirmed");
@@ -242,7 +311,7 @@ namespace client
if (m_IsPublic) if (m_IsPublic)
Publish (); Publish ();
} }
void ClientDestination::Publish () void ClientDestination::Publish ()
{ {
if (!m_LeaseSet || !m_Pool) if (!m_LeaseSet || !m_Pool)
@@ -294,7 +363,7 @@ namespace client
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{ {
uint32_t length = be32toh (*(uint32_t *)buf); uint32_t length = bufbe32toh (buf);
buf += 4; buf += 4;
// we assume I2CP payload // we assume I2CP payload
switch (buf[9]) 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) std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::LeaseSet& remote, int port)
{ {
if (m_StreamingDestination) if (m_StreamingDestination)
return m_StreamingDestination->CreateNewOutgoingStream (remote, port); return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
return nullptr; else
} return nullptr;
}
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor) void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
{ {
@@ -350,5 +456,111 @@ namespace client
m_DatagramDestination = new i2p::datagram::DatagramDestination (*this); m_DatagramDestination = new i2p::datagram::DatagramDestination (*this);
return m_DatagramDestination; 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 <mutex>
#include <memory> #include <memory>
#include <map> #include <map>
#include <set>
#include <string> #include <string>
#include <functional>
#include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "CryptoConst.h" #include "CryptoConst.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "Garlic.h" #include "Garlic.h"
#include "NetDb.h"
#include "Streaming.h" #include "Streaming.h"
#include "Datagram.h" #include "Datagram.h"
@@ -22,15 +26,31 @@ namespace client
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18; const uint8_t PROTOCOL_TYPE_RAW = 18;
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds 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 // I2CP
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length"; const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3; const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
class ClientDestination: public i2p::garlic::GarlicDestination 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: public:
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr); 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; }; i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); }; bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); };
const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident); const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
// streaming // streaming
i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; }; 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); std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::LeaseSet& remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams (); void StopAcceptingStreams ();
@@ -79,18 +102,24 @@ namespace client
void UpdateLeaseSet (); void UpdateLeaseSet ();
void Publish (); void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); 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 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: private:
bool m_IsRunning; volatile bool m_IsRunning;
std::thread * m_Thread; std::thread * m_Thread;
boost::asio::io_service m_Service; boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work; boost::asio::io_service::work m_Work;
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
std::map<i2p::data::IdentHash, i2p::data::LeaseSet *> m_RemoteLeaseSets; std::map<i2p::data::IdentHash, i2p::data::LeaseSet *> m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, LeaseSetRequest *> m_LeaseSetRequests;
i2p::tunnel::TunnelPool * m_Pool; i2p::tunnel::TunnelPool * m_Pool;
i2p::data::LeaseSet * m_LeaseSet; i2p::data::LeaseSet * m_LeaseSet;

View File

@@ -4,6 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <cryptopp/integer.h> #include <cryptopp/integer.h>
#include <cryptopp/osrng.h> #include <cryptopp/osrng.h>
#include <cryptopp/dh.h>
#include <cryptopp/sha.h> #include <cryptopp/sha.h>
#include "CryptoConst.h" #include "CryptoConst.h"
#include "Log.h" #include "Log.h"
@@ -51,7 +52,6 @@ namespace crypto
CryptoPP::AutoSeededRandomPool rnd; CryptoPP::AutoSeededRandomPool rnd;
CryptoPP::Integer y, k, a, b1; CryptoPP::Integer y, k, a, b1;
bool m_ZeroPadding;
}; };
inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
@@ -71,6 +71,17 @@ namespace crypto
memcpy (data, m + 33, 222); memcpy (data, m + 33, 222);
return true; 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 // AES block
len += CreateAESBlock (buf, msg); len += CreateAESBlock (buf, msg);
*(uint32_t *)(m->GetPayload ()) = htobe32 (len); htobe32buf (m->GetPayload (), len);
m->len += len + 4; m->len += len + 4;
FillI2NPMessageHeader (m, eI2NPGarlic); FillI2NPMessageHeader (m, eI2NPGarlic);
if (msg) if (msg)
@@ -151,7 +151,7 @@ namespace garlic
size_t blockSize = 0; size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags/2); bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags/2);
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; 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; blockSize += 2;
if (newTags) // session tags recreated if (newTags) // session tags recreated
{ {
@@ -168,7 +168,7 @@ namespace garlic
buf[blockSize] = 0; // flag buf[blockSize] = 0; // flag
blockSize++; blockSize++;
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
*payloadSize = htobe32 (len); htobe32buf (payloadSize, len);
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len); CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
blockSize += len; blockSize += len;
size_t rem = blockSize % 16; size_t rem = blockSize % 16;
@@ -220,9 +220,9 @@ namespace garlic
memset (payload + size, 0, 3); // certificate of message memset (payload + size, 0, 3); // certificate of message
size += 3; size += 3;
*(uint32_t *)(payload + size) = htobe32 (msgID); // MessageID htobe32buf (payload + size, msgID); // MessageID
size += 4; size += 4;
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message htobe64buf (payload + size, ts); // Expiration of message
size += 8; size += 8;
return size; return size;
} }
@@ -246,9 +246,9 @@ namespace garlic
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength (); size += msg->GetLength ();
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
size += 4; size += 4;
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove htobe64buf (buf + size, ts); // Expiration of clove
size += 8; size += 8;
memset (buf + size, 0, 3); // certificate of clove memset (buf + size, 0, 3); // certificate of clove
size += 3; size += 3;
@@ -269,7 +269,7 @@ namespace garlic
// hash and tunnelID sequence is reversed for Garlic // hash and tunnelID sequence is reversed for Garlic
memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash
size += 32; size += 32;
*(uint32_t *)(buf + size) = htobe32 (leases[i].tunnelID); // tunnelID htobe32buf (buf + size, leases[i].tunnelID); // tunnelID
size += 4; size += 4;
// create msg // create msg
I2NPMessage * msg = CreateDeliveryStatusMsg (msgID); I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
@@ -288,9 +288,9 @@ namespace garlic
DeleteI2NPMessage (msg); DeleteI2NPMessage (msg);
// fill clove // fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec 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; size += 4;
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove htobe64buf (buf + size, ts); // Expiration of clove
size += 8; size += 8;
memset (buf + size, 0, 3); // certificate of clove memset (buf + size, 0, 3); // certificate of clove
size += 3; size += 3;
@@ -331,7 +331,7 @@ namespace garlic
void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg) void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg)
{ {
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
uint32_t length = be32toh (*(uint32_t *)buf); uint32_t length = bufbe32toh (buf);
buf += 4; // length buf += 4; // length
auto it = m_Tags.find (SessionTag(buf)); auto it = m_Tags.find (SessionTag(buf));
if (it != m_Tags.end ()) 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, void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
i2p::tunnel::InboundTunnel * from) i2p::tunnel::InboundTunnel * from)
{ {
uint16_t tagCount = be16toh (*(uint16_t *)buf); uint16_t tagCount = bufbe16toh (buf);
buf += 2; len -= 2; buf += 2; len -= 2;
if (tagCount > 0) if (tagCount > 0)
{ {
@@ -404,7 +404,7 @@ namespace garlic
} }
buf += tagCount*32; buf += tagCount*32;
len -= tagCount*32; len -= tagCount*32;
uint32_t payloadSize = be32toh (*(uint32_t *)buf); uint32_t payloadSize = bufbe32toh (buf);
if (payloadSize > len) if (payloadSize > len)
{ {
LogPrint (eLogError, "Unexpected payload size ", payloadSize); LogPrint (eLogError, "Unexpected payload size ", payloadSize);
@@ -418,9 +418,7 @@ namespace garlic
buf++; // flag buf++; // flag
// payload // payload
uint8_t hash[32]; if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match
CryptoPP::SHA256().CalculateDigest(hash, buf, payloadSize);
if (memcmp (hash, payloadHash, 32)) // payload hash doesn't match
{ {
LogPrint ("Wrong payload hash"); LogPrint ("Wrong payload hash");
return; return;
@@ -462,7 +460,7 @@ namespace garlic
// gwHash and gwTunnel sequence is reverted // gwHash and gwTunnel sequence is reverted
uint8_t * gwHash = buf; uint8_t * gwHash = buf;
buf += 32; buf += 32;
uint32_t gwTunnel = be32toh (*(uint32_t *)buf); uint32_t gwTunnel = bufbe32toh (buf);
buf += 4; buf += 4;
i2p::tunnel::OutboundTunnel * tunnel = nullptr; i2p::tunnel::OutboundTunnel * tunnel = nullptr;
if (from && from->GetTunnelPool ()) if (from && from->GetTunnelPool ())
@@ -528,8 +526,7 @@ namespace garlic
void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg) void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
{ {
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload (); uint32_t msgID = bufbe32toh (msg->GetPayload ());
uint32_t msgID = be32toh (deliveryStatus->msgID);
{ {
auto it = m_CreatedSessions.find (msgID); auto it = m_CreatedSessions.find (msgID);
if (it != m_CreatedSessions.end ()) if (it != m_CreatedSessions.end ())

View File

@@ -485,17 +485,17 @@ namespace util
{ {
switch (status) switch (status)
{ {
case 105: buffers.push_back(boost::asio::buffer("HTTP/1.0 105 Name Not Resolved\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.0 200 OK\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.0 400 Bad Request\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.0 404 Not Found\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.0 408 Request Timeout\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.0 500 Internal Server Error\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.0 502 Bad Gateway\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.0 503 Not Implemented\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.0 504 Gateway Timeout\r\n")); break; case 504: buffers.push_back(boost::asio::buffer("HTTP/1.1 504 Gateway Timeout\r\n")); break;
default: 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) for (std::size_t i = 0; i < headers.size(); ++i)
@@ -792,7 +792,7 @@ namespace util
std::string b32 = it.first.ToBase32 (); std::string b32 = it.first.ToBase32 ();
s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION; s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION;
s << "&" << HTTP_PARAM_BASE32_ADDRESS << "=" << b32 << ">"; 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>"; s << "<br><b>Streams:</b><br>";
for (auto it: dest->GetStreamingDestination ()->GetStreams ()) 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 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
s << "<br>"<< std::endl; s << "<br>"<< std::endl;
@@ -874,10 +874,12 @@ namespace util
SendToDestination (leaseSet, port, buf, len); SendToDestination (leaseSet, port, buf, len);
else 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.expires_from_now (boost::posix_time::seconds(HTTP_DESTINATION_REQUEST_TIMEOUT));
m_Timer.async_wait (boost::bind (&HTTPConnection::HandleDestinationRequestTimeout, 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 <string.h>
#include <atomic> #include <atomic>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <cryptopp/sha.h>
#include <cryptopp/gzip.h> #include <cryptopp/gzip.h>
#include "ElGamal.h" #include "ElGamal.h"
#include "Timestamp.h" #include "Timestamp.h"
@@ -40,31 +39,26 @@ namespace i2p
static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID) void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID)
{ {
I2NPHeader * header = msg->GetHeader (); msg->SetTypeID (msgType);
header->typeID = msgType;
if (replyMsgID) // for tunnel creation if (replyMsgID) // for tunnel creation
header->msgID = htobe32 (replyMsgID); msg->SetMsgID (replyMsgID);
else else
{ {
header->msgID = htobe32 (I2NPmsgID); msg->SetMsgID (I2NPmsgID);
I2NPmsgID++; I2NPmsgID++;
} }
header->expiration = htobe64 (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
int len = msg->GetLength () - sizeof (I2NPHeader); msg->UpdateSize ();
header->size = htobe16 (len); msg->UpdateChks ();
uint8_t hash[32]; }
CryptoPP::SHA256().CalculateDigest(hash, msg->GetPayload (), len);
header->chks = hash[0];
}
void RenewI2NPMessageHeader (I2NPMessage * msg) void RenewI2NPMessageHeader (I2NPMessage * msg)
{ {
if (msg) if (msg)
{ {
I2NPHeader * header = msg->GetHeader (); msg->SetMsgID (I2NPmsgID);
header->msgID = htobe32 (I2NPmsgID);
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) I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
{ {
I2NPDeliveryStatusMsg msg; I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload ();
if (msgID) if (msgID)
{ {
msg.msgID = htobe32 (msgID); htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ()); htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ());
} }
else // for SSU establishment else // for SSU establishment
{ {
msg.msgID = htobe32 (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
msg.timestamp = htobe64 (2); // netID = 2 htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
} }
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg)); m->len += DELIVERY_STATUS_SIZE;
FillI2NPMessageHeader (m, eI2NPDeliveryStatus);
return m;
} }
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers, uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
bool encryption, i2p::tunnel::TunnelPool * pool)
{ {
I2NPMessage * m = NewI2NPMessage (); I2NPMessage * m = NewI2NPMessage ();
uint8_t * buf = m->GetPayload (); uint8_t * buf = m->GetPayload ();
@@ -112,65 +108,81 @@ namespace i2p
buf += 32; buf += 32;
memcpy (buf, from, 32); // from memcpy (buf, from, 32); // from
buf += 32; buf += 32;
uint8_t flag = exploratory ? 0x0C : 0x08; // 1000 - RI, 1100 -exporatory
if (replyTunnelID) if (replyTunnelID)
{ {
*buf = encryption ? 0x03: 0x01; // set delivery flag *buf = flag | 0x01; // set delivery flag
*(uint32_t *)(buf+1) = htobe32 (replyTunnelID); htobe32buf (buf+1, replyTunnelID);
buf += 5; buf += 5;
} }
else else
{ {
encryption = false; // encryption can we set for tunnels only *buf = flag; // flag
*buf = 0; // flag
buf++; buf++;
} }
if (exploratory) if (excludedPeers)
{ {
*(uint16_t *)buf = htobe16 (1); // one exlude record int cnt = excludedPeers->size ();
htobe16buf (buf, cnt);
buf += 2; buf += 2;
// reply with non-floodfill routers only for (auto& it: *excludedPeers)
memset (buf, 0, 32); {
buf += 32; memcpy (buf, it, 32);
buf += 32;
}
} }
else else
{ {
if (excludedPeers) // nothing to exclude
{ htobuf16 (buf, 0);
int cnt = excludedPeers->size (); buf += 2;
*(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;
}
m->len += (buf - m->GetPayload ()); m->len += (buf - m->GetPayload ());
FillI2NPMessageHeader (m, eI2NPDatabaseLookup); FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
return m; 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, I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
const i2p::data::RouterInfo * floodfill) const i2p::data::RouterInfo * floodfill)
{ {
@@ -199,22 +211,22 @@ namespace i2p
router = &context.GetRouterInfo (); router = &context.GetRouterInfo ();
I2NPMessage * m = NewI2NPShortMessage (); I2NPMessage * m = NewI2NPShortMessage ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload (); uint8_t * payload = m->GetPayload ();
memcpy (msg->key, router->GetIdentHash (), 32); memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
msg->type = 0; payload[DATABASE_STORE_TYPE_OFFSET] = 0;
msg->replyToken = 0; htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
CryptoPP::Gzip compressor; CryptoPP::Gzip compressor;
compressor.Put (router->GetBuffer (), router->GetBufferLen ()); compressor.Put (router->GetBuffer (), router->GetBufferLen ());
compressor.MessageEnd(); compressor.MessageEnd();
auto size = compressor.MaxRetrievable (); auto size = compressor.MaxRetrievable ();
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg); uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
*(uint16_t *)buf = htobe16 (size); // size htobe16buf (buf, size); // size
buf += 2; buf += 2;
// TODO: check if size doesn't exceed buffer // TODO: check if size doesn't exceed buffer
compressor.Get (buf, size); 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); FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m; return m;
@@ -225,23 +237,22 @@ namespace i2p
if (!leaseSet) return nullptr; if (!leaseSet) return nullptr;
I2NPMessage * m = NewI2NPShortMessage (); I2NPMessage * m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload (); uint8_t * payload = m->GetPayload ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)payload; memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
memcpy (msg->key, leaseSet->GetIdentHash (), 32); payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
msg->type = 1; // LeaseSet htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
msg->replyToken = htobe32 (replyToken); size_t size = DATABASE_STORE_HEADER_SIZE;
size_t size = sizeof (I2NPDatabaseStoreMsg);
if (replyToken) if (replyToken)
{ {
auto leases = leaseSet->GetNonExpiredLeases (); auto leases = leaseSet->GetNonExpiredLeases ();
if (leases.size () > 0) if (leases.size () > 0)
{ {
*(uint32_t *)(payload + size) = htobe32 (leases[0].tunnelID); htobe32buf (payload + size, leases[0].tunnelID);
size += 4; // reply tunnelID size += 4; // reply tunnelID
memcpy (payload + size, leases[0].tunnelGateway, 32); memcpy (payload + size, leases[0].tunnelGateway, 32);
size += 32; // reply tunnel gateway size += 32; // reply tunnel gateway
} }
else else
msg->replyToken = 0; htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
} }
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen (); size += leaseSet->GetBufferLen ();
@@ -249,73 +260,46 @@ namespace i2p
FillI2NPMessageHeader (m, eI2NPDatabaseStore); FillI2NPMessageHeader (m, eI2NPDatabaseStore);
return m; 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++) 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"); LogPrint ("Record ",i," is ours");
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), records[i].encrypted, (uint8_t *)&clearText); i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
// replace record to reply // replace record to reply
I2NPBuildResponseRecord * reply = (I2NPBuildResponseRecord *)(records + i);
if (i2p::context.AcceptsTunnels ()) if (i2p::context.AcceptsTunnels ())
{ {
i2p::tunnel::TransitTunnel * transitTunnel = i2p::tunnel::TransitTunnel * transitTunnel =
i2p::tunnel::CreateTransitTunnel ( i2p::tunnel::CreateTransitTunnel (
be32toh (clearText.receiveTunnel), bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText.nextIdent, be32toh (clearText.nextTunnel), clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
clearText.layerKey, clearText.ivKey, bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText.flag & 0x80, clearText.flag & 0x40); 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); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
reply->ret = 0; record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
} }
else 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 //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 // encrypt reply
i2p::crypto::CBCEncryption encryption; i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++) for (int j = 0; j < num; j++)
{ {
encryption.SetKey (clearText.replyKey); encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText.replyIV); encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
encryption.Encrypt((uint8_t *)(records + j), sizeof (records[j]), (uint8_t *)(records + j)); uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
} }
return true; return true;
} }
@@ -347,41 +331,42 @@ namespace i2p
} }
else else
{ {
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(buf+1); uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
I2NPBuildRequestRecordClearText clearText; if (HandleBuildRequestRecords (num, buf + 1, clearText))
if (HandleBuildRequestRecords (num, records, 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 // so we send it to reply tunnel
transports.SendMessage (clearText.nextIdent, transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel), CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len, eI2NPVariableTunnelBuildReply, buf, len,
be32toh (clearText.nextMessageID))); bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} }
else else
transports.SendMessage (clearText.nextIdent, transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, be32toh (clearText.nextMessageID))); CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} }
} }
} }
void HandleTunnelBuildMsg (uint8_t * buf, size_t len) void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{ {
I2NPBuildRequestRecordClearText clearText; uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, (I2NPBuildRequestRecordElGamalEncrypted *)buf, clearText)) 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 // so we send it to reply tunnel
transports.SendMessage (clearText.nextIdent, transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel), CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPTunnelBuildReply, buf, len, eI2NPTunnelBuildReply, buf, len,
be32toh (clearText.nextMessageID))); bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} }
else else
transports.SendMessage (clearText.nextIdent, transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPTunnelBuild, buf, len, be32toh (clearText.nextMessageID))); CreateI2NPMessage (eI2NPTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} }
} }
@@ -422,7 +407,7 @@ namespace i2p
{ {
I2NPMessage * msg = NewI2NPMessage (); I2NPMessage * msg = NewI2NPMessage ();
memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); 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; msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
FillI2NPMessageHeader (msg, eI2NPTunnelData); FillI2NPMessageHeader (msg, eI2NPTunnelData);
return msg; return msg;
@@ -431,26 +416,26 @@ namespace i2p
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
{ {
I2NPMessage * msg = NewI2NPMessage (len); I2NPMessage * msg = NewI2NPMessage (len);
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload (); uint8_t * payload = msg->GetPayload ();
header->tunnelID = htobe32 (tunnelID); htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
header->length = htobe16 (len); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
memcpy (msg->GetPayload () + sizeof (TunnelGatewayHeader), buf, len); memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len);
msg->len += sizeof (TunnelGatewayHeader) + len; msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
return msg; return msg;
} }
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * 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 // message is capable to be used without copying
TunnelGatewayHeader * header = (TunnelGatewayHeader *)(msg->GetBuffer () - sizeof (TunnelGatewayHeader)); uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE;
header->tunnelID = htobe32 (tunnelID); htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
int len = msg->GetLength (); int len = msg->GetLength ();
header->length = htobe16 (len); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
msg->offset -= (sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader)); msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
msg->len = msg->offset + sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader) +len; msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
return msg; return msg;
} }
@@ -466,7 +451,7 @@ namespace i2p
const uint8_t * buf, size_t len, uint32_t replyMsgID) const uint8_t * buf, size_t len, uint32_t replyMsgID)
{ {
I2NPMessage * msg = NewI2NPMessage (len); 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->offset += gatewayMsgOffset;
msg->len += gatewayMsgOffset; msg->len += gatewayMsgOffset;
memcpy (msg->GetPayload (), buf, len); memcpy (msg->GetPayload (), buf, len);
@@ -474,24 +459,25 @@ namespace i2p
FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message
len = msg->GetLength (); len = msg->GetLength ();
msg->offset -= gatewayMsgOffset; msg->offset -= gatewayMsgOffset;
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload (); uint8_t * payload = msg->GetPayload ();
header->tunnelID = htobe32 (tunnelID); htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
header->length = htobe16 (len); htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message
return msg; return msg;
} }
void HandleTunnelGatewayMsg (I2NPMessage * msg) void HandleTunnelGatewayMsg (I2NPMessage * msg)
{ {
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload (); const uint8_t * payload = msg->GetPayload ();
uint32_t tunnelID = be32toh(header->tunnelID); uint32_t tunnelID = bufbe32toh(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET);
uint16_t len = be16toh(header->length); uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET);
// we make payload as new I2NP message to send // 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; msg->len = msg->offset + len;
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID); auto typeID = msg->GetTypeID ();
if (msg->GetHeader()->typeID == eI2NPDatabaseStore || LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)typeID);
msg->GetHeader()->typeID == eI2NPDatabaseSearchReply)
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply)
{ {
// transit DatabaseStore my contain new/updated RI // transit DatabaseStore my contain new/updated RI
// or DatabaseSearchReply with new routers // or DatabaseSearchReply with new routers
@@ -511,19 +497,18 @@ namespace i2p
size_t GetI2NPMessageLength (const uint8_t * msg) size_t GetI2NPMessageLength (const uint8_t * msg)
{ {
I2NPHeader * header = (I2NPHeader *)msg; return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
return be16toh (header->size) + sizeof (I2NPHeader);
} }
void HandleI2NPMessage (uint8_t * msg, size_t len) void HandleI2NPMessage (uint8_t * msg, size_t len)
{ {
I2NPHeader * header = (I2NPHeader *)msg; uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
uint32_t msgID = be32toh (header->msgID); uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
LogPrint ("I2NP msg received len=", len,", type=", (int)header->typeID, ", msgID=", (unsigned int)msgID); LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
uint8_t * buf = msg + sizeof (I2NPHeader); uint8_t * buf = msg + I2NP_HEADER_SIZE;
int size = be16toh (header->size); int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
switch (header->typeID) switch (typeID)
{ {
case eI2NPVariableTunnelBuild: case eI2NPVariableTunnelBuild:
LogPrint ("VariableTunnelBuild"); LogPrint ("VariableTunnelBuild");
@@ -542,7 +527,7 @@ namespace i2p
// TODO: // TODO:
break; break;
default: default:
LogPrint ("Unexpected message ", (int)header->typeID); LogPrint ("Unexpected message ", (int)typeID);
} }
} }
@@ -550,7 +535,7 @@ namespace i2p
{ {
if (msg) if (msg)
{ {
switch (msg->GetHeader ()->typeID) switch (msg->GetTypeID ())
{ {
case eI2NPTunnelData: case eI2NPTunnelData:
LogPrint ("TunnelData"); LogPrint ("TunnelData");

View File

@@ -3,80 +3,71 @@
#include <inttypes.h> #include <inttypes.h>
#include <set> #include <set>
#include <cryptopp/sha.h>
#include <string.h> #include <string.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "LeaseSet.h" #include "LeaseSet.h"
namespace i2p 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 // I2NP short header
{ const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0;
uint8_t typeID; const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1;
uint32_t msgID; const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4;
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;
};
struct I2NPBuildRequestRecordClearText // Tunnel Gateway header
{ const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0;
uint32_t receiveTunnel; const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4;
uint8_t ourIdent[32]; const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2;
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];
};
struct I2NPBuildResponseRecord // DeliveryStatus
{ const size_t DELIVERY_STATUS_MSGID_OFFSET = 0;
uint8_t hash[32]; const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4;
uint8_t padding[495]; const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8;
uint8_t ret;
}; // 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 // BuildRequestRecordEncrypted
{ const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0;
uint8_t toPeer[16]; const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16;
uint8_t encrypted[512];
};
struct TunnelGatewayHeader
{
uint32_t tunnelID;
uint16_t length;
};
#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 enum I2NPMessageType
{ {
@@ -110,14 +101,36 @@ namespace tunnel
size_t len, offset, maxLen; size_t len, offset, maxLen;
i2p::tunnel::InboundTunnel * from; i2p::tunnel::InboundTunnel * from;
I2NPMessage (): buf (nullptr),len (sizeof (I2NPHeader) + 2), I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr) {}; offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
// reserve 2 bytes for NTCP header
I2NPHeader * GetHeader () { return (I2NPHeader *)GetBuffer (); }; // header accessors
uint8_t * GetPayload () { return GetBuffer () + sizeof(I2NPHeader); }; 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; }; uint8_t * GetBuffer () { return buf + offset; };
const uint8_t * GetBuffer () const { 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) void Align (size_t alignment)
{ {
size_t rem = ((size_t)GetBuffer ()) % alignment; size_t rem = ((size_t)GetBuffer ()) % alignment;
@@ -137,25 +150,25 @@ namespace tunnel
} }
// for SSU only // 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 void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
{ {
I2NPHeaderShort ssu = *(I2NPHeaderShort *)GetSSUHeader (); const uint8_t * ssu = GetSSUHeader ();
I2NPHeader * header = GetHeader (); GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
header->typeID = ssu.typeID; SetMsgID (msgID);
header->msgID = htobe32 (msgID); SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL);
header->expiration = htobe64 (be32toh (ssu.shortExpiration)*1000LL); SetSize (len - offset - I2NP_HEADER_SIZE);
header->size = htobe16 (len - offset - sizeof (I2NPHeader)); SetChks (0);
header->chks = 0;
} }
uint32_t ToSSU () // return msgID uint32_t ToSSU () // return msgID
{ {
I2NPHeader header = *GetHeader (); uint8_t header[I2NP_HEADER_SIZE];
I2NPHeaderShort * ssu = (I2NPHeaderShort *)GetSSUHeader (); memcpy (header, GetHeader (), I2NP_HEADER_SIZE);
ssu->typeID = header.typeID; uint8_t * ssu = GetSSUHeader ();
ssu->shortExpiration = htobe32 (be64toh (header.expiration)/1000LL); ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
len = offset + sizeof (I2NPHeaderShort) + be16toh (header.size); htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
return be32toh (header.msgID); 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 * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from = nullptr);
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID); I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::set<i2p::data::IdentHash> * excludedPeers = nullptr, bool encryption = false, I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
i2p::tunnel::TunnelPool * pool = nullptr); 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 * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr); I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr);
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0); I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0);
I2NPBuildRequestRecordClearText CreateBuildRequestRecord ( bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
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);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleVariableTunnelBuildReplyMsg (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); void HandleTunnelBuildMsg (uint8_t * buf, size_t len);

View File

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

View File

@@ -1,5 +1,7 @@
#ifndef I2PENDIAN_H__ #ifndef I2PENDIAN_H__
#define I2PENDIAN_H__ #define I2PENDIAN_H__
#include <inttypes.h>
#include <string.h>
#if defined(__linux__) || defined(__FreeBSD_kernel__) #if defined(__linux__) || defined(__FreeBSD_kernel__)
#include <endian.h> #include <endian.h>
@@ -25,6 +27,7 @@
#define le64toh(x) OSSwapLittleToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x)
#else #else
#define NEEDS_LOCAL_ENDIAN
#include <cstdint> #include <cstdint>
uint16_t htobe16(uint16_t int16); uint16_t htobe16(uint16_t int16);
uint32_t htobe32(uint32_t int32); uint32_t htobe32(uint32_t int32);
@@ -44,5 +47,73 @@ uint64_t be64toh(uint64_t big64);
#endif #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__ #endif // I2PENDIAN_H__

View File

@@ -1,6 +1,5 @@
#include "base64.h" #include "base64.h"
#include "Log.h" #include "Log.h"
#include "NetDb.h"
#include "Destination.h" #include "Destination.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "I2PTunnel.h" #include "I2PTunnel.h"
@@ -17,6 +16,13 @@ namespace client
m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet); 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, 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): 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) 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) void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{ {
if (ecode) if (ecode)
{ {
LogPrint ("I2PTunnel read error: ", ecode.message ()); LogPrint ("I2PTunnel read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); 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) void I2PTunnel::AddConnection (std::shared_ptr<I2PTunnelConnection> conn)
{ {
m_Connections.insert (conn); m_Connections.insert (conn);
@@ -155,13 +166,10 @@ namespace client
m_Connections.clear (); m_Connections.clear ();
} }
I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination, I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination):
int port, ClientDestination * localDestination): I2PTunnel (localDestination),
I2PTunnel (service, localDestination ? localDestination : m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
i2p::client::context.CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256)), m_Timer (GetService ()), m_Destination (destination), m_DestinationIdentHash (nullptr)
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)
{ {
} }
@@ -172,11 +180,7 @@ namespace client
void I2PClientTunnel::Start () void I2PClientTunnel::Start ()
{ {
i2p::data::IdentHash identHash; GetIdentHash();
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);
m_Acceptor.listen (); m_Acceptor.listen ();
Accept (); Accept ();
} }
@@ -186,9 +190,26 @@ namespace client
m_Acceptor.close(); m_Acceptor.close();
m_Timer.cancel (); m_Timer.cancel ();
ClearConnections (); ClearConnections ();
auto *originalIdentHash = m_DestinationIdentHash;
m_DestinationIdentHash = nullptr; 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 () void I2PClientTunnel::Accept ()
{ {
auto newSocket = new boost::asio::ip::tcp::socket (GetService ()); auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
@@ -200,71 +221,43 @@ namespace client
{ {
if (!ecode) if (!ecode)
{ {
if (!m_DestinationIdentHash) const i2p::data::IdentHash *identHash = GetIdentHash();
{ if (identHash)
i2p::data::IdentHash identHash; GetLocalDestination ()->CreateStream (
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash)) std::bind (&I2PClientTunnel::HandleStreamRequestComplete,
m_DestinationIdentHash = new i2p::data::IdentHash (identHash); this, std::placeholders::_1, socket), *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));
}
}
else else
{ {
LogPrint ("Remote destination ", m_Destination, " not found"); LogPrint (eLogError,"Closing socket");
delete socket; delete socket;
} }
Accept (); Accept ();
} }
else 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) LogPrint (eLogError,"Closing socket on accept because: ", ecode.message ());
{ delete socket;
m_RemoteLeaseSet = GetLocalDestination ()->FindLeaseSet (*m_DestinationIdentHash);
CreateConnection (socket);
return;
}
} }
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 if (stream)
{ {
LogPrint ("New I2PTunnel connection"); LogPrint (eLogInfo,"New I2PTunnel connection");
auto connection = std::make_shared<I2PTunnelConnection>(this, socket, m_RemoteLeaseSet); auto connection = std::make_shared<I2PTunnelConnection>(this, socket, stream);
AddConnection (connection); AddConnection (connection);
connection->I2PConnect (); connection->I2PConnect ();
} }
else else
{ {
LogPrint ("LeaseSet for I2PTunnel destination not found"); LogPrint (eLogError,"Issue when creating the stream, check the previous warnings for more info.");
delete socket; delete socket;
} }
} }
I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port, I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination):
ClientDestination * localDestination): I2PTunnel (service, localDestination), I2PTunnel (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port)
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 size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // 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 I2PTunnel;
class I2PTunnelConnection: public std::enable_shared_from_this<I2PTunnelConnection> class I2PTunnelConnection: public std::enable_shared_from_this<I2PTunnelConnection>
@@ -25,6 +26,8 @@ namespace client
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket, I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
const i2p::data::LeaseSet * leaseSet); // to I2P 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, 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 const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
~I2PTunnelConnection (); ~I2PTunnelConnection ();
@@ -58,8 +61,7 @@ namespace client
{ {
public: public:
I2PTunnel (boost::asio::io_service& service, ClientDestination * localDestination): I2PTunnel (ClientDestination * localDestination = nullptr);
m_Service (service), m_LocalDestination (localDestination) {};
virtual ~I2PTunnel () { ClearConnections (); }; virtual ~I2PTunnel () { ClearConnections (); };
void AddConnection (std::shared_ptr<I2PTunnelConnection> conn); void AddConnection (std::shared_ptr<I2PTunnelConnection> conn);
@@ -68,11 +70,10 @@ namespace client
ClientDestination * GetLocalDestination () { return m_LocalDestination; }; ClientDestination * GetLocalDestination () { return m_LocalDestination; };
void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; }; 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: private:
boost::asio::io_service& m_Service;
ClientDestination * m_LocalDestination; ClientDestination * m_LocalDestination;
std::set<std::shared_ptr<I2PTunnelConnection> > m_Connections; std::set<std::shared_ptr<I2PTunnelConnection> > m_Connections;
}; };
@@ -81,8 +82,7 @@ namespace client
{ {
public: public:
I2PClientTunnel (boost::asio::io_service& service, const std::string& destination, int port, I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination = nullptr);
ClientDestination * localDestination = nullptr);
~I2PClientTunnel (); ~I2PClientTunnel ();
void Start (); void Start ();
@@ -90,10 +90,10 @@ namespace client
private: private:
const i2p::data::IdentHash * GetIdentHash ();
void Accept (); void Accept ();
void HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket); 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 HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket);
void CreateConnection (boost::asio::ip::tcp::socket * socket);
private: private:
@@ -101,15 +101,13 @@ namespace client
boost::asio::deadline_timer m_Timer; boost::asio::deadline_timer m_Timer;
std::string m_Destination; std::string m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash; const i2p::data::IdentHash * m_DestinationIdentHash;
const i2p::data::LeaseSet * m_RemoteLeaseSet;
}; };
class I2PServerTunnel: public I2PTunnel class I2PServerTunnel: public I2PTunnel
{ {
public: public:
I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port, I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination);
ClientDestination * localDestination);
void Start (); void Start ();
void Stop (); void Stop ();

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
UNAME := $(shell uname -s) UNAME := $(shell uname -s)
SHLIB := libi2pd.so SHLIB := libi2pd.so
I2PD := i2p I2PD := i2p
GREP := fgrep
DEPS := obj/make.dep
include filelist.mk include filelist.mk
@@ -8,45 +10,64 @@ USE_AESNI := yes
USE_STATIC := no USE_STATIC := no
ifeq ($(UNAME),Darwin) ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp DAEMON_SRC += DaemonLinux.cpp
include Makefile.osx include Makefile.osx
else ifeq ($(UNAME),FreeBSD) else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
DAEMON_SRC += DaemonLinux.cpp DAEMON_SRC += DaemonLinux.cpp
include Makefile.bsd include Makefile.bsd
else ifeq ($(UNAME),Linux) else ifeq ($(UNAME),Linux)
DAEMON_SRC += DaemonLinux.cpp DAEMON_SRC += DaemonLinux.cpp
include Makefile.linux include Makefile.linux
else # win32 else # win32
DAEMON_SRC += DaemonWin32.cpp DAEMON_SRC += DaemonWin32.cpp
endif endif
all: obj $(SHLIB) $(I2PD) all: mk_build_dir $(SHLIB) $(I2PD)
obj: mk_build_dir:
mkdir -p obj test -d obj || mkdir obj
obj/%.o : %.cpp %.h api: $(SHLIB)
$(CXX) $(CXXFLAGS) $(INCFLAGS) -c -o $@ $<
## 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 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)) $(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) $(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS) -shared ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
endif
clean: clean:
rm -fr obj $(I2PD) $(SHLIB) rm -rf obj
$(RM) $(I2PD) $(SHLIB)
LATEST_TAG=$(shell git describe --tags --abbrev=0 master) LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
dist: dist:
git archive --format=tar.gz -9 --worktree-attributes \ git archive --format=tar.gz -9 --worktree-attributes \
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz --prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
.PHONY: all .PHONY: all
.PHONY: clean .PHONY: clean
.PHONY: deps
.PHONY: dist .PHONY: dist
.PHONY: api
.PHONY: mk_build_dir

View File

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

View File

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

View File

@@ -1,9 +1,15 @@
CXX = clang++ 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 INCFLAGS = -I/usr/local/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LDLIBS = -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 # OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html # 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 # 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 CXXFLAGS += -maes -DAESNI
endif endif
install: all # Disabled, since it will be the default make rule. I think its better
mkdir -p ${PREFIX}/ # to define the default rule in Makefile and not Makefile.<ostype> - torkel
cp -r i2p ${PREFIX}/ #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); m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
// verify // verify
uint8_t xy[512], hxy[32]; uint8_t xy[512];
memcpy (xy, m_DHKeysPair->publicKey, 256); memcpy (xy, m_DHKeysPair->publicKey, 256);
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
CryptoPP::SHA256().CalculateDigest(hxy, xy, 512); if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512))
if (memcmp (hxy, m_Establisher->phase2.encrypted.hxy, 32))
{ {
LogPrint (eLogError, "Incorrect hash"); LogPrint (eLogError, "Incorrect hash");
transports.ReuseDHKeysPair (m_DHKeysPair); transports.ReuseDHKeysPair (m_DHKeysPair);
@@ -275,11 +274,11 @@ namespace transport
{ {
auto keys = i2p::context.GetPrivateKeys (); auto keys = i2p::context.GetPrivateKeys ();
uint8_t * buf = m_ReceiveBuffer; uint8_t * buf = m_ReceiveBuffer;
*(uint16_t *)buf = htobe16 (keys.GetPublic ().GetFullLen ()); htobe16buf (buf, keys.GetPublic ().GetFullLen ());
buf += 2; buf += 2;
buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE); buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE);
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ()); uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
*(uint32_t *)buf = tsA; htobuf32(buf,tsA);
buf += 4; buf += 4;
size_t signatureLen = keys.GetPublic ().GetSignatureLen (); size_t signatureLen = keys.GetPublic ().GetSignatureLen ();
size_t len = (buf - m_ReceiveBuffer) + signatureLen; size_t len = (buf - m_ReceiveBuffer) + signatureLen;
@@ -339,7 +338,7 @@ namespace transport
LogPrint (eLogDebug, "Phase 3 received: ", bytes_transferred); LogPrint (eLogDebug, "Phase 3 received: ", bytes_transferred);
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
uint8_t * buf = 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); m_RemoteIdentity.FromBuffer (buf + 2, size);
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen (); size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen ();
size_t paddingLen = expectedSize & 0x0F; size_t paddingLen = expectedSize & 0x0F;
@@ -377,7 +376,7 @@ namespace transport
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen) void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
{ {
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/; uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/;
uint32_t tsA = *(uint32_t *)buf; uint32_t tsA = buf32toh(buf);
buf += 4; buf += 4;
buf += paddingLen; buf += paddingLen;
@@ -527,7 +526,7 @@ namespace transport
m_NextMessageOffset = 0; m_NextMessageOffset = 0;
m_Decryption.Decrypt (encrypted, m_NextMessage->buf); 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) if (dataSize)
{ {
// new message // new message
@@ -581,15 +580,15 @@ namespace transport
} }
sendBuffer = msg->GetBuffer () - 2; sendBuffer = msg->GetBuffer () - 2;
len = msg->GetLength (); len = msg->GetLength ();
*((uint16_t *)sendBuffer) = htobe16 (len); htobe16buf (sendBuffer, len);
} }
else else
{ {
// prepare timestamp // prepare timestamp
sendBuffer = m_TimeSyncBuffer; sendBuffer = m_TimeSyncBuffer;
len = 4; len = 4;
*((uint16_t *)sendBuffer) = 0; htobuf16(sendBuffer, 0);
*((uint32_t *)(sendBuffer + 2)) = htobe32 (time (0)); htobe32buf (sendBuffer + 2, time (0));
} }
int rem = (len + 6) & 0x0F; // %16 int rem = (len + 6) & 0x0F; // %16
int padding = 0; int padding = 0;

193
NetDb.cpp
View File

@@ -1,3 +1,4 @@
#include <string.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <fstream> #include <fstream>
#include <vector> #include <vector>
@@ -24,16 +25,9 @@ namespace data
I2NPMessage * RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router, I2NPMessage * RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
const i2p::tunnel::InboundTunnel * replyTunnel) const i2p::tunnel::InboundTunnel * replyTunnel)
{ {
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination, I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
&m_ExcludedPeers, m_IsLeaseSet, m_Pool); &m_ExcludedPeers);
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.insert (router->GetIdentHash ()); m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRouter = router; m_LastRouter = router;
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
@@ -42,7 +36,7 @@ namespace data
I2NPMessage * RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) 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); i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill); m_ExcludedPeers.insert (floodfill);
m_LastRouter = nullptr; m_LastRouter = nullptr;
@@ -62,7 +56,7 @@ namespace data
#endif #endif
NetDb netdb; 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 () void NetDb::Start ()
{ {
Load (m_NetDbPath); 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 reseeder;
reseeder.LoadCertificates (); // we need certificates for SU3 verification reseeder.LoadCertificates (); // we need certificates for SU3 verification
// try SU3 first // try SU3 first
int reseedRetries = 0; int reseedRetries = 0;
while (m_RouterInfos.size () < 100 && reseedRetries < 10) while (m_RouterInfos.size () < 50 && reseedRetries < 10)
{ {
reseeder.ReseedNowSU3(); reseeder.ReseedNowSU3();
reseedRetries++; reseedRetries++;
@@ -93,7 +87,7 @@ namespace data
// if still not enough download .dat files // if still not enough download .dat files
reseedRetries = 0; reseedRetries = 0;
while (m_RouterInfos.size () < 100 && reseedRetries < 10) while (m_RouterInfos.size () < 50 && reseedRetries < 10)
{ {
reseeder.reseedNow(); reseeder.reseedNow();
reseedRetries++; reseedRetries++;
@@ -128,7 +122,7 @@ namespace data
{ {
while (msg) while (msg)
{ {
switch (msg->GetHeader ()->typeID) switch (msg->GetTypeID ())
{ {
case eI2NPDatabaseStore: case eI2NPDatabaseStore:
LogPrint ("DatabaseStore"); LogPrint ("DatabaseStore");
@@ -143,7 +137,7 @@ namespace data
HandleDatabaseLookupMsg (msg); HandleDatabaseLookupMsg (msg);
break; break;
default: // WTF? default: // WTF?
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID); LogPrint ("NetDb: unexpected message type ", msg->GetTypeID ());
i2p::HandleI2NPMessage (msg); i2p::HandleI2NPMessage (msg);
} }
msg = m_Queue.Get (); msg = m_Queue.Get ();
@@ -153,6 +147,7 @@ namespace data
{ {
if (!m_IsRunning) break; if (!m_IsRunning) break;
// if no new DatabaseStore coming, explore it // if no new DatabaseStore coming, explore it
ManageRequests ();
auto numRouters = m_RouterInfos.size (); auto numRouters = m_RouterInfos.size ();
Explore (numRouters < 1500 ? 5 : 1); 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 // request RouterInfo directly
{ RequestedDestination * dest = CreateRequestedDestination (destination, false);
i2p::tunnel::OutboundTunnel * outbound = pool ? pool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel (); auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (outbound) if (floodfill)
{ transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
i2p::tunnel::InboundTunnel * inbound = pool ? pool->GetNextInboundTunnel () :i2p::tunnel::tunnels.GetNextInboundTunnel (); else
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
{ {
RequestedDestination * dest = CreateRequestedDestination (destination, false, false, pool); LogPrint (eLogError, "No floodfills found");
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); DeleteRequestedDestination (dest);
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
} }
} }
void NetDb::HandleDatabaseStoreMsg (I2NPMessage * m) void NetDb::HandleDatabaseStoreMsg (I2NPMessage * m)
{ {
const uint8_t * buf = m->GetPayload (); const uint8_t * buf = m->GetPayload ();
size_t len = be16toh (m->GetHeader ()->size); size_t len = m->GetSize ();
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf; uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = sizeof (I2NPDatabaseStoreMsg); size_t offset = DATABASE_STORE_HEADER_SIZE;
if (msg->replyToken) if (replyToken)
offset += 36; offset += 36;
if (msg->type) if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
{ {
LogPrint ("LeaseSet"); LogPrint ("LeaseSet");
AddLeaseSet (msg->key, buf + offset, len - offset, m->from); AddLeaseSet (buf + DATABASE_STORE_KEY_OFFSET, buf + offset, len - offset, m->from);
} }
else else
{ {
LogPrint ("RouterInfo"); LogPrint ("RouterInfo");
size_t size = be16toh (*(uint16_t *)(buf + offset)); size_t size = bufbe16toh (buf + offset);
if (size > 2048) if (size > 2048)
{ {
LogPrint ("Invalid RouterInfo length ", (int)size); LogPrint ("Invalid RouterInfo length ", (int)size);
@@ -484,7 +450,7 @@ namespace data
uint8_t uncompressed[2048]; uint8_t uncompressed[2048];
size_t uncomressedSize = decompressor.MaxRetrievable (); size_t uncomressedSize = decompressor.MaxRetrievable ();
decompressor.Get (uncompressed, uncomressedSize); decompressor.Get (uncompressed, uncomressedSize);
AddRouterInfo (msg->key, uncompressed, uncomressedSize); AddRouterInfo (buf + DATABASE_STORE_KEY_OFFSET, uncompressed, uncomressedSize);
} }
i2p::DeleteI2NPMessage (m); i2p::DeleteI2NPMessage (m);
} }
@@ -504,9 +470,9 @@ namespace data
bool deleteDest = true; bool deleteDest = true;
if (num > 0) if (num > 0)
{ {
auto pool = dest ? dest->GetTunnelPool () : nullptr; auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool ? pool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel (); auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool ? pool->GetNextInboundTunnel () : i2p::tunnel::tunnels.GetNextInboundTunnel (); auto inbound = pool->GetNextInboundTunnel ();
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
if (!dest->IsExploratory ()) if (!dest->IsExploratory ())
{ {
@@ -518,17 +484,14 @@ namespace data
{ {
auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
if (nextFloodfill) if (nextFloodfill)
{ {
if (!dest->IsLeaseSet ()) // tell floodfill about us
{ msgs.push_back (i2p::tunnel::TunnelMessageBlock
// tell floodfill about us {
msgs.push_back (i2p::tunnel::TunnelMessageBlock i2p::tunnel::eDeliveryTypeRouter,
{ nextFloodfill->GetIdentHash (), 0,
i2p::tunnel::eDeliveryTypeRouter, CreateDatabaseStoreMsg ()
nextFloodfill->GetIdentHash (), 0, });
CreateDatabaseStoreMsg ()
});
}
// request destination // request destination
LogPrint ("Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ()); LogPrint ("Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ());
@@ -563,7 +526,7 @@ namespace data
LogPrint ("Found new/outdated router. Requesting RouterInfo ..."); LogPrint ("Found new/outdated router. Requesting RouterInfo ...");
if (outbound && inbound && dest->GetLastRouter ()) if (outbound && inbound && dest->GetLastRouter ())
{ {
RequestedDestination * d1 = CreateRequestedDestination (router, false, false, pool); RequestedDestination * d1 = CreateRequestedDestination (router, false);
auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), inbound); auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), inbound);
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
{ {
@@ -572,7 +535,7 @@ namespace data
}); });
} }
else else
RequestDestination (router, false, pool); RequestDestination (router);
} }
else else
LogPrint ("Bayan"); LogPrint ("Bayan");
@@ -585,7 +548,7 @@ namespace data
{ {
// request router // request router
LogPrint ("Found new floodfill. Request it"); LogPrint ("Found new floodfill. Request it");
RequestDestination (router, false, pool); RequestDestination (router);
} }
} }
} }
@@ -635,10 +598,10 @@ namespace data
uint32_t replyTunnelID = 0; uint32_t replyTunnelID = 0;
if (flag & 0x01) //reply to tunnel if (flag & 0x01) //reply to tunnel
{ {
replyTunnelID = be32toh (*(uint32_t *)(buf + 64)); replyTunnelID = bufbe32toh (buf + 64);
excluded += 4; excluded += 4;
} }
uint16_t numExcluded = be16toh (*(uint16_t *)excluded); uint16_t numExcluded = bufbe16toh (excluded);
excluded += 2; excluded += 2;
if (numExcluded > 512) if (numExcluded > 512)
{ {
@@ -713,18 +676,6 @@ namespace data
void NetDb::Explore (int numDestinations) 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 // new requests
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel (); auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
@@ -739,7 +690,7 @@ namespace data
for (int i = 0; i < numDestinations; i++) for (int i = 0; i < numDestinations; i++)
{ {
rnd.GenerateBlock (randomHash, 32); rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true, exploratoryPool); RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), true);
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
{ {
@@ -784,14 +735,13 @@ namespace data
} }
} }
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest, RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest, bool isExploratory)
bool isLeaseSet, bool isExploratory, i2p::tunnel::TunnelPool * pool)
{ {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (dest); auto it = m_RequestedDestinations.find (dest);
if (it == m_RequestedDestinations.end ()) // not exist yet 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; m_RequestedDestinations[dest] = d;
return d; return d;
} }
@@ -918,5 +868,54 @@ namespace data
it++; 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: public:
RequestedDestination (const IdentHash& destination, bool isLeaseSet, RequestedDestination (const IdentHash& destination, bool isExploratory = false):
bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr): m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory),
m_Pool (pool), m_CreationTime (0) {};
const IdentHash& GetDestination () const { return m_Destination; }; const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; }; const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers (); void ClearExcludedPeers ();
std::shared_ptr<const RouterInfo> GetLastRouter () const { return m_LastRouter; }; std::shared_ptr<const RouterInfo> GetLastRouter () const { return m_LastRouter; };
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
bool IsExploratory () const { return m_IsExploratory; }; bool IsExploratory () const { return m_IsExploratory; };
bool IsLeaseSet () const { return m_IsLeaseSet; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
uint64_t GetCreationTime () const { return m_CreationTime; }; uint64_t GetCreationTime () const { return m_CreationTime; };
I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, const i2p::tunnel::InboundTunnel * replyTunnel); I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, const i2p::tunnel::InboundTunnel * replyTunnel);
@@ -45,8 +41,7 @@ namespace data
private: private:
IdentHash m_Destination; IdentHash m_Destination;
bool m_IsLeaseSet, m_IsExploratory; bool m_IsExploratory;
i2p::tunnel::TunnelPool * m_Pool;
std::set<IdentHash> m_ExcludedPeers; std::set<IdentHash> m_ExcludedPeers;
std::shared_ptr<const RouterInfo> m_LastRouter; std::shared_ptr<const RouterInfo> m_LastRouter;
uint64_t m_CreationTime; uint64_t m_CreationTime;
@@ -68,8 +63,7 @@ namespace data
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const; std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
LeaseSet * FindLeaseSet (const IdentHash& destination) const; LeaseSet * FindLeaseSet (const IdentHash& destination) const;
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false, void RequestDestination (const IdentHash& destination);
i2p::tunnel::TunnelPool * pool = nullptr);
void HandleDatabaseStoreMsg (I2NPMessage * msg); void HandleDatabaseStoreMsg (I2NPMessage * msg);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg); void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
@@ -94,12 +88,12 @@ namespace data
void Load (const char * directory); void Load (const char * directory);
void SaveUpdated (const char * directory); void SaveUpdated (const char * directory);
void Run (); // exploratory thread void Run (); // exploratory thread
void Explore (int numDestinations); void Explore (int numDestinations);
void Publish (); void Publish ();
void ManageLeaseSets (); void ManageLeaseSets ();
void ManageRequests ();
RequestedDestination * CreateRequestedDestination (const IdentHash& dest, RequestedDestination * CreateRequestedDestination (const IdentHash& dest, bool isExploratory = false);
bool isLeaseSet, bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr);
bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found
void DeleteRequestedDestination (RequestedDestination * dest); 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 x64 - [![Build Status](https://jenkins.nordcloud.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/)
- Linux ARM - To be added - 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 - Microsoft VC13 - To be added

View File

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

72
SAM.cpp
View File

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

21
SAM.h
View File

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

632
SOCKS.cpp
View File

@@ -3,281 +3,447 @@
#include "NetDb.h" #include "NetDb.h"
#include "Destination.h" #include "Destination.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "I2PEndian.h"
#include <cstring> #include <cstring>
#include <stdexcept> #include <cassert>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
namespace i2p namespace i2p
{ {
namespace proxy namespace proxy
{ {
const uint8_t socks_leaseset_timeout = 10; void SOCKSHandler::AsyncSockRead()
const uint8_t socks_timeout = 60;
void SOCKS4AHandler::AsyncSockRead()
{ {
LogPrint("--- socks4a async sock read"); LogPrint(eLogDebug,"--- SOCKS async sock read");
if(m_sock) { if(m_sock) {
if (m_state == INITIAL) { m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), std::bind(&SOCKSHandler::HandleSockRecv, this,
boost::bind(&SOCKS4AHandler::HandleSockRecv, this, std::placeholders::_1, std::placeholders::_2));
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));
}
} else { } else {
LogPrint("--- socks4a no socket for read"); LogPrint(eLogError,"--- SOCKS 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");
} }
} }
void SOCKS4AHandler::Terminate() { void SOCKSHandler::Done() {
CloseStream(); if (m_parent) m_parent->RemoveHandler (shared_from_this ());
CloseSock();
delete this; // ew
}
void SOCKS4AHandler::SocksFailed()
{
LogPrint("--- socks4a failed");
m_sock->send(boost::asio::buffer("\x00\x5b 12345"));
Terminate();
} }
void SOCKS4AHandler::CloseSock() void SOCKSHandler::Terminate() {
{ if (dead.exchange(true)) return;
if (m_sock) { if (m_sock) {
LogPrint("--- socks4a close sock"); LogPrint(eLogDebug,"--- SOCKS close sock");
m_sock->close(); m_sock->close();
delete m_sock; delete m_sock;
m_sock = nullptr; m_sock = nullptr;
} }
}
void SOCKS4AHandler::CloseStream()
{
if (m_stream) { if (m_stream) {
LogPrint("--- socks4a close stream"); LogPrint(eLogDebug,"--- SOCKS close stream");
m_stream.reset (); m_stream.reset ();
} }
Done();
} }
const size_t socks_hostname_size = 1024; boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
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)
{ {
if(ecode) { assert(error >= SOCKS4_OK);
LogPrint("--- socks4a forward got error: ", ecode); m_response[0] = '\x00'; //Version
Terminate(); 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; 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) { if(ecode) {
LogPrint(" --- sock recv got error: ", ecode); LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode);
Terminate(); Terminate();
return; return;
} }
if (m_state == INITIAL) { if (HandleData(m_sock_buff, len)) {
if (m_state == DONE) {
char hostbuff[socks_hostname_size]; LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
char identbuff[socks_ident_size]; m_parent->GetLocalDestination ()->CreateStream (
std::memset(hostbuff, 0, sizeof(hostbuff)); std::bind (&SOCKSHandler::HandleStreamRequestComplete,
std::memset(identbuff, 0, sizeof(hostbuff)); this, std::placeholders::_1), m_address.dns.ToString(), m_port);
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));
} else { } else {
ConnectionSuccess(); AsyncSockRead();
}
} 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());
} }
} }
}
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() void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
{
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)
{ {
if (!ecode) { if (!ecode) {
LogPrint("--- socks4a accepted"); Terminate();
new SOCKS4AHandler(&m_ios, m_new_sock); } else {
Accept(); 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__ #ifndef SOCKS_H__
#define SOCKS4A_H__ #define SOCKS_H__
#include <thread>
#include <boost/asio.hpp>
#include <vector>
#include <mutex>
#include <memory> #include <memory>
#include <string>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include <atomic>
#include "Identity.h" #include "Identity.h"
#include "Streaming.h" #include "Streaming.h"
#include "I2PTunnel.h"
namespace i2p namespace i2p
{ {
@@ -15,83 +17,151 @@ namespace proxy
{ {
const size_t socks_buffer_size = 8192; 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 { struct SOCKSDnsAddress {
uint8_t size;
private: char value[max_socks_hostname_size];
enum state { void FromString (std::string str) {
INITIAL, size = str.length();
OKAY, if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
END memcpy(value,str.c_str(),size);
}; }
std::string ToString() { return std::string(value, size); }
void GotClientRequest(boost::system::error_code & ecode, std::string & host, uint16_t port); void push_back (char c) { value[size++] = c; }
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; }
}; };
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: public:
SOCKS4AServer(int port) : m_run(false), SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
m_thread(nullptr), m_parent(parent), m_sock(sock), m_stream(nullptr),
m_work(m_ios), m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), dead(false)
m_acceptor(m_ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), { m_address.ip = 0; AsyncSockRead(); EnterState(GET_SOCKSV); }
m_new_sock(nullptr) { } ~SOCKSHandler() { Terminate(); }
~SOCKS4AServer() { Stop(); } };
void Start();
void Stop(); class SOCKSServer: public i2p::client::I2PTunnel
{
boost::asio::io_service& GetService () { return m_ios; }; 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: private:
void Run();
void Accept(); 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; public:
std::thread * m_thread; SOCKSServer(int port) : I2PTunnel(nullptr),
boost::asio::io_service m_ios; m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
boost::asio::io_service::work m_work; m_Timer (GetService ()) {};
boost::asio::ip::tcp::acceptor m_acceptor; ~SOCKSServer() { Stop(); }
boost::asio::ip::tcp::socket * m_new_sock;
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; uint8_t numAcks =*buf;
buf++; buf++;
for (int i = 0; i < numAcks; i++) for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (be32toh (((uint32_t *)buf)[i])); ProcessSentMessageAck (bufbe32toh (buf+i*4));
buf += numAcks*4; buf += numAcks*4;
} }
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
@@ -94,7 +94,7 @@ namespace transport
buf++; buf++;
for (int i = 0; i < numBitfields; i++) for (int i = 0; i < numBitfields; i++)
{ {
uint32_t msgID = be32toh (*(uint32_t *)buf); uint32_t msgID = bufbe32toh (buf);
buf += 4; // msgID buf += 4; // msgID
auto it = m_SentMessages.find (msgID); auto it = m_SentMessages.find (msgID);
// process individual Ack bitfields // process individual Ack bitfields
@@ -137,13 +137,13 @@ namespace transport
buf++; buf++;
for (int i = 0; i < numFragments; i++) 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; buf += 4;
uint8_t frag[4]; uint8_t frag[4];
frag[0] = 0; frag[0] = 0;
memcpy (frag + 1, buf, 3); memcpy (frag + 1, buf, 3);
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 uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16 bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
@@ -168,7 +168,7 @@ namespace transport
{ {
// create new message // create new message
msg = NewI2NPMessage (); msg = NewI2NPMessage ();
msg->len -= sizeof (I2NPHeaderShort); msg->len -= I2NP_SHORT_HEADER_SIZE;
incompleteMessage = new IncompleteMessage (msg); incompleteMessage = new IncompleteMessage (msg);
m_IncomleteMessages[msgID] = incompleteMessage; m_IncomleteMessages[msgID] = incompleteMessage;
} }
@@ -246,13 +246,13 @@ namespace transport
else else
{ {
// we expect DeliveryStatus // we expect DeliveryStatus
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus) if (msg->GetTypeID () == eI2NPDeliveryStatus)
{ {
LogPrint ("SSU session established"); LogPrint ("SSU session established");
m_Session.Established (); m_Session.Established ();
} }
else else
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetHeader ()->typeID); LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ());
DeleteI2NPMessage (msg); 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) 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; SSUHeader * header = (SSUHeader *)buf;
switch (header->GetPayloadType ()) switch (header->GetPayloadType ())
{ {
@@ -210,7 +211,7 @@ namespace transport
} }
s.Insert (ourAddress, addressSize); // our IP s.Insert (ourAddress, addressSize); // our IP
payload += addressSize; // address payload += addressSize; // address
uint16_t ourPort = be16toh (*(uint16_t *)payload); uint16_t ourPort = bufbe16toh (payload);
s.Insert (payload, 2); // our port s.Insert (payload, 2); // our port
payload += 2; // port payload += 2; // port
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); 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 (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP v6
s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port
s.Insert (payload, 8); // relayTag and signed on time s.Insert (payload, 8); // relayTag and signed on time
m_RelayTag = be32toh (*(uint32_t *)payload); m_RelayTag = bufbe32toh (payload);
payload += 4; // relayTag payload += 4; // relayTag
payload += 4; // signed on time payload += 4; // signed on time
// decrypt signature // decrypt signature
size_t signatureLen = m_RemoteIdentity.GetSignatureLen (); size_t signatureLen = m_RemoteIdentity.GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16 size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize); 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.SetIV (((SSUHeader *)buf)->iv);
m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload);
// verify // verify
@@ -242,7 +244,7 @@ namespace transport
LogPrint (eLogDebug, "Session confirmed received"); LogPrint (eLogDebug, "Session confirmed received");
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
payload++; // identity fragment info payload++; // identity fragment info
uint16_t identitySize = be16toh (*(uint16_t *)payload); uint16_t identitySize = bufbe16toh (payload);
payload += 2; // size of identity fragment payload += 2; // size of identity fragment
m_RemoteIdentity.FromBuffer (payload, identitySize); m_RemoteIdentity.FromBuffer (payload, identitySize);
m_Data.UpdatePacketSize (m_RemoteIdentity.GetIdentHash ()); m_Data.UpdatePacketSize (m_RemoteIdentity.GetIdentHash ());
@@ -299,18 +301,18 @@ namespace transport
uint8_t buf[96 + 18]; uint8_t buf[96 + 18];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*(uint32_t *)payload = htobe32 (iTag); htobe32buf (payload, iTag);
payload += 4; payload += 4;
*payload = 0; // no address *payload = 0; // no address
payload++; payload++;
*(uint16_t *)payload = 0; // port = 0 htobuf16(payload, 0); // port = 0
payload += 2; payload += 2;
*payload = 0; // challenge *payload = 0; // challenge
payload++; payload++;
memcpy (payload, (const uint8_t *)address->key, 32); memcpy (payload, (const uint8_t *)address->key, 32);
payload += 32; payload += 32;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
*(uint32_t *)payload = htobe32 (rnd.GenerateWord32 ()); // nonce htobe32buf (payload, rnd.GenerateWord32 ()); // nonce
uint8_t iv[16]; uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
@@ -358,7 +360,7 @@ namespace transport
s.Insert (payload, 16); // remote endpoint IP V6 s.Insert (payload, 16); // remote endpoint IP V6
payload += 16; payload += 16;
} }
*(uint16_t *)(payload) = htobe16 (m_RemoteEndpoint.port ()); htobe16buf (payload, m_RemoteEndpoint.port ());
s.Insert (payload, 2); // remote port s.Insert (payload, 2); // remote port
payload += 2; payload += 2;
if (address->host.is_v4 ()) if (address->host.is_v4 ())
@@ -373,9 +375,9 @@ namespace transport
if (!relayTag) relayTag = 1; if (!relayTag) relayTag = 1;
m_Server.AddRelay (relayTag, m_RemoteEndpoint); m_Server.AddRelay (relayTag, m_RemoteEndpoint);
} }
*(uint32_t *)(payload) = htobe32 (relayTag); htobe32buf (payload, relayTag);
payload += 4; // relay tag payload += 4; // relay tag
*(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time
payload += 4; payload += 4;
s.Insert (payload - 8, 8); // relayTag and signed on time s.Insert (payload - 8, 8); // relayTag and signed on time
s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature
@@ -404,12 +406,12 @@ namespace transport
*payload = 1; // 1 fragment *payload = 1; // 1 fragment
payload++; // info payload++; // info
size_t identLen = i2p::context.GetIdentity ().GetFullLen (); // 387+ bytes size_t identLen = i2p::context.GetIdentity ().GetFullLen (); // 387+ bytes
*(uint16_t *)(payload) = htobe16 (identLen); htobe16buf (payload, identLen);
payload += 2; // cursize payload += 2; // cursize
i2p::context.GetIdentity ().ToBuffer (payload, identLen); i2p::context.GetIdentity ().ToBuffer (payload, identLen);
payload += identLen; payload += identLen;
uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch (); uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch ();
*(uint32_t *)(payload) = htobe32 (signedOnTime); // signed on time htobe32buf (payload, signedOnTime); // signed on time
payload += 4; payload += 4;
auto signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); auto signatureLen = i2p::context.GetIdentity ().GetSignatureLen ();
size_t paddingSize = ((payload - buf) + signatureLen)%16; 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) 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); auto session = m_Server.FindRelaySession (relayTag);
if (session) if (session)
{ {
@@ -457,7 +459,7 @@ namespace transport
buf += challengeSize; buf += challengeSize;
uint8_t * introKey = buf; uint8_t * introKey = buf;
buf += 32; // introkey buf += 32; // introkey
uint32_t nonce = be32toh (*(uint32_t *)buf); uint32_t nonce = bufbe32toh (buf);
SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint); SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint);
SendRelayIntro (session.get (), from); SendRelayIntro (session.get (), from);
} }
@@ -476,9 +478,9 @@ namespace transport
} }
*payload = 4; *payload = 4;
payload++; // size 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 payload += 4; // address
*(uint16_t *)payload = htobe16 (to.port ()); // Charlie's port htobe16buf (payload, to.port ()); // Charlie's port
payload += 2; // port payload += 2; // port
// Alice // Alice
bool isV4 = from.address ().is_v4 (); // Alice's 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 memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6
payload += 16; // address payload += 16; // address
} }
*(uint16_t *)payload = htobe16 (from.port ()); // Alice's port htobe16buf (payload, from.port ()); // Alice's port
payload += 2; // port payload += 2; // port
*(uint32_t *)payload = htobe32 (nonce); htobe32buf (payload, nonce);
if (m_State == eSessionStateEstablished) if (m_State == eSessionStateEstablished)
{ {
@@ -531,9 +533,9 @@ namespace transport
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 4; *payload = 4;
payload++; // size 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 payload += 4; // address
*(uint16_t *)payload = htobe16 (from.port ()); // Alice's port htobe16buf (payload, from.port ()); // Alice's port
payload += 2; // port payload += 2; // port
*payload = 0; // challenge size *payload = 0; // challenge size
uint8_t iv[16]; uint8_t iv[16];
@@ -550,9 +552,9 @@ namespace transport
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t remoteSize = *payload; uint8_t remoteSize = *payload;
payload++; // remote size 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 payload += remoteSize; // remote address
//uint16_t remotePort = be16toh (*(uint16_t *)(payload)); //uint16_t remotePort = bufbe16toh (payload);
payload += 2; // remote port payload += 2; // remote port
uint8_t ourSize = *payload; uint8_t ourSize = *payload;
payload++; // our size payload++; // our size
@@ -570,7 +572,7 @@ namespace transport
ourIP = boost::asio::ip::address_v6 (bytes); ourIP = boost::asio::ip::address_v6 (bytes);
} }
payload += ourSize; // our address payload += ourSize; // our address
uint16_t ourPort = be16toh (*(uint16_t *)(payload)); uint16_t ourPort = bufbe16toh (payload);
payload += 2; // our port payload += 2; // our port
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP); i2p::context.UpdateAddress (ourIP);
@@ -582,9 +584,9 @@ namespace transport
if (size == 4) if (size == 4)
{ {
buf++; // size buf++; // size
boost::asio::ip::address_v4 address (be32toh (*(uint32_t* )buf)); boost::asio::ip::address_v4 address (bufbe32toh (buf));
buf += 4; // address buf += 4; // address
uint16_t port = be16toh (*(uint16_t *)buf); uint16_t port = bufbe16toh (buf);
// send hole punch of 1 byte // send hole punch of 1 byte
m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (address, port)); 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); LogPrint (eLogError, "Unexpected SSU packet length ", len);
return; return;
} }
//TODO: we are using a dirty solution here but should work for now
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
memcpy (header->iv, iv, 16); memcpy (header->iv, iv, 16);
header->flag = payloadType << 4; // MSB is 0 header->flag = payloadType << 4; // MSB is 0
header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ()); htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ());
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
i2p::crypto::CBCEncryption encryption; i2p::crypto::CBCEncryption encryption;
@@ -612,7 +615,7 @@ namespace transport
encryption.Encrypt (encrypted, encryptedLen, encrypted); encryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more // assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, iv, 16); 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); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
} }
@@ -623,17 +626,18 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len); LogPrint (eLogError, "Unexpected SSU packet length ", len);
return; return;
} }
//TODO: we are using a dirty solution here but should work for now
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv
m_SessionKeyEncryption.SetIV (header->iv); m_SessionKeyEncryption.SetIV (header->iv);
header->flag = payloadType << 4; // MSB is 0 header->flag = payloadType << 4; // MSB is 0
header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ()); htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ());
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more // assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16); 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); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
} }
@@ -644,6 +648,7 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len); LogPrint (eLogError, "Unexpected SSU packet length ", len);
return; 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; SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
@@ -660,6 +665,7 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len); LogPrint (eLogError, "Unexpected SSU packet length ", len);
return; 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; SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
@@ -677,12 +683,13 @@ namespace transport
LogPrint (eLogError, "Unexpected SSU packet length ", len); LogPrint (eLogError, "Unexpected SSU packet length ", len);
return false; 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; SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
// assume actual buffer size is 18 (16 + 2) bytes more // assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16); memcpy (buf + len, header->iv, 16);
*(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen); htobe16buf (buf + len + 16, encryptedLen);
uint8_t digest[16]; uint8_t digest[16];
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest);
return !memcmp (header->mac, digest, 16); 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) void SSUSession::ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
uint8_t * buf1 = buf; uint8_t * buf1 = buf;
uint32_t nonce = be32toh (*(uint32_t *)buf); uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce buf += 4; // nonce
uint8_t size = *buf; uint8_t size = *buf;
buf++; // size 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 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 buf += 2; // port
uint8_t * introKey = buf; uint8_t * introKey = buf;
if (port && !address) if (port && !address)
@@ -914,13 +922,13 @@ namespace transport
uint8_t buf[80 + 18]; uint8_t buf[80 + 18];
uint8_t iv[16]; uint8_t iv[16];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*(uint32_t *)payload = htobe32 (nonce); htobe32buf (payload, nonce);
payload += 4; // nonce payload += 4; // nonce
if (address) if (address)
{ {
*payload = 4; *payload = 4;
payload++; // size payload++; // size
*(uint32_t *)payload = htobe32 (address); htobe32buf (payload, address);
payload += 4; // address payload += 4; // address
} }
else else
@@ -928,7 +936,7 @@ namespace transport
*payload = 0; *payload = 0;
payload++; //size payload++; //size
} }
*(uint16_t *)payload = htobe16 (port); htobe16buf (payload, port);
payload += 2; // port payload += 2; // port
memcpy (payload, introKey, 32); // intro key memcpy (payload, introKey, 32); // intro key

View File

@@ -167,7 +167,7 @@ namespace stream
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) 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); LogPrint (eLogDebug, "Max packet size ", maxPacketSize);
optionData += 2; optionData += 2;
} }
@@ -256,19 +256,20 @@ namespace stream
uint8_t * packet = p->GetBuffer (); uint8_t * packet = p->GetBuffer ();
// TODO: implement setters // TODO: implement setters
size_t size = 0; size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID); htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID size += 4; // sendStreamID
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID); htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID size += 4; // receiveStreamID
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++); htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum size += 4; // sequenceNum
if (isNoAck) if (isNoAck)
*(uint32_t *)(packet + size) = htobe32 (m_LastReceivedSequenceNumber); htobe32buf (packet + size, m_LastReceivedSequenceNumber);
else else
*(uint32_t *)(packet + size) = 0; htobuf32 (packet + size, 0);
size += 4; // ack Through size += 4; // ack Through
packet[size] = 0; packet[size] = 0;
size++; // NACK count size++; // NACK count
packet[size] = RESEND_TIMEOUT;
size++; // resend delay size++; // resend delay
if (!m_IsOpen) if (!m_IsOpen)
{ {
@@ -277,15 +278,15 @@ namespace stream
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
if (isNoAck) flags |= PACKET_FLAG_NO_ACK; if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
*(uint16_t *)(packet + size) = htobe16 (flags); htobe16buf (packet + size, flags);
size += 2; // flags size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen (); size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); 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 size += 2; // options size
m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen); m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen);
size += identityLen; // from size += identityLen; // from
*(uint16_t *)(packet + size) = htobe16 (STREAMING_MTU); htobe16buf (packet + size, STREAMING_MTU);
size += 2; // max packet size size += 2; // max packet size
uint8_t * signature = packet + size; // set it later uint8_t * signature = packet + size; // set it later
memset (signature, 0, signatureLen); // zeroes for now memset (signature, 0, signatureLen); // zeroes for now
@@ -301,9 +302,9 @@ namespace stream
else else
{ {
// follow on packet // follow on packet
*(uint16_t *)(packet + size) = 0; htobuf16 (packet + size, 0);
size += 2; // flags size += 2; // flags
*(uint16_t *)(packet + size) = 0; // no options htobuf16 (packet + size, 0); // no options
size += 2; // options size size += 2; // options size
size_t sentLen = STREAMING_MTU - size; size_t sentLen = STREAMING_MTU - size;
if (len < sentLen) sentLen = len; if (len < sentLen) sentLen = len;
@@ -338,26 +339,26 @@ namespace stream
Packet p; Packet p;
uint8_t * packet = p.GetBuffer (); uint8_t * packet = p.GetBuffer ();
size_t size = 0; size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID); htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID size += 4; // sendStreamID
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID); htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID 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 size += 4; // sequenceNum
*(uint32_t *)(packet + size) = htobe32 (lastReceivedSeqn); htobe32buf (packet + size, lastReceivedSeqn);
size += 4; // ack Through size += 4; // ack Through
uint8_t numNacks = 0;
if (lastReceivedSeqn > m_LastReceivedSequenceNumber) if (lastReceivedSeqn > m_LastReceivedSequenceNumber)
{ {
// fill NACKs // fill NACKs
uint8_t * nacks = packet + size + 1; uint8_t * nacks = packet + size + 1;
uint8_t numNacks = 0;
auto nextSeqn = m_LastReceivedSequenceNumber + 1; auto nextSeqn = m_LastReceivedSequenceNumber + 1;
for (auto it: m_SavedPackets) for (auto it: m_SavedPackets)
{ {
auto seqn = it->GetSeqn (); auto seqn = it->GetSeqn ();
for (uint32_t i = nextSeqn; i < seqn; i++) for (uint32_t i = nextSeqn; i < seqn; i++)
{ {
*(uint32_t *)nacks = htobe32 (i); htobe32buf (nacks, i);
nacks += 4; nacks += 4;
numNacks++; numNacks++;
} }
@@ -374,14 +375,14 @@ namespace stream
size++; // NACK count size++; // NACK count
} }
size++; // resend delay size++; // resend delay
*(uint16_t *)(packet + size) = 0; // nof flags set htobuf16 (packet + size, 0); // nof flags set
size += 2; // flags size += 2; // flags
*(uint16_t *)(packet + size) = 0; // no options htobuf16 (packet + size, 0); // no options
size += 2; // options size size += 2; // options size
p.len = size; p.len = size;
SendPackets (std::vector<Packet *> { &p }); SendPackets (std::vector<Packet *> { &p });
LogPrint ("Quick Ack sent"); LogPrint ("Quick Ack sent. ", (int)numNacks, " NACKs");
} }
void Stream::Close () void Stream::Close ()
@@ -392,21 +393,21 @@ namespace stream
Packet * p = new Packet (); Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer (); uint8_t * packet = p->GetBuffer ();
size_t size = 0; size_t size = 0;
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID); htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID size += 4; // sendStreamID
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID); htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID size += 4; // receiveStreamID
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++); htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum size += 4; // sequenceNum
*(uint32_t *)(packet + size) = htobe32 (m_LastReceivedSequenceNumber); htobe32buf (packet + size, m_LastReceivedSequenceNumber);
size += 4; // ack Through size += 4; // ack Through
packet[size] = 0; packet[size] = 0;
size++; // NACK count size++; // NACK count
size++; // resend delay 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 += 2; // flags
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); 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 size += 2; // options size
uint8_t * signature = packet + size; uint8_t * signature = packet + size;
memset (packet + size, 0, signatureLen); memset (packet + size, 0, signatureLen);
@@ -617,11 +618,11 @@ namespace stream
compressor.MessageEnd(); compressor.MessageEnd();
int size = compressor.MaxRetrievable (); int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
*(uint32_t *)buf = htobe32 (size); // length htobe32buf (buf, size); // length
buf += 4; buf += 4;
compressor.Get (buf, size); compressor.Get (buf, size);
*(uint16_t *)(buf + 4) = 0; // source port htobuf16(buf + 4, 0); // source port
*(uint16_t *)(buf + 6) = htobe16 (m_Port); // destination port htobe16buf (buf + 6, m_Port); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
msg->len += size + 4; msg->len += size + 4;
FillI2NPMessageHeader (msg, eI2NPData); FillI2NPMessageHeader (msg, eI2NPData);
@@ -641,7 +642,7 @@ namespace stream
m_Streams.clear (); m_Streams.clear ();
} }
} }
void StreamingDestination::HandleNextPacket (Packet * packet) void StreamingDestination::HandleNextPacket (Packet * packet)
{ {
uint32_t sendStreamID = packet->GetSendStreamID (); uint32_t sendStreamID = packet->GetSendStreamID ();
@@ -652,21 +653,38 @@ namespace stream
it->second->HandleNextPacket (packet); it->second->HandleNextPacket (packet);
else else
{ {
LogPrint ("Unknown stream ", sendStreamID); LogPrint ("Unknown stream sendStreamID=", sendStreamID);
delete packet; delete packet;
} }
} }
else // new incoming stream else
{ {
auto incomingStream = CreateNewIncomingStream (); if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
incomingStream->HandleNextPacket (packet); {
if (m_Acceptor != nullptr) auto incomingStream = CreateNewIncomingStream ();
m_Acceptor (incomingStream); incomingStream->HandleNextPacket (packet);
else 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"); uint32_t receiveStreamID = packet->GetReceiveStreamID ();
DeleteStream (incomingStream); 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; }; uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; }; size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return be32toh (*(uint32_t *)buf); }; uint32_t GetSendStreamID () const { return bufbe32toh (buf); };
uint32_t GetReceiveStreamID () const { return be32toh (*(uint32_t *)(buf + 4)); }; uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
uint32_t GetSeqn () const { return be32toh (*(uint32_t *)(buf + 8)); }; uint32_t GetSeqn () const { return bufbe32toh (buf + 8); };
uint32_t GetAckThrough () const { return be32toh (*(uint32_t *)(buf + 12)); }; uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); };
uint8_t GetNACKCount () const { return buf[16]; }; 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 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 GetFlags () const { return bufbe16toh (GetOption () - 2); };
uint16_t GetOptionSize () const { return be16toh (*(uint16_t *)GetOption ()); }; uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); };
const uint8_t * GetOptionData () const { return GetOption () + 2; }; const uint8_t * GetOptionData () const { return GetOption () + 2; };
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); }; const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
@@ -100,8 +100,10 @@ namespace stream
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); 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 Close ();
void Cancel () { m_ReceiveTimer.cancel (); };
size_t GetNumSentBytes () const { return m_NumSentBytes; }; size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
@@ -224,7 +226,7 @@ namespace stream
else else
// socket closed // socket closed
handler (m_IsReset ? boost::asio::error::make_error_code (boost::asio::error::connection_reset) : 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 else
// timeout expired // timeout expired

View File

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

View File

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

View File

@@ -8,6 +8,7 @@
#include "aes.h" #include "aes.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Timestamp.h"
namespace i2p namespace i2p
{ {
@@ -82,6 +83,28 @@ namespace tunnel
isGateway = false; 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 class TunnelConfig

View File

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

View File

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

View File

@@ -232,8 +232,12 @@ namespace tunnel
void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg) void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg)
{ {
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload (); const uint8_t * buf = msg->GetPayload ();
auto it = m_Tests.find (be32toh (deliveryStatus->msgID)); uint32_t msgID = bufbe32toh (buf);
buf += 4;
uint64_t timestamp = bufbe64toh (buf);
auto it = m_Tests.find (msgID);
if (it != m_Tests.end ()) if (it != m_Tests.end ())
{ {
// restore from test failed state if any // restore from test failed state if any
@@ -241,7 +245,7 @@ namespace tunnel
it->second.first->SetState (eTunnelStateEstablished); it->second.first->SetState (eTunnelStateEstablished);
if (it->second.second->GetState () == eTunnelStateTestFailed) if (it->second.second->GetState () == eTunnelStateTestFailed)
it->second.second->SetState (eTunnelStateEstablished); 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); m_Tests.erase (it);
DeleteI2NPMessage (msg); DeleteI2NPMessage (msg);
} }

314
UPnP.cpp
View File

@@ -1,68 +1,270 @@
#ifdef USE_UPNP
#include <string> #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 <boost/bind.hpp>
#include "Log.h" #include "Log.h"
#include "RouterContext.h"
#include "UPnP.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 namespace i2p
{ {
UPnP::UPnP (): m_Timer (m_Service), namespace UPnP
m_Endpoint (boost::asio::ip::udp::v4 (), UPNP_REPLY_PORT), {
m_MulticastEndpoint (boost::asio::ip::address::from_string (UPNP_GROUP), UPNP_PORT), UPnP upnpc;
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 ()
{
}
void UPnP::Run () UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false)
{ {
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));
std::string address = UPNP_GROUP; void UPnP::Stop ()
address += ":" + boost::lexical_cast<std::string>(UPNP_PORT); {
std::string request = "M-SEARCH * HTTP/1.1\r\n" if (m_Thread)
"HOST: " + address + "\r\n" {
"ST:" + UPNP_ROUTER + "\r\n" m_Thread->join ();
"MAN:\"ssdp:discover\"\r\n" delete m_Thread;
"MX:3\r\n" m_Thread = nullptr;
"\r\n\r\n"; }
m_Socket.send_to (boost::asio::buffer (request.c_str (), request.length ()), m_MulticastEndpoint); }
Receive ();
}
void UPnP::Receive () void UPnP::Start()
{ {
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, UPNP_MAX_PACKET_LEN), m_SenderEndpoint, m_Thread = new std::thread (std::bind (&UPnP::Run, this));
boost::bind (&UPnP::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); }
}
UPnP::~UPnP ()
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); void UPnP::Run ()
LogPrint (str); {
m_Timer.cancel (); #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__ #ifndef __UPNP_H__
#define 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 <boost/asio.hpp>
#include "util.h"
#define I2P_UPNP_TCP 1
#define I2P_UPNP_UDP 2
namespace i2p namespace i2p
{ {
const int UPNP_MAX_PACKET_LEN = 1500; namespace UPnP
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";
class 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 (); std::thread * m_Thread;
void Receive (); struct UPNPUrls m_upnpUrls;
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred); struct IGDdatas m_upnpData;
void HandleTimer (const boost::system::error_code& ecode);
private:
boost::asio::io_service m_Service; // For miniupnpc
boost::asio::deadline_timer m_Timer; char * m_MulticastIf = 0;
boost::asio::ip::udp::endpoint m_Endpoint, m_MulticastEndpoint, m_SenderEndpoint; char * m_Minissdpdpath = 0;
boost::asio::ip::udp::socket m_Socket; struct UPNPDev * m_Devlist = 0;
char m_ReceiveBuffer[UPNP_MAX_PACKET_LEN]; 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
#endif

View File

@@ -66,7 +66,7 @@ namespace crypto
"movups %%xmm1, 224(%[sched]) \n" "movups %%xmm1, 224(%[sched]) \n"
: // output : // output
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input : [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" "movups (%[in]), %%xmm0 \n"
EncryptAES256(sched) EncryptAES256(sched)
"movups %%xmm0, (%[out]) \n" "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" "movups (%[in]), %%xmm0 \n"
DecryptAES256(sched) DecryptAES256(sched)
"movups %%xmm0, (%[out]) \n" "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(176)
CallAESIMC(192) CallAESIMC(192)
CallAESIMC(208) 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) void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote)
{ {
if (dest) 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) 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" ) project ( "i2pd" )
# configurale options # configurale options
option(WITH_AESNI "Use AES-NI instructions set" OFF) option(WITH_AESNI "Use AES-NI instructions set" OFF)
option(WITH_HARDENING "Use hardening compiler flags" OFF) option(WITH_HARDENING "Use hardening compiler flags" OFF)
option(WITH_LIBRARY "Build library" ON) option(WITH_LIBRARY "Build library" ON)
option(WITH_BINARY "Build binary" ON)
option(WITH_STATIC "Static build" OFF) option(WITH_STATIC "Static build" OFF)
# paths # paths
@@ -140,25 +141,32 @@ message(STATUS "Options:")
message(STATUS " AESNI : ${WITH_AESNI}") message(STATUS " AESNI : ${WITH_AESNI}")
message(STATUS " HARDENING : ${WITH_HARDENING}") message(STATUS " HARDENING : ${WITH_HARDENING}")
message(STATUS " LIBRARY : ${WITH_LIBRARY}") message(STATUS " LIBRARY : ${WITH_LIBRARY}")
message(STATUS " BINARY : ${WITH_BINARY}")
message(STATUS " STATIC BUILD : ${WITH_STATIC}") message(STATUS " STATIC BUILD : ${WITH_STATIC}")
message(STATUS "---------------------------------------") 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") if (WITH_BINARY)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-z relro -z now" ) 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 () 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) if (WITH_LIBRARY)
add_library("lib${PROJECT_NAME}" SHARED ${COMMON_SRC} ${LIBRARY_SRC}) add_library(${PROJECT_NAME} SHARED ${COMMON_SRC} ${LIBRARY_SRC})
install(TARGETS "lib${PROJECT_NAME}" LIBRARY DESTINATION "lib") install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
endif () endif ()

View File

@@ -5,6 +5,16 @@ COMMON_SRC = \
TransitTunnel.cpp Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp \ TransitTunnel.cpp Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp \
TunnelGateway.cpp Destination.cpp util.cpp aes.cpp base64.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 # also: Daemon{Linux,Win32}.cpp will be added later
DAEMON_SRC = $(COMMON_SRC) \ DAEMON_SRC = $(COMMON_SRC) \
BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp SAM.cpp SOCKS.cpp UPnP.cpp \ BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp SAM.cpp SOCKS.cpp UPnP.cpp \

View File

@@ -239,7 +239,7 @@ namespace http
if (site) if (site)
{ {
// User-Agent is needed to get the server list routerInfo files. // 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"; << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
// read response // read response
std::string version, statusMessage; std::string version, statusMessage;
@@ -249,10 +249,25 @@ namespace http
std::getline (site, statusMessage); std::getline (site, statusMessage);
if (status == 200) // OK if (status == 200) // OK
{ {
bool isChunked = false;
std::string header; 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; std::stringstream ss;
ss << site.rdbuf(); if (isChunked)
MergeChunkedResponse (site, ss);
else
ss << site.rdbuf();
return ss.str(); return ss.str();
} }
else 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) int httpRequestViaI2pProxy(const std::string& address, std::string &content)
{ {
content = ""; content = "";

8
util.h
View File

@@ -3,6 +3,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <iostream>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@@ -39,7 +40,14 @@ namespace util
namespace http 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); 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 int httpRequestViaI2pProxy(const std::string& address, std::string &content); // return http code
struct url { struct url {

View File

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