Browse Source

Merge branch 'master' of github-meeh420:orignal/i2pd

* 'master' of github-meeh420:orignal/i2pd:
  delete unreachable SSU sessions
  show number sent/received bytes through the status page
  resend
  fixed memory leak
  close connection to first hop of declined tunnel
  process extended data
  handle individaul bitfields ack
  Moving file list to a common one. Still in makefile format, but now with CPP/H as input instead of OBJECTS. Issue #82
  Removing Qt build file. Issue #82
  create inbound tunnel though outbound
  Makefile now detects which file to use (OSX or Linux) Issue #82
  Prepare support for POST/PUT
  close SSU session if not established
  send ack per fragment. temporary disble check for duplicated through IV
  fixed memory leak
  save out-of-sequence fragments
pull/93/head
Meeh 10 years ago
parent
commit
45289891d6
  1. 18
      HTTPServer.cpp
  2. 2
      HTTPServer.h
  3. 1
      I2NPProtocol.cpp
  4. 39
      Makefile
  5. 33
      Makefile.linux
  6. 7
      Makefile.osx
  7. 8
      NTCPSession.cpp
  8. 5
      NTCPSession.h
  9. 21
      SSU.cpp
  10. 5
      SSU.h
  11. 292
      SSUData.cpp
  12. 62
      SSUData.h
  13. 17
      Transports.cpp
  14. 2
      Transports.h
  15. 4
      Tunnel.cpp
  16. 2
      TunnelPool.cpp
  17. 85
      build/i2pd.pro
  18. 18
      filelist.mk

18
HTTPServer.cpp

@ -303,6 +303,7 @@ namespace util
s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () << ": " s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string (); << it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!outgoing) s << "-->"; if (!outgoing) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<BR>"; s << "<BR>";
} }
} }
@ -318,13 +319,18 @@ namespace util
if (outgoing) s << "-->"; if (outgoing) s << "-->";
s << endpoint.address ().to_string () << ":" << endpoint.port (); s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!outgoing) s << "-->"; if (!outgoing) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<BR>"; s << "<BR>";
} }
} }
s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq\">Flibusta</a></p>"; s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq\">Flibusta</a></p>";
} }
void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri) void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri)
{
HandleDestinationRequest(address, "GET", "", uri);
}
void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& method, const std::string& data, const std::string& uri)
{ {
i2p::data::IdentHash destination; i2p::data::IdentHash destination;
std::string fullAddress; std::string fullAddress;
@ -380,7 +386,13 @@ namespace util
m_Stream = i2p::stream::CreateStream (*leaseSet); m_Stream = i2p::stream::CreateStream (*leaseSet);
if (m_Stream) if (m_Stream)
{ {
std::string request = "GET " + uri + " HTTP/1.1\n Host:" + fullAddress + "\n"; std::string request = method+" " + uri + " HTTP/1.1\n Host:" + fullAddress + "\r\n";
if (!strcmp(method.c_str(), "GET"))
{
// POST/PUT, apply body
request += "\r\n"+ data;
}
LogPrint("HTTP Client Request: ", request);
m_Stream->Send ((uint8_t *)request.c_str (), request.length (), 10); m_Stream->Send ((uint8_t *)request.c_str (), request.length (), 10);
AsyncStreamReceive (); AsyncStreamReceive ();
} }
@ -421,7 +433,7 @@ namespace util
m_Reply.headers[1].value = "text/html"; m_Reply.headers[1].value = "text/html";
boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status), boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status),
boost::bind (&HTTPConnection::HandleWriteReply, this, boost::bind (&HTTPConnection::HandleWriteReply, this,
boost::asio::placeholders::error)); boost::asio::placeholders::error));
} }

2
HTTPServer.h

@ -69,7 +69,9 @@ namespace util
protected: protected:
virtual void HandleDestinationRequest(const std::string& address, const std::string& uri); virtual void HandleDestinationRequest(const std::string& address, const std::string& uri);
virtual void HandleDestinationRequest(const std::string& address, const std::string& method, const std::string& data, const std::string& uri);
virtual void RunRequest (); virtual void RunRequest ();
private: private:

1
I2NPProtocol.cpp

@ -359,6 +359,7 @@ namespace i2p
else else
{ {
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
i2p::transports.CloseSession (tunnel->GetTunnelConfig ()->GetFirstHop ()->router);
delete tunnel; delete tunnel;
} }
} }

39
Makefile

@ -1,38 +1,9 @@
CC = g++ UNAME := $(shell uname -s)
CFLAGS = -g -Wall -std=c++0x
OBJECTS = obj/CryptoConst.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \
obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \
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/SSUData.o obj/i2p.o obj/aes.o obj/SOCKS.o
INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
#check if AES-NI is supported by CPU ifeq ($(UNAME),Darwin)
ifneq ($(shell grep -c aes /proc/cpuinfo),0) include Makefile.osx
CPU_FLAGS = -DAESNI else
include Makefile.linux
endif endif
all: obj i2p
i2p: $(OBJECTS:obj/%=obj/%)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o
obj/%.o : %.cpp
$(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CPU_FLAGS)
obj:
mkdir -p obj
clean:
rm -fr obj i2p
.PHONY: all
.PHONY: clean

33
Makefile.linux

@ -0,0 +1,33 @@
CC = g++
CFLAGS = -g -Wall -std=c++0x
include filelist.mk
INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS =
#check if AES-NI is supported by CPU
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
CPU_FLAGS = -DAESNI
endif
all: obj i2p
i2p: $(OBJECTS:obj/%=obj/%)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o
obj/%.o : %.cpp
$(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CPU_FLAGS)
obj:
mkdir -p obj
clean:
rm -fr obj i2p
.PHONY: all
.PHONY: clean

7
Makefile.osx vendored

@ -1,11 +1,6 @@
CC = clang++ CC = clang++
CFLAGS = -g -Wall -std=c++11 -lstdc++ -I/usr/local/include CFLAGS = -g -Wall -std=c++11 -lstdc++ -I/usr/local/include
OBJECTS = obj/CryptoConst.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \ include filelist.mk
obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \
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/SSUData.o obj/i2p.o obj/aes.o obj/SOCKS.o
INCFLAGS = -DCRYPTOPP_DISABLE_ASM INCFLAGS = -DCRYPTOPP_DISABLE_ASM
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS = LIBS =

8
NTCPSession.cpp

@ -21,7 +21,8 @@ namespace ntcp
{ {
NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo): NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo):
m_Socket (service), m_TerminationTimer (service), m_IsEstablished (false), m_Socket (service), m_TerminationTimer (service), m_IsEstablished (false),
m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr) m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr),
m_NumSentBytes (0), m_NumReceivedBytes (0)
{ {
m_DHKeysPair = i2p::transports.GetNextDHKeysPair (); m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
} }
@ -29,7 +30,6 @@ namespace ntcp
NTCPSession::~NTCPSession () NTCPSession::~NTCPSession ()
{ {
delete m_DHKeysPair; delete m_DHKeysPair;
delete m_NextMessage;
} }
void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey) void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey)
@ -403,7 +403,7 @@ namespace ntcp
} }
else else
{ {
LogPrint ("Received: ", bytes_transferred); m_NumReceivedBytes += bytes_transferred;
m_ReceiveBufferOffset += bytes_transferred; m_ReceiveBufferOffset += bytes_transferred;
if (m_ReceiveBufferOffset >= 16) if (m_ReceiveBufferOffset >= 16)
@ -514,7 +514,7 @@ namespace ntcp
} }
else else
{ {
LogPrint ("Msg sent: ", bytes_transferred); m_NumSentBytes += bytes_transferred;
ScheduleTermination (); // reset termination timer ScheduleTermination (); // reset termination timer
} }
} }

5
NTCPSession.h

@ -79,6 +79,9 @@ namespace ntcp
void ServerLogin (); void ServerLogin ();
void SendI2NPMessage (I2NPMessage * msg); void SendI2NPMessage (I2NPMessage * msg);
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
protected: protected:
void Terminate (); void Terminate ();
@ -142,6 +145,8 @@ namespace ntcp
i2p::I2NPMessage * m_NextMessage; i2p::I2NPMessage * m_NextMessage;
std::list<i2p::I2NPMessage *> m_DelayedMessages; std::list<i2p::I2NPMessage *> m_DelayedMessages;
size_t m_NextMessageOffset; size_t m_NextMessageOffset;
size_t m_NumSentBytes, m_NumReceivedBytes;
}; };
class NTCPClient: public NTCPSession class NTCPClient: public NTCPSession

21
SSU.cpp

@ -19,7 +19,8 @@ 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_IsSessionKey (false), m_RelayTag (0), m_Data (*this) m_IsSessionKey (false), m_RelayTag (0), m_Data (*this),
m_NumSentBytes (0), m_NumReceivedBytes (0)
{ {
m_DHKeysPair = i2p::transports.GetNextDHKeysPair (); m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
} }
@ -74,6 +75,7 @@ namespace ssu
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)
{ {
m_NumReceivedBytes += len;
if (m_State == eSessionStateIntroduced) if (m_State == eSessionStateIntroduced)
{ {
// HolePunch received // HolePunch received
@ -83,11 +85,12 @@ namespace ssu
} }
else else
{ {
ScheduleTermination (); if (m_State == eSessionStateEstablished)
// check for duplicate ScheduleTermination ();
/* // check for duplicate
const uint8_t * iv = ((SSUHeader *)buf)->iv; const uint8_t * iv = ((SSUHeader *)buf)->iv;
if (m_ReceivedIVs.count (iv)) return; // duplicate detected if (m_ReceivedIVs.count (iv)) return; // duplicate detected
m_ReceivedIVs.insert (iv); m_ReceivedIVs.insert (iv);*/
if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first
DecryptSessionKey (buf, len); DecryptSessionKey (buf, len);
@ -652,7 +655,6 @@ namespace ssu
if (m_State != eSessionStateFailed) if (m_State != eSessionStateFailed)
{ {
m_State = eSessionStateFailed; m_State = eSessionStateFailed;
Close ();
m_Server.DeleteSession (this); // delete this m_Server.DeleteSession (this); // delete this
} }
} }
@ -822,6 +824,7 @@ namespace ssu
// encrypt message with session key // encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48);
Send (buf, 48); Send (buf, 48);
LogPrint ("SSU session destoryed sent");
} }
} }
@ -842,6 +845,7 @@ namespace ssu
void SSUSession::Send (const uint8_t * buf, size_t size) void SSUSession::Send (const uint8_t * buf, size_t size)
{ {
m_NumSentBytes += size;
m_Server.Send (buf, size, m_RemoteEndpoint); m_Server.Send (buf, size, m_RemoteEndpoint);
} }
@ -910,7 +914,6 @@ namespace ssu
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
{ {
m_Socket.send_to (boost::asio::buffer (buf, len), to); m_Socket.send_to (boost::asio::buffer (buf, len), to);
LogPrint ("SSU sent ", len, " bytes");
} }
void SSUServer::Receive () void SSUServer::Receive ()
@ -923,7 +926,6 @@ namespace ssu
{ {
if (!ecode) if (!ecode)
{ {
LogPrint ("SSU received ", bytes_transferred, " bytes");
SSUSession * session = nullptr; SSUSession * session = nullptr;
auto it = m_Sessions.find (m_SenderEndpoint); auto it = m_Sessions.find (m_SenderEndpoint);
if (it != m_Sessions.end ()) if (it != m_Sessions.end ())
@ -1020,7 +1022,12 @@ namespace ssu
introducerSession->Introduce (introducer->iTag, introducer->iKey); introducerSession->Introduce (introducer->iTag, introducer->iKey);
} }
else else
{
LogPrint ("Router is unreachable, but no introducers presented. Ignored"); LogPrint ("Router is unreachable, but no introducers presented. Ignored");
m_Sessions.erase (remoteEndpoint);
delete session;
session = nullptr;
}
} }
} }
} }

5
SSU.h

@ -31,7 +31,6 @@ namespace ssu
}; };
#pragma pack() #pragma pack()
const size_t SSU_MTU = 1484;
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
@ -74,6 +73,9 @@ namespace ssu
void SendPeerTest (); // Alice void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; }; SessionState GetState () const { return m_State; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
private: private:
@ -132,6 +134,7 @@ namespace ssu
std::list<i2p::I2NPMessage *> m_DelayedMessages; std::list<i2p::I2NPMessage *> m_DelayedMessages;
std::set<IV> m_ReceivedIVs; std::set<IV> m_ReceivedIVs;
SSUData m_Data; SSUData m_Data;
size_t m_NumSentBytes, m_NumReceivedBytes;
}; };
class SSUServer class SSUServer

292
SSUData.cpp

@ -1,4 +1,7 @@
#include <stdlib.h>
#include <boost/bind.hpp>
#include "Log.h" #include "Log.h"
#include "Timestamp.h"
#include "SSU.h" #include "SSU.h"
#include "SSUData.h" #include "SSUData.h"
@ -7,7 +10,7 @@ namespace i2p
namespace ssu namespace ssu
{ {
SSUData::SSUData (SSUSession& session): SSUData::SSUData (SSUSession& session):
m_Session (session) m_Session (session), m_ResendTimer (session.m_Server.GetService ())
{ {
} }
@ -20,10 +23,7 @@ namespace ssu
delete it.second; delete it.second;
} }
for (auto it: m_SentMessages) for (auto it: m_SentMessages)
{ delete it.second;
for (auto f: it.second)
delete[] f;
}
} }
void SSUData::ProcessSentMessageAck (uint32_t msgID) void SSUData::ProcessSentMessageAck (uint32_t msgID)
@ -31,19 +31,15 @@ namespace ssu
auto it = m_SentMessages.find (msgID); auto it = m_SentMessages.find (msgID);
if (it != m_SentMessages.end ()) if (it != m_SentMessages.end ())
{ {
// delete all ack-ed message's fragments delete it->second;
for (auto f: it->second)
delete[] f;
m_SentMessages.erase (it); m_SentMessages.erase (it);
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
} }
} }
void SSUData::ProcessMessage (uint8_t * buf, size_t len) void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
{ {
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
LogPrint ("Process SSU data flags=", (int)flag);
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{ {
// explicit ACKs // explicit ACKs
@ -60,13 +56,45 @@ namespace ssu
buf++; buf++;
for (int i = 0; i < numBitfields; i++) for (int i = 0; i < numBitfields; i++)
{ {
uint32_t msgID = be32toh (*(uint32_t *)buf);
buf += 4; // msgID buf += 4; // msgID
// TODO: process individual Ack bitfields auto it = m_SentMessages.find (msgID);
while (*buf & 0x80) // not last // process individual Ack bitfields
bool isNonLast = false;
int fragment = 0;
do
{
uint8_t bitfield = *buf;
isNonLast = bitfield & 0x80;
bitfield &= 0x7F; // clear MSB
if (bitfield && it != m_SentMessages.end ())
{
int numSentFragments = it->second->fragments.size ();
// process bits
uint8_t mask = 0x40;
for (int j = 0; j < 7; j++)
{
if (bitfield & mask)
{
if (fragment < numSentFragments)
{
delete it->second->fragments[fragment];
it->second->fragments[fragment] = nullptr;
}
}
fragment++;
mask >>= 1;
}
}
buf++; buf++;
buf++; // last byte }
while (isNonLast);
} }
} }
}
void SSUData::ProcessFragments (uint8_t * buf)
{
uint8_t numFragments = *buf; // number of fragments uint8_t numFragments = *buf; // number of fragments
buf++; buf++;
for (int i = 0; i < numFragments; i++) for (int i = 0; i < numFragments; i++)
@ -82,77 +110,124 @@ namespace ssu
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
LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last"); LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
// find message with msgID
I2NPMessage * msg = nullptr; I2NPMessage * msg = nullptr;
if (fragmentNum > 0) // follow-up fragment IncompleteMessage * incompleteMessage = nullptr;
auto it = m_IncomleteMessages.find (msgID);
if (it != m_IncomleteMessages.end ())
{
// message exists
incompleteMessage = it->second;
msg = incompleteMessage->msg;
}
else
{
// create new message
msg = NewI2NPMessage ();
msg->len -= sizeof (I2NPHeaderShort);
incompleteMessage = new IncompleteMessage (msg);
m_IncomleteMessages[msgID] = incompleteMessage;
}
// handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum)
{ {
auto it = m_IncomleteMessages.find (msgID); // expected fragment
if (it != m_IncomleteMessages.end ()) memcpy (msg->buf + msg->len, buf, fragmentSize);
msg->len += fragmentSize;
incompleteMessage->nextFragmentNum++;
if (!isLast && !incompleteMessage->savedFragments.empty ())
{ {
if (fragmentNum == it->second->nextFragmentNum) // try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
{ {
// expected fragment auto savedFragment = *it1;
msg = it->second->msg; if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
memcpy (msg->buf + msg->len, buf, fragmentSize); {
msg->len += fragmentSize; memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len);
it->second->nextFragmentNum++; msg->len += savedFragment->len;
} isLast = savedFragment->isLast;
else if (fragmentNum < it->second->nextFragmentNum) incompleteMessage->nextFragmentNum++;
// duplicate fragment incompleteMessage->savedFragments.erase (it1++);
LogPrint ("Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored"); delete savedFragment;
else }
{ else
// missing fragment break;
LogPrint ("Missing fragments from ", it->second->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
//TODO
} }
if (isLast) if (isLast)
{ LogPrint ("Message ", msgID, " complete");
if (!msg)
DeleteI2NPMessage (it->second->msg);
delete it->second;
m_IncomleteMessages.erase (it);
}
} }
else
// TODO:
LogPrint ("Unexpected follow-on fragment ", (int)fragmentNum, " of message ", msgID);
} }
else // first fragment else
{ {
msg = NewI2NPMessage (); if (fragmentNum < incompleteMessage->nextFragmentNum)
memcpy (msg->GetSSUHeader (), buf, fragmentSize); // duplicate fragment
msg->len += fragmentSize - sizeof (I2NPHeaderShort); LogPrint ("Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored");
else
{
// missing fragment
LogPrint ("Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
if (!incompleteMessage->savedFragments.insert (savedFragment).second)
{
LogPrint ("Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
delete savedFragment;
}
}
isLast = false;
} }
if (msg) if (isLast)
{ {
if (!fragmentNum && !isLast) // delete incomplete message
m_IncomleteMessages[msgID] = new IncompleteMessage (msg); delete incompleteMessage;
if (isLast) m_IncomleteMessages.erase (msgID);
// process message
SendMsgAck (msgID);
msg->FromSSU (msgID);
if (m_Session.GetState () == eSessionStateEstablished)
i2p::HandleI2NPMessage (msg);
else
{ {
SendMsgAck (msgID); // we expect DeliveryStatus
msg->FromSSU (msgID); if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
if (m_Session.GetState () == eSessionStateEstablished)
i2p::HandleI2NPMessage (msg);
else
{ {
// we expect DeliveryStatus LogPrint ("SSU session established");
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus) m_Session.Established ();
{
LogPrint ("SSU session established");
m_Session.Established ();
}
else
LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID);
DeleteI2NPMessage (msg);
} }
else
LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID);
DeleteI2NPMessage (msg);
} }
} }
else
SendFragmentAck (msgID, fragmentNum);
buf += fragmentSize; buf += fragmentSize;
} }
} }
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);
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
// extended data if presented
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
{
uint8_t extendedDataSize = *buf;
buf++; // size
LogPrint ("SSU extended data of ", extendedDataSize, " bytes presented");
buf += extendedDataSize;
}
// process data
ProcessFragments (buf);
}
void SSUData::Send (i2p::I2NPMessage * msg) void SSUData::Send (i2p::I2NPMessage * msg)
{ {
uint32_t msgID = msg->ToSSU (); uint32_t msgID = msg->ToSSU ();
@ -162,7 +237,13 @@ namespace ssu
DeleteI2NPMessage (msg); DeleteI2NPMessage (msg);
return; return;
} }
auto fragments = m_SentMessages[msgID]; if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
SentMessage * sentMessage = new SentMessage;
m_SentMessages[msgID] = sentMessage;
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
auto& fragments = sentMessage->fragments;
msgID = htobe32 (msgID); msgID = htobe32 (msgID);
size_t payloadSize = SSU_MTU - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) size_t payloadSize = SSU_MTU - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength (); size_t len = msg->GetLength ();
@ -171,8 +252,9 @@ namespace ssu
uint32_t fragmentNum = 0; uint32_t fragmentNum = 0;
while (len > 0) while (len > 0)
{ {
uint8_t * buf = new uint8_t[SSU_MTU + 18]; Fragment * fragment = new Fragment;
fragments.push_back (buf); uint8_t * buf = fragment->buf;
fragments.push_back (fragment);
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility *payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++; payload++;
@ -195,6 +277,7 @@ namespace ssu
size += payload - buf; size += payload - buf;
if (size & 0x0F) // make sure 16 bytes boundary if (size & 0x0F) // make sure 16 bytes boundary
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
fragment->len = size;
// encrypt message with session key // encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size); m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
@ -228,6 +311,77 @@ namespace ssu
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
m_Session.Send (buf, 48); m_Session.Send (buf, 48);
} }
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
{
if (fragmentNum > 64)
{
LogPrint ("Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACK bitfields
payload++;
// one ack
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
payload += 4;
div_t d = div (fragmentNum, 7);
memset (payload, 0x80, d.quot); // 0x80 means non-last
payload += d.quot;
*payload = 0x40 >> d.rem; // set corresponding bit
payload++;
*payload = 0; // number of fragments
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send (buf, len);
}
void SSUData::ScheduleResend()
{
m_ResendTimer.cancel ();
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
m_ResendTimer.async_wait (boost::bind (&SSUData::HandleResendTimer,
this, boost::asio::placeholders::error));
}
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
{
if (ts >= it->second->nextResendTime)
{
bool isEmpty = true;
for (auto f: it->second->fragments)
if (f)
{
isEmpty = false;
m_Session.Send (f->buf, f->len); // resend
}
it->second->numResends++;
if (isEmpty || it->second->numResends >= MAX_NUM_RESENDS)
{
delete it->second;
it = m_SentMessages.erase (it);
}
else
it++;
}
else
it++;
}
if (!m_SentMessages.empty ())
ScheduleResend ();
}
}
} }
} }

62
SSUData.h

@ -2,8 +2,11 @@
#define SSU_DATA_H__ #define SSU_DATA_H__
#include <inttypes.h> #include <inttypes.h>
#include <string.h>
#include <map> #include <map>
#include <vector> #include <vector>
#include <set>
#include <boost/asio.hpp>
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
namespace i2p namespace i2p
@ -11,6 +14,9 @@ namespace i2p
namespace ssu namespace ssu
{ {
const size_t SSU_MTU = 1484;
const int RESEND_INTERVAL = 3; // in seconds
const int MAX_NUM_RESENDS = 5;
// data flags // data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04; const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
@ -19,6 +25,45 @@ namespace ssu
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40; const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
struct Fragment
{
int fragmentNum;
size_t len;
bool isLast;
uint8_t buf[SSU_MTU + 18];
Fragment () = default;
Fragment (int n, const uint8_t * b, int l, bool last):
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); };
};
struct FragmentCmp
{
bool operator() (const Fragment * f1, const Fragment * f2) const
{
return f1->fragmentNum < f2->fragmentNum;
};
};
struct IncompleteMessage
{
I2NPMessage * msg;
int nextFragmentNum;
std::set<Fragment *, FragmentCmp> savedFragments;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0) {};
~IncompleteMessage () { for (auto it: savedFragments) { delete it; }; };
};
struct SentMessage
{
std::vector<Fragment *> fragments;
uint32_t nextResendTime; // in seconds
int numResends;
~SentMessage () { for (auto it: fragments) { delete it; }; };
};
class SSUSession; class SSUSession;
class SSUData class SSUData
{ {
@ -33,21 +78,20 @@ namespace ssu
private: private:
void SendMsgAck (uint32_t msgID); void SendMsgAck (uint32_t msgID);
void SendFragmentAck (uint32_t msgID, int fragmentNum);
void ProcessAcks (uint8_t *& buf, uint8_t flag);
void ProcessFragments (uint8_t * buf);
void ProcessSentMessageAck (uint32_t msgID); void ProcessSentMessageAck (uint32_t msgID);
private: void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
struct IncompleteMessage
{
I2NPMessage * msg;
uint8_t nextFragmentNum;
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (1) {}; private:
};
SSUSession& m_Session; SSUSession& m_Session;
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages; std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
std::map<uint32_t, std::vector<uint8_t *> > m_SentMessages; // msgID -> fragments std::map<uint32_t, SentMessage *> m_SentMessages;
boost::asio::deadline_timer m_ResendTimer;
}; };
} }
} }

17
Transports.cpp

@ -263,6 +263,23 @@ namespace i2p
} }
} }
void Transports::CloseSession (const i2p::data::RouterInfo * router)
{
if (!router) return;
m_Service.post (boost::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (const i2p::data::RouterInfo * router)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
if (ssuSession) // try SSU first
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint ("SSU session closed");
}
// TODO: delete NTCP
}
void Transports::DetectExternalIP () void Transports::DetectExternalIP ()
{ {
for (int i = 0; i < 5; i ++) for (int i = 0; i < 5; i ++)

2
Transports.h

@ -63,12 +63,14 @@ namespace i2p
i2p::ntcp::NTCPSession * FindNTCPSession (const i2p::data::IdentHash& ident); i2p::ntcp::NTCPSession * FindNTCPSession (const i2p::data::IdentHash& ident);
void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void CloseSession (const i2p::data::RouterInfo * router);
private: private:
void Run (); void Run ();
void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error); void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void PostCloseSession (const i2p::data::RouterInfo * router);
void DetectExternalIP (); void DetectExternalIP ();

4
Tunnel.cpp

@ -361,10 +361,12 @@ namespace tunnel
void Tunnels::ManageTunnels () void Tunnels::ManageTunnels ()
{ {
// check pending tunnel. if something is still there, wipe it out // check pending tunnel. if something is still there, wipe it out
// because it wouldn't be reponded anyway // because it wouldn't be responded anyway
for (auto& it : m_PendingTunnels) for (auto& it : m_PendingTunnels)
{ {
LogPrint ("Pending tunnel build request ", it.first, " has not been responded. Deleted"); LogPrint ("Pending tunnel build request ", it.first, " has not been responded. Deleted");
if (it.second->GetTunnelConfig ()->GetFirstHop ()->isGateway) // outbound
i2p::transports.CloseSession (it.second->GetTunnelConfig ()->GetFirstHop ()->router);
delete it.second; delete it.second;
} }
m_PendingTunnels.clear (); m_PendingTunnels.clear ();

2
TunnelPool.cpp

@ -188,7 +188,7 @@ namespace tunnel
hops.push_back (hop); hops.push_back (hop);
} }
std::reverse (hops.begin (), hops.end ()); std::reverse (hops.begin (), hops.end ());
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops)); auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops), outboundTunnel);
tunnel->SetTunnelPool (this); tunnel->SetTunnelPool (this);
} }

85
build/i2pd.pro

@ -1,85 +0,0 @@
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 \
../SOCKS.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 \
../SOCKS.h
OTHER_FILES += \
../README.md \
../Makefile \
../LICENSE

18
filelist.mk

@ -0,0 +1,18 @@
CPP_FILES := CryptoConst.cpp base64.cpp NTCPSession.cpp RouterInfo.cpp Transports.cpp \
RouterContext.cpp NetDb.cpp LeaseSet.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelGateway.cpp \
TransitTunnel.cpp I2NPProtocol.cpp Log.cpp Garlic.cpp HTTPServer.cpp Streaming.cpp Identity.cpp \
SSU.cpp util.cpp Reseed.cpp DaemonLinux.cpp SSUData.cpp i2p.cpp aes.cpp SOCKS.cpp UPnP.cpp \
TunnelPool.cpp HTTPProxy.cpp AddressBook.cpp Daemon.cpp
H_FILES := CryptoConst.h base64.h NTCPSession.h RouterInfo.h Transports.h \
RouterContext.h NetDb.h LeaseSet.h Tunnel.h TunnelEndpoint.h TunnelGateway.h \
TransitTunnel.h I2NPProtocol.h Log.h Garlic.h HTTPServer.h Streaming.h Identity.h \
SSU.h util.h Reseed.h DaemonLinux.h SSUData.h i2p.h aes.h SOCKS.h UPnP.h TunnelPool.h \
HTTPProxy.h AddressBook.h Daemon.h
OBJECTS = $(addprefix obj/, $(notdir $(CPP_FILES:.cpp=.o)))
Loading…
Cancel
Save