This commit is contained in:
chertov 2014-04-23 02:16:07 +04:00
commit 9fe808dbba
8 changed files with 269 additions and 174 deletions

View File

@ -6,7 +6,7 @@ OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transpor
obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \
obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o \
obj/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o obj/AddressBook.o \
obj/Daemon.o obj/DaemonLinux.o
obj/Daemon.o obj/DaemonLinux.o obj/SSUData.o
INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =

152
SSU.cpp
View File

@ -19,20 +19,14 @@ namespace ssu
const i2p::data::RouterInfo * router, bool peerTest ):
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router),
m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), m_State (eSessionStateUnknown),
m_IsSessionKey (false), m_RelayTag (0)
m_IsSessionKey (false), m_RelayTag (0), m_Data (*this)
{
m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
}
SSUSession::~SSUSession ()
{
delete m_DHKeysPair;
for (auto it: m_IncomleteMessages)
if (it.second)
{
DeleteI2NPMessage (it.second->msg);
delete it.second;
}
delete m_DHKeysPair;
}
void SSUSession::CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey)
@ -603,6 +597,7 @@ namespace ssu
void SSUSession::Established ()
{
m_State = eSessionStateEstablished;
SendI2NPMessage (CreateDatabaseStoreMsg ());
if (!m_DelayedMessages.empty ())
{
@ -671,115 +666,7 @@ namespace ssu
void SSUSession::ProcessData (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint ("Process SSU data flags=", (int)flag);
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{
// explicit ACKs
uint8_t numAcks =*buf;
buf++;
// TODO: process ACKs
buf += numAcks*4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
{
// explicit ACK bitfields
uint8_t numBitfields =*buf;
buf++;
for (int i = 0; i < numBitfields; i++)
{
buf += 4; // msgID
// TODO: process ACH bitfields
while (*buf & 0x80) // not last
buf++;
buf++; // last byte
}
}
uint8_t numFragments = *buf; // number of fragments
buf++;
for (int i = 0; i < numFragments; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf); // message ID
buf += 4;
uint8_t frag[4];
frag[0] = 0;
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = be32toh (*(uint32_t *)frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
I2NPMessage * msg = nullptr;
if (fragmentNum > 0) // follow-up fragment
{
auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ())
{
if (fragmentNum == it->second->nextFragmentNum)
{
// expected fragment
msg = it->second->msg;
memcpy (msg->buf + msg->len, buf, 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
// TODO:
LogPrint ("Unexpected follow-on fragment ", fragmentNum, " of message ", msgID);
}
else // first fragment
{
msg = NewI2NPMessage ();
memcpy (msg->GetSSUHeader (), buf, fragmentSize);
msg->len += fragmentSize - sizeof (I2NPHeaderShort);
}
if (msg)
{
if (!fragmentNum && !isLast)
m_IncomleteMessages[msgID] = new IncompleteMessage (msg);
if (isLast)
{
SendMsgAck (msgID);
msg->FromSSU (msgID);
if (m_State == eSessionStateEstablished)
i2p::HandleI2NPMessage (msg);
else
{
// we expect DeliveryStatus
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
{
LogPrint ("SSU session established");
m_State = eSessionStateEstablished;
Established ();
}
else
LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID);
DeleteI2NPMessage (msg);
}
}
}
buf += fragmentSize;
}
m_Data.ProcessMessage (buf, len);
}
@ -992,8 +879,8 @@ namespace ssu
m_Server.Send (buf, msgSize, m_RemoteEndpoint);
}
SSUServer::SSUServer (boost::asio::io_service& service, int port):
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint)
SSUServer::SSUServer (int port): m_Thread (nullptr), m_Work (m_Service),
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (m_Service, m_Endpoint)
{
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
@ -1007,15 +894,40 @@ namespace ssu
void SSUServer::Start ()
{
Receive ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_Service.post (boost::bind (&SSUServer::Receive, this));
}
void SSUServer::Stop ()
{
DeleteAllSessions ();
m_IsRunning = false;
m_Service.stop ();
m_Socket.close ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
}
void SSUServer::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint ("SSU server: ", ex.what ());
}
}
}
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
{
m_Relays[tag] = relay;

32
SSU.h
View File

@ -5,6 +5,7 @@
#include <map>
#include <list>
#include <set>
#include <thread>
#include <boost/asio.hpp>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
@ -12,6 +13,7 @@
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "SSUData.h"
namespace i2p
{
@ -44,14 +46,6 @@ namespace ssu
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
enum SessionState
{
eSessionStateUnknown,
@ -87,6 +81,8 @@ namespace ssu
void SendI2NPMessage (I2NPMessage * msg);
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; };
private:
void CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey);
@ -124,15 +120,8 @@ namespace ssu
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
struct IncompleteMessage
{
I2NPMessage * msg;
uint8_t nextFragmentNum;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (1) {};
};
friend class SSUData; // TODO: change in later
SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
const i2p::data::RouterInfo * m_RemoteRouter;
@ -146,15 +135,15 @@ namespace ssu
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32], m_MacKey[32];
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
std::list<i2p::I2NPMessage *> m_DelayedMessages;
SSUData m_Data;
};
class SSUServer
{
public:
SSUServer (boost::asio::io_service& service, int port);
SSUServer (int port);
~SSUServer ();
void Start ();
void Stop ();
@ -172,11 +161,16 @@ namespace ssu
private:
void Run ();
void Receive ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::ip::udp::endpoint m_Endpoint;
boost::asio::ip::udp::socket m_Socket;
boost::asio::ip::udp::endpoint m_SenderEndpoint;

138
SSUData.cpp Normal file
View File

@ -0,0 +1,138 @@
#include "Log.h"
#include "SSU.h"
#include "SSUData.h"
namespace i2p
{
namespace ssu
{
SSUData::SSUData (SSUSession& session):
m_Session (session)
{
}
SSUData::~SSUData ()
{
for (auto it: m_IncomleteMessages)
if (it.second)
{
DeleteI2NPMessage (it.second->msg);
delete it.second;
}
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint ("Process SSU data flags=", (int)flag);
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{
// explicit ACKs
uint8_t numAcks =*buf;
buf++;
// TODO: process ACKs
buf += numAcks*4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
{
// explicit ACK bitfields
uint8_t numBitfields =*buf;
buf++;
for (int i = 0; i < numBitfields; i++)
{
buf += 4; // msgID
// TODO: process ACH bitfields
while (*buf & 0x80) // not last
buf++;
buf++; // last byte
}
}
uint8_t numFragments = *buf; // number of fragments
buf++;
for (int i = 0; i < numFragments; i++)
{
uint32_t msgID = be32toh (*(uint32_t *)buf); // message ID
buf += 4;
uint8_t frag[4];
frag[0] = 0;
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = be32toh (*(uint32_t *)frag); // fragment info
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
I2NPMessage * msg = nullptr;
if (fragmentNum > 0) // follow-up fragment
{
auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ())
{
if (fragmentNum == it->second->nextFragmentNum)
{
// expected fragment
msg = it->second->msg;
memcpy (msg->buf + msg->len, buf, 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
// TODO:
LogPrint ("Unexpected follow-on fragment ", fragmentNum, " of message ", msgID);
}
else // first fragment
{
msg = NewI2NPMessage ();
memcpy (msg->GetSSUHeader (), buf, fragmentSize);
msg->len += fragmentSize - sizeof (I2NPHeaderShort);
}
if (msg)
{
if (!fragmentNum && !isLast)
m_IncomleteMessages[msgID] = new IncompleteMessage (msg);
if (isLast)
{
m_Session.SendMsgAck (msgID);
msg->FromSSU (msgID);
if (m_Session.GetState () == eSessionStateEstablished)
i2p::HandleI2NPMessage (msg);
else
{
// we expect DeliveryStatus
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
{
LogPrint ("SSU session established");
m_Session.Established ();
}
else
LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID);
DeleteI2NPMessage (msg);
}
}
}
buf += fragmentSize;
}
}
}
}

48
SSUData.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef SSU_DATA_H__
#define SSU_DATA_H__
#include <inttypes.h>
#include <map>
#include "I2NPProtocol.h"
namespace i2p
{
namespace ssu
{
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
class SSUSession;
class SSUData
{
public:
SSUData (SSUSession& session);
~SSUData ();
void ProcessMessage (uint8_t * buf, size_t len);
private:
struct IncompleteMessage
{
I2NPMessage * msg;
uint8_t nextFragmentNum;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (1) {};
};
SSUSession& m_Session;
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
};
}
}
#endif

View File

@ -138,20 +138,8 @@ namespace stream
}
}
size_t Stream::Send (uint8_t * buf, size_t len, int timeout)
size_t Stream::Send (const uint8_t * buf, size_t len, int timeout)
{
if (!m_IsOpen)
ConnectAndSend (buf, len);
else
{
// TODO: implement
}
return len;
}
void Stream::ConnectAndSend (uint8_t * buf, size_t len)
{
m_IsOpen = true;
Packet * p = new Packet ();
uint8_t * packet = p->GetBuffer ();
// TODO: implement setters
@ -167,27 +155,43 @@ namespace stream
packet[size] = 0;
size++; // NACK count
size++; // resend delay
// TODO: for initial packet only, following packets have different falgs
*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_SYNCHRONIZE |
PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED |
PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED | PACKET_FLAG_NO_ACK);
size += 2; // flags
*(uint16_t *)(packet + size) = htobe16 (sizeof (i2p::data::Identity) + 40 + 2); // identity + signature + packet size
size += 2; // options size
memcpy (packet + size, &m_LocalDestination->GetIdentity (), sizeof (i2p::data::Identity));
size += sizeof (i2p::data::Identity); // from
*(uint16_t *)(packet + size) = htobe16 (STREAMING_MTU);
size += 2; // max packet size
uint8_t * signature = packet + size; // set it later
memset (signature, 0, 40); // zeroes for now
size += 40; // signature
memcpy (packet + size, buf, len);
size += len; // payload
m_LocalDestination->Sign (packet, size, signature);
if (!m_IsOpen)
{
// initial packet
m_IsOpen = true;
*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_SYNCHRONIZE |
PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED |
PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED | PACKET_FLAG_NO_ACK);
size += 2; // flags
*(uint16_t *)(packet + size) = htobe16 (sizeof (i2p::data::Identity) + 40 + 2); // identity + signature + packet size
size += 2; // options size
memcpy (packet + size, &m_LocalDestination->GetIdentity (), sizeof (i2p::data::Identity));
size += sizeof (i2p::data::Identity); // from
*(uint16_t *)(packet + size) = htobe16 (STREAMING_MTU);
size += 2; // max packet size
uint8_t * signature = packet + size; // set it later
memset (signature, 0, 40); // zeroes for now
size += 40; // signature
memcpy (packet + size, buf, len);
size += len; // payload
m_LocalDestination->Sign (packet, size, signature);
}
else
{
// follow on packet
*(uint16_t *)(packet + size) = 0;
size += 2; // flags
*(uint16_t *)(packet + size) = 0; // no options
size += 2; // options size
memcpy (packet + size, buf, len);
size += len; // payload
}
p->len = size;
m_Service.post (boost::bind (&Stream::SendPacket, this, p));
return len;
}
void Stream::SendQuickAck ()
{

View File

@ -79,7 +79,7 @@ namespace stream
bool IsEstablished () const { return m_SendStreamID; };
void HandleNextPacket (Packet * packet);
size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds
size_t Send (const uint8_t * buf, size_t len, int timeout); // timeout in seconds
template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
@ -90,7 +90,6 @@ namespace stream
private:
void ConnectAndSend (uint8_t * buf, size_t len);
void SendQuickAck ();
bool SendPacket (Packet * packet);
bool SendPacket (const uint8_t * buf, size_t len);

View File

@ -110,7 +110,7 @@ namespace i2p
{
if (!m_SSUServer)
{
m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port);
m_SSUServer = new i2p::ssu::SSUServer (address.port);
LogPrint ("Start listening UDP port ", address.port);
m_SSUServer->Start ();
DetectExternalIP ();