This commit is contained in:
chertov 2014-04-14 10:15:43 +04:00
commit 8a42ca9d64
7 changed files with 324 additions and 261 deletions

View File

@ -43,7 +43,7 @@ void AddressBook::LoadHostsFromI2P ()
content = i2p::util::http::httpRequest(url_ss.str()); content = i2p::util::http::httpRequest(url_ss.str());
// TODO: check http errors // TODO: check http errors
if (! boost::starts_with(content, "<html>")) if (! boost::starts_with(content, "<html>") && content.size() > 0)
break; break;
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(5));
} }

335
SSU.cpp
View File

@ -19,7 +19,7 @@ namespace ssu
const i2p::data::RouterInfo * router, bool peerTest ): const i2p::data::RouterInfo * router, bool peerTest ):
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router), m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router),
m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), m_State (eSessionStateUnknown), m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), m_State (eSessionStateUnknown),
m_RelayTag (0) m_IsSessionKey (false), m_RelayTag (0)
{ {
m_DHKeysPair = i2p::transports.GetNextDHKeysPair (); m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
} }
@ -27,9 +27,15 @@ namespace ssu
SSUSession::~SSUSession () SSUSession::~SSUSession ()
{ {
delete m_DHKeysPair; delete m_DHKeysPair;
for (auto it: m_IncomleteMessages)
if (it.second)
{
DeleteI2NPMessage (it.second->msg);
delete it.second;
}
} }
void SSUSession::CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey) void SSUSession::CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey)
{ {
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength()); CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength());
@ -50,53 +56,55 @@ namespace ssu
memcpy (aesKey, secretKey, 32); memcpy (aesKey, secretKey, 32);
memcpy (macKey, secretKey + 32, 32); memcpy (macKey, secretKey + 32, 32);
} }
m_IsSessionKey = true;
} }
void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
switch (m_State) if (m_State == eSessionStateIntroduced)
{ {
case eSessionStateConfirmedSent:
case eSessionStateEstablished:
// most common case
ScheduleTermination ();
ProcessMessage (buf, len, senderEndpoint);
break;
// establishing or testing
case eSessionStateUnknown:
case eSessionStateRequestSent:
// we must use intro key
ProcessIntroKeyMessage (buf, len, senderEndpoint);
break;
case eSessionStateCreatedSent:
// session confirmed
ProcessSessionConfirmed (buf, len);
break;
case eSessionRelayRequestSent:
// relay response
ProcessRelayResponse (buf,len);
break;
case eSessionRelayResponseReceived:
// HolePunch received // HolePunch received
LogPrint ("SSU HolePuch of ", len, " bytes received"); LogPrint ("SSU HolePuch of ", len, " bytes received");
m_State = eSessionStateEstablished;
Established ();
break;
case eSessionRelayRequestReceived:
// HolePunch
m_State = eSessionStateUnknown; m_State = eSessionStateUnknown;
Connect (); Connect ();
break; }
default: else
LogPrint ("SSU state not implemented yet"); {
ScheduleTermination ();
if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first
Decrypt (buf, len, m_SessionKey);
else
{
// try intro key depending on side
auto introKey = GetIntroKey ();
if (introKey && Validate (buf, len, introKey))
Decrypt (buf, len, introKey);
else
{
// try own intro key
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address)
{
LogPrint ("SSU is not supported");
return;
}
if (Validate (buf, len, address->key))
Decrypt (buf, len, address->key);
else
{
LogPrint ("MAC verifcation failed");
m_Server.DeleteSession (this);
return;
}
}
}
// successfully decrypted
ProcessMessage (buf, len, senderEndpoint);
} }
} }
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)
{ {
if (Validate (buf, len, m_MacKey))
{
Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
switch (header->GetPayloadType ()) switch (header->GetPayloadType ())
{ {
@ -104,6 +112,15 @@ namespace ssu
LogPrint ("SSU data received"); LogPrint ("SSU data received");
ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
break; break;
case PAYLOAD_TYPE_SESSION_REQUEST:
ProcessSessionRequest (buf, len, senderEndpoint);
break;
case PAYLOAD_TYPE_SESSION_CREATED:
ProcessSessionCreated (buf, len);
break;
case PAYLOAD_TYPE_SESSION_CONFIRMED:
ProcessSessionConfirmed (buf, len);
break;
case PAYLOAD_TYPE_PEER_TEST: case PAYLOAD_TYPE_PEER_TEST:
LogPrint ("SSU peer test received"); LogPrint ("SSU peer test received");
ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint);
@ -114,6 +131,10 @@ namespace ssu
m_Server.DeleteSession (this); // delete this m_Server.DeleteSession (this); // delete this
break; break;
} }
case PAYLOAD_TYPE_RELAY_RESPONSE:
ProcessRelayResponse (buf, len);
m_Server.DeleteSession (this);
break;
case PAYLOAD_TYPE_RELAY_INTRO: case PAYLOAD_TYPE_RELAY_INTRO:
LogPrint ("SSU relay intro received"); LogPrint ("SSU relay intro received");
ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
@ -122,63 +143,13 @@ namespace ssu
LogPrint ("Unexpected SSU payload type ", (int)header->GetPayloadType ()); LogPrint ("Unexpected SSU payload type ", (int)header->GetPayloadType ());
} }
} }
else
{
LogPrint ("MAC key failed. Trying intro key");
auto introKey = GetIntroKey ();
if (introKey && Validate (buf, len, introKey))
{
Decrypt (buf, len, introKey);
SSUHeader * header = (SSUHeader *)buf;
LogPrint ("Unexpected SSU payload type ", (int)(header->flag >> 4));
// TODO:
}
else
LogPrint ("MAC verifcation failed");
m_State = eSessionStateUnknown;
}
}
void SSUSession::ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
auto introKey = GetIntroKey ();
if (!introKey)
{
LogPrint ("SSU is not supported");
return;
}
// use intro key for verification and decryption
if (!Validate (buf, len, introKey))
{
LogPrint ("MAC verification intro key failed");
Failed ();
return;
}
Decrypt (buf, len, introKey);
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
SSUHeader * header = (SSUHeader *)buf;
switch (header->GetPayloadType ())
{
case PAYLOAD_TYPE_SESSION_REQUEST:
ProcessSessionRequest (buf, len, senderEndpoint);
break;
case PAYLOAD_TYPE_SESSION_CREATED:
ProcessSessionCreated (buf, len);
break;
case PAYLOAD_TYPE_PEER_TEST:
LogPrint ("SSU peer test received");
// TODO:
break;
default: ;
}
}
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
m_State = eSessionStateRequestReceived; m_State = eSessionStateRequestReceived;
LogPrint ("Session request received"); LogPrint ("Session request received");
m_RemoteEndpoint = senderEndpoint; m_RemoteEndpoint = senderEndpoint;
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
SendSessionCreated (buf + sizeof (SSUHeader)); SendSessionCreated (buf + sizeof (SSUHeader));
} }
@ -196,6 +167,7 @@ namespace ssu
uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t * y = payload; uint8_t * y = payload;
CreateAESandMacKey (y, m_SessionKey, m_MacKey);
memcpy (signedData, m_DHKeysPair->publicKey, 256); // x memcpy (signedData, m_DHKeysPair->publicKey, 256); // x
memcpy (signedData + 256, y, 256); // y memcpy (signedData + 256, y, 256); // y
payload += 256; payload += 256;
@ -228,13 +200,6 @@ namespace ssu
} }
void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len) void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len)
{
LogPrint ("Process session confirmed");
if (Validate (buf, len, m_MacKey))
{
Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf;
if (header->GetPayloadType () == PAYLOAD_TYPE_SESSION_CONFIRMED)
{ {
m_State = eSessionStateConfirmedReceived; m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received"); LogPrint ("Session confirmed received");
@ -242,12 +207,6 @@ namespace ssu
SendI2NPMessage (CreateDeliveryStatusMsg (0)); SendI2NPMessage (CreateDeliveryStatusMsg (0));
Established (); Established ();
} }
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
}
else
LogPrint ("MAC verifcation failed");
}
void SSUSession::SendSessionRequest () void SSUSession::SendSessionRequest ()
{ {
@ -273,7 +232,7 @@ namespace ssu
m_Server.Send (buf, 304, m_RemoteEndpoint); m_Server.Send (buf, 304, m_RemoteEndpoint);
} }
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer) void SSUSession::SendRelayRequest (uint32_t iTag, const uint8_t * iKey)
{ {
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address) if (!address)
@ -284,7 +243,7 @@ namespace ssu
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 (introducer.iTag); *(uint32_t *)payload = htobe32 (iTag);
payload += 4; payload += 4;
*payload = 0; // no address *payload = 0; // no address
payload++; payload++;
@ -299,8 +258,13 @@ namespace ssu
uint8_t iv[16]; uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); if (m_State == eSessionStateEstablished)
m_State = eSessionRelayRequestSent; FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey);
else
{
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey);
m_State = eSessionStateRelayRequestSent;
}
m_Server.Send (buf, 96, m_RemoteEndpoint); m_Server.Send (buf, 96, m_RemoteEndpoint);
} }
@ -391,44 +355,21 @@ namespace ssu
} }
void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len) void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len)
{
LogPrint ("Process relay response");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address)
{
LogPrint ("SSU is not supported");
return;
}
if (Validate (buf, len, address->key))
{
Decrypt (buf, len, address->key);
SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == PAYLOAD_TYPE_RELAY_RESPONSE)
{ {
LogPrint ("Relay response received"); LogPrint ("Relay response received");
m_State = eSessionRelayRequestReceived;
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
payload++; payload++; // remote size
boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload))); //boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload)));
payload += 4; payload += 4; // remote address
uint16_t remotePort = be16toh (*(uint16_t *)(payload)); //uint16_t remotePort = be16toh (*(uint16_t *)(payload));
payload += 2; payload += 2; // remote port
boost::asio::ip::udp::endpoint newRemoteEndpoint(remoteIP, remotePort); payload++; // our size
m_Server.ReassignSession (m_RemoteEndpoint, newRemoteEndpoint);
m_RemoteEndpoint = newRemoteEndpoint;
payload++;
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(payload))); boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(payload)));
payload += 4; payload += 4; // our address
uint16_t ourPort = be16toh (*(uint16_t *)(payload)); uint16_t ourPort = be16toh (*(uint16_t *)(payload));
payload += 2; 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.to_string ().c_str ()); i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
m_State= eSessionRelayResponseReceived;
}
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
}
} }
void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len) void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len)
@ -524,7 +465,7 @@ namespace ssu
} }
} }
void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer) void SSUSession::Introduce (uint32_t iTag, const uint8_t * iKey)
{ {
if (m_State == eSessionStateUnknown) if (m_State == eSessionStateUnknown)
{ {
@ -532,8 +473,17 @@ namespace ssu
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (boost::bind (&SSUSession::HandleConnectTimer, m_Timer.async_wait (boost::bind (&SSUSession::HandleConnectTimer,
this, boost::asio::placeholders::error)); this, boost::asio::placeholders::error));
SendRelayRequest (introducer);
} }
SendRelayRequest (iTag, iKey);
}
void SSUSession::WaitForIntroduction ()
{
m_State = eSessionStateIntroduced;
// set connect timer
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (boost::bind (&SSUSession::HandleConnectTimer,
this, boost::asio::placeholders::error));
} }
void SSUSession::Close () void SSUSession::Close ()
@ -556,7 +506,7 @@ namespace ssu
Send (it); Send (it);
m_DelayedMessages.clear (); m_DelayedMessages.clear ();
} }
if (m_PeerTest) if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ()))
SendPeerTest (); SendPeerTest ();
ScheduleTermination (); ScheduleTermination ();
} }
@ -664,9 +614,29 @@ namespace ssu
auto it = m_IncomleteMessages.find (msgID); auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ()) if (it != m_IncomleteMessages.end ())
{ {
msg = it->second; if (fragmentNum == it->second->nextFragmentNum)
{
// expected fragment
msg = it->second->msg;
memcpy (msg->buf + msg->len, buf, fragmentSize); memcpy (msg->buf + msg->len, buf, fragmentSize);
msg->len += fragmentSize; msg->len += fragmentSize;
it->second->nextFragmentNum++;
}
else if (fragmentNum < it->second->nextFragmentNum)
// duplicate fragment
LogPrint ("Duplicate fragment ", fragmentNum, " of message ", msgID, ". Ignored");
else
{
// missing fragment
LogPrint ("Missing fragments from ", it->second->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
//TODO
}
if (isLast)
{
delete it->second;
m_IncomleteMessages.erase (it);
}
} }
else else
// TODO: // TODO:
@ -682,12 +652,10 @@ namespace ssu
if (msg) if (msg)
{ {
if (!fragmentNum && !isLast) if (!fragmentNum && !isLast)
m_IncomleteMessages[msgID] = msg; m_IncomleteMessages[msgID] = new IncompleteMessage (msg);
if (isLast) if (isLast)
{ {
SendMsgAck (msgID); SendMsgAck (msgID);
if (fragmentNum > 0)
m_IncomleteMessages.erase (msgID);
msg->FromSSU (msgID); msg->FromSSU (msgID);
if (m_State == eSessionStateEstablished) if (m_State == eSessionStateEstablished)
i2p::HandleI2NPMessage (msg); i2p::HandleI2NPMessage (msg);
@ -723,19 +691,46 @@ namespace ssu
uint16_t port = *(uint16_t *)buf; // use it as is uint16_t port = *(uint16_t *)buf; // use it as is
buf += 2; // port buf += 2; // port
uint8_t * introKey = buf; uint8_t * introKey = buf;
if (port) if (port && !address)
{ {
LogPrint ("SSU peer test. We are Charlie");
Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Bob
if (address)
SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice
else
LogPrint ("Address of ", size, " bytes not supported"); LogPrint ("Address of ", size, " bytes not supported");
return;
}
if (m_PeerTestNonces.count (nonce) > 0)
{
// existing test
if (m_PeerTest)
{
LogPrint ("SSU peer test from Bob. We are Alice");
m_PeerTestNonces.erase (nonce);
m_PeerTest = false;
}
else if (port)
{
LogPrint ("SSU peer test from Charlie. We are Bob");
// TODO: back to Alice
} }
else else
{ {
LogPrint ("SSU peer test. We are Bob"); LogPrint ("SSU peer test from Alice. We are Charlie");
// TODO: //SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice
}
}
else
{
// new test
m_PeerTestNonces.insert (nonce);
if (port)
{
LogPrint ("SSU peer test from Bob. We are Charlie");
Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Bob
SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice
}
else
{
LogPrint ("SSU peer test from Alice. We are Bob");
// TODO: find Charlie
}
} }
} }
@ -777,6 +772,7 @@ namespace ssu
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t nonce = 0; uint32_t nonce = 0;
rnd.GenerateWord32 (nonce); rnd.GenerateWord32 (nonce);
m_PeerTestNonces.insert (nonce);
*(uint32_t *)payload = htobe32 (nonce); *(uint32_t *)payload = htobe32 (nonce);
payload += 4; // nonce payload += 4; // nonce
*payload = 4; *payload = 4;
@ -812,12 +808,12 @@ namespace ssu
} }
void SSUSession::SendSesionDestroyed () void SSUSession::SendSesionDestroyed ()
{
if (m_IsSessionKey)
{ {
uint8_t buf[48 + 18], iv[16]; uint8_t buf[48 + 18], iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
if (m_State == eSessionStateEstablished)
{
// encrypt message with session key // encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey);
m_Server.Send (buf, 48, m_RemoteEndpoint); m_Server.Send (buf, 48, m_RemoteEndpoint);
@ -977,11 +973,12 @@ namespace ssu
else else
{ {
// otherwise create new session // otherwise create new session
session = new SSUSession (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session;
if (!router->UsesIntroducer ()) if (!router->UsesIntroducer ())
{ {
// connect directly // connect directly
session = new SSUSession (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session;
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ", LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect (); session->Connect ();
@ -989,24 +986,27 @@ namespace ssu
else else
{ {
// connect through introducer // connect through introducer
session->WaitForIntroduction ();
if (address->introducers.size () > 0) if (address->introducers.size () > 0)
{ {
auto& introducer = address->introducers[0]; // TODO: auto& introducer = address->introducers[0]; // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort); boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort);
it = m_Sessions.find (introducerEndpoint);
if (it == m_Sessions.end ())
{
session = new SSUSession (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = session;
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (),
"] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ()); "] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ());
session->ConnectThroughIntroducer (introducer); it = m_Sessions.find (introducerEndpoint);
SSUSession * introducerSession = nullptr;
if (it != m_Sessions.end ())
{
LogPrint ("Session to introducer already exists");
introducerSession = it->second;
} }
else else
{ {
LogPrint ("Session to introducer already exists"); LogPrint ("New session to introducer created");
// TODO: introducerSession = new SSUSession (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession;
} }
introducerSession->Introduce (introducer.iTag, introducer.iKey);
} }
else else
LogPrint ("Router is unreachable, but not introducers presentd. Ignored"); LogPrint ("Router is unreachable, but not introducers presentd. Ignored");
@ -1038,19 +1038,6 @@ namespace ssu
} }
m_Sessions.clear (); m_Sessions.clear ();
} }
void SSUServer::ReassignSession (const boost::asio::ip::udp::endpoint& oldEndpoint, const boost::asio::ip::udp::endpoint& newEndpoint)
{
auto it = m_Sessions.find (oldEndpoint);
if (it != m_Sessions.end ())
{
auto session = it->second;
m_Sessions.erase (it);
m_Sessions[newEndpoint] = session;
LogPrint ("SSU session reassigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (),
" to ", newEndpoint.address ().to_string (), ":", newEndpoint.port ());
}
}
} }
} }

29
SSU.h
View File

@ -4,6 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <map>
#include <list> #include <list>
#include <set>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cryptopp/modes.h> #include <cryptopp/modes.h>
#include <cryptopp/aes.h> #include <cryptopp/aes.h>
@ -60,9 +61,9 @@ namespace ssu
eSessionStateCreatedReceived, eSessionStateCreatedReceived,
eSessionStateConfirmedSent, eSessionStateConfirmedSent,
eSessionStateConfirmedReceived, eSessionStateConfirmedReceived,
eSessionRelayRequestSent, eSessionStateRelayRequestSent,
eSessionRelayRequestReceived, eSessionStateRelayRequestReceived,
eSessionRelayResponseReceived, eSessionStateIntroduced,
eSessionStateEstablished, eSessionStateEstablished,
eSessionStateFailed eSessionStateFailed
}; };
@ -78,7 +79,8 @@ namespace ssu
~SSUSession (); ~SSUSession ();
void Connect (); void Connect ();
void ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer); void Introduce (uint32_t iTag, const uint8_t * iKey);
void WaitForIntroduction ();
void Close (); void Close ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; }; const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; };
@ -87,14 +89,12 @@ namespace ssu
private: private:
void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey); void CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for non-established session
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendSessionRequest (); void SendSessionRequest ();
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer); void SendRelayRequest (uint32_t iTag, const uint8_t * iKey);
void ProcessSessionCreated (uint8_t * buf, size_t len); void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x); void SendSessionCreated (const uint8_t * x);
void ProcessSessionConfirmed (uint8_t * buf, size_t len); void ProcessSessionConfirmed (uint8_t * buf, size_t len);
@ -122,6 +122,14 @@ namespace ssu
private: private:
struct IncompleteMessage
{
I2NPMessage * msg;
uint8_t nextFragmentNum;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (1) {};
};
SSUServer& m_Server; SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
const i2p::data::RouterInfo * m_RemoteRouter; const i2p::data::RouterInfo * m_RemoteRouter;
@ -129,11 +137,13 @@ namespace ssu
i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
bool m_PeerTest; bool m_PeerTest;
SessionState m_State; SessionState m_State;
bool m_IsSessionKey;
uint32_t m_RelayTag; uint32_t m_RelayTag;
std::set<uint32_t> m_PeerTestNonces;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32], m_MacKey[32]; uint8_t m_SessionKey[32], m_MacKey[32];
std::map<uint32_t, I2NPMessage *> m_IncomleteMessages; std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
std::list<i2p::I2NPMessage *> m_DelayedMessages; std::list<i2p::I2NPMessage *> m_DelayedMessages;
}; };
@ -153,7 +163,6 @@ namespace ssu
boost::asio::io_service& GetService () { return m_Socket.get_io_service(); }; boost::asio::io_service& GetService () { return m_Socket.get_io_service(); };
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void ReassignSession (const boost::asio::ip::udp::endpoint& oldEndpoint, const boost::asio::ip::udp::endpoint& newEndpoint);
private: private:

View File

@ -28,8 +28,12 @@ namespace stream
Stream::~Stream () Stream::~Stream ()
{ {
m_ReceiveTimer.cancel (); m_ReceiveTimer.cancel ();
while (auto packet = m_ReceiveQueue.Get ()) while (!m_ReceiveQueue.empty ())
{
auto packet = m_ReceiveQueue.front ();
m_ReceiveQueue.pop ();
delete packet; delete packet;
}
for (auto it: m_SavedPackets) for (auto it: m_SavedPackets)
delete it; delete it;
} }
@ -118,7 +122,7 @@ namespace stream
packet->offset = packet->GetPayload () - packet->buf; packet->offset = packet->GetPayload () - packet->buf;
if (packet->GetLength () > 0) if (packet->GetLength () > 0)
{ {
m_ReceiveQueue.Put (packet); m_ReceiveQueue.push (packet);
m_ReceiveTimer.cancel (); m_ReceiveTimer.cancel ();
} }
else else
@ -131,7 +135,6 @@ namespace stream
LogPrint ("Closed"); LogPrint ("Closed");
SendQuickAck (); // send ack for close explicitly? SendQuickAck (); // send ack for close explicitly?
m_IsOpen = false; m_IsOpen = false;
m_ReceiveQueue.WakeUp ();
} }
} }
@ -239,45 +242,25 @@ namespace stream
if (SendPacket (packet, size)) if (SendPacket (packet, size))
LogPrint ("FIN sent"); LogPrint ("FIN sent");
m_ReceiveQueue.WakeUp ();
} }
} }
size_t Stream::Receive (uint8_t * buf, size_t len, int timeout)
{
if (!m_IsOpen) return 0;
if (m_ReceiveQueue.IsEmpty ())
{
if (!timeout) return 0;
if (!m_ReceiveQueue.Wait (timeout, 0))
return 0;
}
// either non-empty or we have received something
return ConcatenatePackets (buf, len);
}
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
{ {
size_t pos = 0; size_t pos = 0;
while (pos < len) while (pos < len && !m_ReceiveQueue.empty ())
{
Packet * packet = m_ReceiveQueue.Peek ();
if (packet)
{ {
Packet * packet = m_ReceiveQueue.front ();
size_t l = std::min (packet->GetLength (), len - pos); size_t l = std::min (packet->GetLength (), len - pos);
memcpy (buf + pos, packet->GetBuffer (), l); memcpy (buf + pos, packet->GetBuffer (), l);
pos += l; pos += l;
packet->offset += l; packet->offset += l;
if (!packet->GetLength ()) if (!packet->GetLength ())
{ {
m_ReceiveQueue.Get (); m_ReceiveQueue.pop ();
delete packet; delete packet;
} }
} }
else // no more data available
break;
}
return pos; return pos;
} }

View File

@ -5,12 +5,12 @@
#include <string> #include <string>
#include <map> #include <map>
#include <set> #include <set>
#include <queue>
#include <thread> #include <thread>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <cryptopp/dsa.h> #include <cryptopp/dsa.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Queue.h"
#include "Identity.h" #include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -80,7 +80,7 @@ namespace stream
void HandleNextPacket (Packet * packet); void HandleNextPacket (Packet * packet);
size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds
size_t Receive (uint8_t * buf, size_t len, int timeout = 0); // returns 0 if timeout expired
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);
@ -112,7 +112,7 @@ namespace stream
StreamingDestination * m_LocalDestination; StreamingDestination * m_LocalDestination;
const i2p::data::LeaseSet& m_RemoteLeaseSet; const i2p::data::LeaseSet& m_RemoteLeaseSet;
i2p::data::Lease m_CurrentRemoteLease; i2p::data::Lease m_CurrentRemoteLease;
i2p::util::Queue<Packet> m_ReceiveQueue; std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets; std::set<Packet *, PacketCmp> m_SavedPackets;
i2p::tunnel::OutboundTunnel * m_OutboundTunnel; i2p::tunnel::OutboundTunnel * m_OutboundTunnel;
boost::asio::deadline_timer m_ReceiveTimer; boost::asio::deadline_timer m_ReceiveTimer;
@ -204,7 +204,7 @@ namespace stream
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
{ {
if (!m_ReceiveQueue.IsEmpty ()) if (!m_ReceiveQueue.empty ())
{ {
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer)); size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
if (received) if (received)

83
build/i2pd.pro Normal file
View File

@ -0,0 +1,83 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
TARGET = ./../../i2pd_qt
QMAKE_CXXFLAGS += -std=c++0x
LIBS += -lcrypto++
LIBS += \
-lboost_system\
-lboost_filesystem\
-lboost_regex\
-lboost_program_options\
-lpthread
SOURCES += \
../LeaseSet.cpp \
../i2p.cpp \
../HTTPServer.cpp \
../HTTPProxy.cpp \
../Garlic.cpp \
../base64.cpp \
../AddressBook.cpp \
../util.cpp \
../UPnP.cpp \
../TunnelPool.cpp \
../TunnelGateway.cpp \
../TunnelEndpoint.cpp \
../Tunnel.cpp \
../Transports.cpp \
../TransitTunnel.cpp \
../Streaming.cpp \
../SSU.cpp \
../RouterInfo.cpp \
../RouterContext.cpp \
../Reseed.cpp \
../NTCPSession.cpp \
../NetDb.cpp \
../Log.cpp \
../Identity.cpp \
../I2NPProtocol.cpp
HEADERS += \
../LeaseSet.h \
../Identity.h \
../HTTPServer.h \
../HTTPProxy.h \
../hmac.h \
../Garlic.h \
../ElGamal.h \
../CryptoConst.h \
../base64.h \
../AddressBook.h \
../util.h \
../UPnP.h \
../TunnelPool.h \
../TunnelGateway.h \
../TunnelEndpoint.h \
../TunnelConfig.h \
../TunnelBase.h \
../Tunnel.h \
../Transports.h \
../TransitTunnel.h \
../Timestamp.h \
../Streaming.h \
../SSU.h \
../RouterInfo.h \
../RouterContext.h \
../Reseed.h \
../Queue.h \
../NTCPSession.h \
../NetDb.h \
../Log.h \
../LittleBigEndian.h \
../I2PEndian.h \
../I2NPProtocol.h
OTHER_FILES += \
../README.md \
../Makefile \
../LICENSE

View File

@ -262,6 +262,7 @@ namespace http
// code for parser tests // code for parser tests
//{ //{
// i2p::util::http::url u_0("http://127.0.0.1:7070/asdasd?qqqqqqqqqqqq");
// i2p::util::http::url u_1("http://user:password@site.com:8080/asdasd?qqqqqqqqqqqqq"); // i2p::util::http::url u_1("http://user:password@site.com:8080/asdasd?qqqqqqqqqqqqq");
// i2p::util::http::url u_2("http://user:password@site.com/asdasd?qqqqqqqqqqqqqq"); // i2p::util::http::url u_2("http://user:password@site.com/asdasd?qqqqqqqqqqqqqq");
// i2p::util::http::url u_3("http://user:@site.com/asdasd?qqqqqqqqqqqqq"); // i2p::util::http::url u_3("http://user:@site.com/asdasd?qqqqqqqqqqqqq");