Browse Source

Merge pull request #503 from PurpleI2P/openssl

recent changes
pull/580/head
orignal 8 years ago
parent
commit
5e52b3609c
  1. 5
      AddressBook.cpp
  2. 1
      ClientContext.h
  3. 397
      Destination.cpp
  4. 107
      Destination.h
  5. 2
      Family.cpp
  6. 2
      Garlic.h
  7. 44
      HTTP.cpp
  8. 40
      HTTP.h
  9. 35
      HTTPProxy.cpp
  10. 7
      HTTPProxy.h
  11. 27
      HTTPServer.cpp
  12. 6
      I2CP.cpp
  13. 25
      I2CP.h
  14. 27
      I2NPProtocol.cpp
  15. 3
      I2NPProtocol.h
  16. 157
      I2PControl.cpp
  17. 3
      I2PControl.h
  18. 8
      Identity.h
  19. 96
      LeaseSet.cpp
  20. 31
      LeaseSet.h
  21. 6
      RouterContext.h
  22. 4
      Tunnel.cpp
  23. 3
      debian/i2pd.1
  24. 14
      tests/test-http-req.cpp
  25. 15
      tests/test-http-res.cpp
  26. 2
      tests/test-http-url.cpp
  27. 2
      tests/test-http-url_decode.cpp

5
AddressBook.cpp

@ -5,7 +5,6 @@
#include <fstream> #include <fstream>
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>
#include <boost/lexical_cast.hpp>
#include <openssl/rand.h> #include <openssl/rand.h>
#include "Base.h" #include "Base.h"
#include "util.h" #include "util.h"
@ -716,12 +715,14 @@ namespace client
if (status == 200) // OK if (status == 200) // OK
{ {
bool isChunked = false, isGzip = false; bool isChunked = false, isGzip = false;
m_Etag = ""; m_LastModified = "";
std::string header, statusMessage; std::string header, statusMessage;
std::getline (response, statusMessage); std::getline (response, statusMessage);
// read until new line meaning end of header // read until new line meaning end of header
while (!response.eof () && header != "\r") while (!response.eof () && header != "\r")
{ {
std::getline (response, header); std::getline (response, header);
if (response.fail ()) break;
auto colon = header.find (':'); auto colon = header.find (':');
if (colon != std::string::npos) if (colon != std::string::npos)
{ {
@ -741,7 +742,7 @@ namespace client
} }
} }
LogPrint (eLogInfo, "Addressbook: received ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); LogPrint (eLogInfo, "Addressbook: received ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
if (!response.eof ()) if (!response.eof () && !response.fail ())
{ {
success = true; success = true;
if (!isChunked) if (!isChunked)

1
ClientContext.h

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Destination.h" #include "Destination.h"
#include "I2PService.h"
#include "HTTPProxy.h" #include "HTTPProxy.h"
#include "SOCKS.h" #include "SOCKS.h"
#include "I2PTunnel.h" #include "I2PTunnel.h"

397
Destination.cpp

@ -13,17 +13,11 @@ namespace i2p
{ {
namespace client namespace client
{ {
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
const std::map<std::string, std::string> * params): m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service) m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
{ {
if (m_IsPublic)
PersistTemporaryKeys ();
else
i2p::crypto::GenerateElGamalKeyPair(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;
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY; int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
@ -103,11 +97,9 @@ namespace client
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity); m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
if (explicitPeers) if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers); m_Pool->SetExplicitPeers (explicitPeers);
if (m_IsPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
} }
ClientDestination::~ClientDestination () LeaseSetDestination::~LeaseSetDestination ()
{ {
if (m_IsRunning) if (m_IsRunning)
Stop (); Stop ();
@ -116,11 +108,9 @@ namespace client
m_LeaseSetRequests.clear (); m_LeaseSetRequests.clear ();
if (m_Pool) if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
if (m_DatagramDestination)
delete m_DatagramDestination;
} }
void ClientDestination::Run () void LeaseSetDestination::Run ()
{ {
while (m_IsRunning) while (m_IsRunning)
{ {
@ -135,26 +125,29 @@ namespace client
} }
} }
void ClientDestination::Start () bool LeaseSetDestination::Start ()
{ {
if (!m_IsRunning) if (!m_IsRunning)
{ {
m_IsRunning = true; m_IsRunning = true;
if (m_IsPublic)
PersistTemporaryKeys ();
else
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
m_Pool->SetLocalDestination (shared_from_this ()); m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true); m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&ClientDestination::Run, shared_from_this ())); m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this ()); // TODO:
m_StreamingDestination->Start ();
for (auto it: m_StreamingDestinationsByPorts)
it.second->Start ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer, m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
return true;
} }
else
return false;
} }
void ClientDestination::Stop () bool LeaseSetDestination::Stop ()
{ {
if (m_IsRunning) if (m_IsRunning)
{ {
@ -162,16 +155,6 @@ namespace client
m_PublishConfirmationTimer.cancel (); m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel (); m_PublishVerificationTimer.cancel ();
m_IsRunning = false; m_IsRunning = false;
m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto it: m_StreamingDestinationsByPorts)
it.second->Stop ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
if (m_Pool) if (m_Pool)
{ {
m_Pool->SetLocalDestination (nullptr); m_Pool->SetLocalDestination (nullptr);
@ -184,10 +167,13 @@ namespace client
delete m_Thread; delete m_Thread;
m_Thread = 0; m_Thread = 0;
} }
return true;
} }
else
return false;
} }
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident) std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{ {
auto it = m_RemoteLeaseSets.find (ident); auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ()) if (it != m_RemoteLeaseSets.end ())
@ -210,7 +196,7 @@ namespace client
return nullptr; return nullptr;
} }
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::GetLeaseSet () std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
{ {
if (!m_Pool) return nullptr; if (!m_Pool) return nullptr;
if (!m_LeaseSet) if (!m_LeaseSet)
@ -218,12 +204,17 @@ namespace client
return m_LeaseSet; return m_LeaseSet;
} }
void ClientDestination::UpdateLeaseSet () void LeaseSetDestination::UpdateLeaseSet ()
{ {
m_LeaseSet.reset (new i2p::data::LeaseSet (m_Pool)); int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), GetEncryptionPublicKey (),
m_Pool->GetInboundTunnels (numTunnels));
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
m_LeaseSet.reset (leaseSet);
} }
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{ {
struct struct
{ {
@ -239,17 +230,17 @@ namespace client
return true; return true;
} }
void ClientDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{ {
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, shared_from_this (), msg)); m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
} }
void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, shared_from_this (), msg)); m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
} }
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{ {
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET]; uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID) switch (typeID)
@ -272,7 +263,7 @@ namespace client
} }
} }
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
{ {
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
size_t offset = DATABASE_STORE_HEADER_SIZE; size_t offset = DATABASE_STORE_HEADER_SIZE;
@ -336,7 +327,7 @@ namespace client
} }
} }
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len) void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
{ {
i2p::data::IdentHash key (buf); i2p::data::IdentHash key (buf);
int num = buf[32]; // num int num = buf[32]; // num
@ -379,7 +370,7 @@ namespace client
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
} }
void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken) if (msgID == m_PublishReplyToken)
@ -389,14 +380,14 @@ namespace client
m_PublishReplyToken = 0; m_PublishReplyToken = 0;
// schedule verification // schedule verification
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer, m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
else else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg); i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
} }
void ClientDestination::SetLeaseSetUpdated () void LeaseSetDestination::SetLeaseSetUpdated ()
{ {
i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
UpdateLeaseSet (); UpdateLeaseSet ();
@ -407,7 +398,7 @@ namespace client
} }
} }
void ClientDestination::Publish () void LeaseSetDestination::Publish ()
{ {
if (!m_LeaseSet || !m_Pool) if (!m_LeaseSet || !m_Pool)
{ {
@ -425,6 +416,12 @@ namespace client
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
return; return;
} }
auto inbound = m_Pool->GetNextInboundTunnel ();
if (!inbound)
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
return;
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills); auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
if (!floodfill) if (!floodfill)
{ {
@ -435,14 +432,14 @@ namespace client
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken)); auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken, inbound));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer, m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
} }
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode) void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
@ -455,7 +452,7 @@ namespace client
} }
} }
void ClientDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode) void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
@ -471,7 +468,7 @@ namespace client
// we got latest LeasetSet // we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified"); LogPrint (eLogDebug, "Destination: published LeaseSet verified");
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return; return;
} }
} }
@ -483,129 +480,18 @@ namespace client
} }
} }
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
uint32_t length = bufbe32toh (buf);
buf += 4;
// we assume I2CP payload
uint16_t fromPort = bufbe16toh (buf + 4), // source
toPort = bufbe16toh (buf + 6); // destination
switch (buf[9])
{
case PROTOCOL_TYPE_STREAMING:
{
// streaming protocol
auto dest = GetStreamingDestination (toPort);
if (dest)
dest->HandleDataMessagePayload (buf, length);
else
LogPrint (eLogError, "Destination: Missing streaming destination");
}
break;
case PROTOCOL_TYPE_DATAGRAM:
// datagram protocol
if (m_DatagramDestination)
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
else
LogPrint (eLogError, "Destination: Missing datagram destination");
break;
default:
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
{
if (!streamRequestComplete)
{
LogPrint (eLogError, "Destination: request callback is not specified in CreateStream");
return;
}
auto leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (leaseSet, port));
else
{
auto s = shared_from_this ();
RequestDestination (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
streamRequestComplete(s->CreateStream (ls, port));
else
streamRequestComplete (nullptr);
});
}
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
if (m_StreamingDestination)
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
else
return nullptr;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const
{
if (port)
{
auto it = m_StreamingDestinationsByPorts.find (port);
if (it != m_StreamingDestinationsByPorts.end ())
return it->second;
}
// if port is zero or not found, use default destination
return m_StreamingDestination;
}
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
{
if (m_StreamingDestination)
m_StreamingDestination->SetAcceptor (acceptor);
}
void ClientDestination::StopAcceptingStreams ()
{
if (m_StreamingDestination)
m_StreamingDestination->ResetAcceptor ();
}
bool ClientDestination::IsAcceptingStreams () const
{
if (m_StreamingDestination)
return m_StreamingDestination->IsAcceptorSet ();
return false;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
{
auto dest = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this (), port, gzip);
if (port)
m_StreamingDestinationsByPorts[port] = dest;
else // update default
m_StreamingDestination = dest;
return dest;
}
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{
if (!m_DatagramDestination)
m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
return m_DatagramDestination;
}
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{ {
if (!m_Pool || !IsReady ()) if (!m_Pool || !IsReady ())
{ {
if (requestComplete) requestComplete (nullptr); if (requestComplete) requestComplete (nullptr);
return false; return false;
} }
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete)); m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true; return true;
} }
void ClientDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest) void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
{ {
auto s = shared_from_this (); auto s = shared_from_this ();
m_Service.post ([dest, s](void) m_Service.post ([dest, s](void)
@ -620,7 +506,7 @@ namespace client
}); });
} }
void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete) void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{ {
std::set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
@ -652,7 +538,7 @@ namespace client
} }
} }
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest, bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request) std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
{ {
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ()) if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
@ -685,7 +571,7 @@ namespace client
} }
}); });
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer, request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
shared_from_this (), std::placeholders::_1, dest)); shared_from_this (), std::placeholders::_1, dest));
} }
else else
@ -693,7 +579,7 @@ namespace client
return true; return true;
} }
void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest) void LeaseSetDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
@ -731,19 +617,19 @@ namespace client
} }
} }
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode) void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
CleanupExpiredTags (); CleanupExpiredTags ();
CleanupRemoteLeaseSets (); CleanupRemoteLeaseSets ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer, m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
} }
void ClientDestination::CleanupRemoteLeaseSets () void LeaseSetDestination::CleanupRemoteLeaseSets ()
{ {
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();) for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
@ -758,7 +644,7 @@ namespace client
} }
} }
void ClientDestination::PersistTemporaryKeys () void LeaseSetDestination::PersistTemporaryKeys ()
{ {
std::string ident = GetIdentHash().ToBase32(); std::string ident = GetIdentHash().ToBase32();
std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat")); std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat"));
@ -780,6 +666,165 @@ namespace client
return; return;
} }
LogPrint(eLogError, "Destinations: Can't save keys to ", path); LogPrint(eLogError, "Destinations: Can't save keys to ", path);
}
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (isPublic, params),
m_Keys (keys), m_DatagramDestination (nullptr)
{
if (isPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
}
ClientDestination::~ClientDestination ()
{
if (m_DatagramDestination)
delete m_DatagramDestination;
}
bool ClientDestination::Start ()
{
if (LeaseSetDestination::Start ())
{
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto it: m_StreamingDestinationsByPorts)
it.second->Start ();
return true;
}
else
return false;
}
bool ClientDestination::Stop ()
{
if (LeaseSetDestination::Stop ())
{
m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto it: m_StreamingDestinationsByPorts)
it.second->Stop ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
return true;
}
else
return false;
}
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = bufbe32toh (buf);
buf += 4;
// we assume I2CP payload
uint16_t fromPort = bufbe16toh (buf + 4), // source
toPort = bufbe16toh (buf + 6); // destination
switch (buf[9])
{
case PROTOCOL_TYPE_STREAMING:
{
// streaming protocol
auto dest = GetStreamingDestination (toPort);
if (dest)
dest->HandleDataMessagePayload (buf, length);
else
LogPrint (eLogError, "Destination: Missing streaming destination");
}
break;
case PROTOCOL_TYPE_DATAGRAM:
// datagram protocol
if (m_DatagramDestination)
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
else
LogPrint (eLogError, "Destination: Missing datagram destination");
break;
default:
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
}
}
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
{
if (!streamRequestComplete)
{
LogPrint (eLogError, "Destination: request callback is not specified in CreateStream");
return;
}
auto leaseSet = FindLeaseSet (dest);
if (leaseSet)
streamRequestComplete(CreateStream (leaseSet, port));
else
{
auto s = GetSharedFromThis ();
RequestDestination (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
streamRequestComplete(s->CreateStream (ls, port));
else
streamRequestComplete (nullptr);
});
}
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
if (m_StreamingDestination)
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
else
return nullptr;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const
{
if (port)
{
auto it = m_StreamingDestinationsByPorts.find (port);
if (it != m_StreamingDestinationsByPorts.end ())
return it->second;
}
// if port is zero or not found, use default destination
return m_StreamingDestination;
}
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
{
if (m_StreamingDestination)
m_StreamingDestination->SetAcceptor (acceptor);
}
void ClientDestination::StopAcceptingStreams ()
{
if (m_StreamingDestination)
m_StreamingDestination->ResetAcceptor ();
}
bool ClientDestination::IsAcceptingStreams () const
{
if (m_StreamingDestination)
return m_StreamingDestination->IsAcceptorSet ();
return false;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
{
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
if (port)
m_StreamingDestinationsByPorts[port] = dest;
else // update default
m_StreamingDestination = dest;
return dest;
}
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{
if (!m_DatagramDestination)
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
return m_DatagramDestination;
} }
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const

107
Destination.h

@ -49,8 +49,8 @@ namespace client
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete; typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class ClientDestination: public i2p::garlic::GarlicDestination, class LeaseSetDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<ClientDestination> public std::enable_shared_from_this<LeaseSetDestination>
{ {
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete; typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found // leaseSet = nullptr means not found
@ -68,11 +68,11 @@ namespace client
public: public:
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr); LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination (); ~LeaseSetDestination ();
virtual void Start (); virtual bool Start ();
virtual void Stop (); virtual bool Stop ();
bool IsRunning () const { return m_IsRunning; }; bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; }; std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
@ -80,28 +80,13 @@ namespace client
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident); std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
void CancelDestinationRequest (const i2p::data::IdentHash& dest); void CancelDestinationRequest (const i2p::data::IdentHash& dest);
// streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
// datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
// implements LocalDestination // implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; }; const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
// implements GarlicDestination // implements GarlicDestination
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet (); std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; } std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from); void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@ -111,9 +96,11 @@ namespace client
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated (); void SetLeaseSetUpdated ();
// I2CP protected:
void HandleDataMessage (const uint8_t * buf, size_t len);
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
private: private:
void Run (); void Run ();
@ -129,29 +116,26 @@ namespace client
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode); void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets (); void CleanupRemoteLeaseSets ();
void PersistTemporaryKeys ();
void PersistTemporaryKeys ();
private: private:
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
volatile 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;
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets; std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests; std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool; std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::shared_ptr<i2p::data::LeaseSet> m_LeaseSet; std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic; bool m_IsPublic;
uint32_t m_PublishReplyToken; uint32_t m_PublishReplyToken;
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination;
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer; boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
@ -159,6 +143,59 @@ namespace client
// for HTTP only // for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); }; int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
};
class ClientDestination: public LeaseSetDestination
{
public:
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
bool Start ();
bool Stop ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
// streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
// datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
// implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
protected:
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
private:
std::shared_ptr<ClientDestination> GetSharedFromThis ()
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
private:
i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination;
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const; std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
}; };
} }

2
Family.cpp

@ -94,7 +94,7 @@ namespace data
int numCertificates = 0; int numCertificates = 0;
if (!i2p::fs::ReadDir(certDir, files)) { if (!i2p::fs::ReadDir(certDir, files)) {
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir); LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir);
return; return;
} }

2
Garlic.h

@ -163,7 +163,7 @@ namespace garlic
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated (); virtual void SetLeaseSetUpdated ();
virtual std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () = 0; // TODO virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0; virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0; virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;

44
HTTP.cpp

@ -8,6 +8,7 @@
#include "HTTP.h" #include "HTTP.h"
#include <algorithm> #include <algorithm>
#include <ctime>
namespace i2p { namespace i2p {
namespace http { namespace http {
@ -44,9 +45,10 @@ namespace http {
bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) { bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) {
std::size_t pos = 0; std::size_t pos = 0;
std::size_t len = 2; /* strlen(": ") */ std::size_t len = 2; /* strlen(": ") */
std::size_t max = line.length();
if ((pos = line.find(": ", pos)) == std::string::npos) if ((pos = line.find(": ", pos)) == std::string::npos)
return false; return false;
while (isspace(line.at(pos + len))) while ((pos + len) < max && isspace(line.at(pos + len)))
len++; len++;
std::string name = line.substr(0, pos); std::string name = line.substr(0, pos);
std::string value = line.substr(pos + len); std::string value = line.substr(pos + len);
@ -54,6 +56,13 @@ namespace http {
return true; return true;
} }
void gen_rfc1123_date(std::string & out) {
std::time_t now = std::time(nullptr);
char buf[128];
std::strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&now));
out = buf;
}
bool URL::parse(const char *str, std::size_t len) { bool URL::parse(const char *str, std::size_t len) {
std::string url(str, len ? len : strlen(str)); std::string url(str, len ? len : strlen(str));
return parse(url); return parse(url);
@ -184,6 +193,25 @@ namespace http {
return out; return out;
} }
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) {
add_header(name, value.c_str(), replace);
}
void HTTPMsg::add_header(const char *name, const char *value, bool replace) {
std::size_t count = headers.count(name);
if (count && !replace)
return;
if (count) {
headers[name] = value;
return;
}
headers.insert(std::pair<std::string, std::string>(name, value));
}
void HTTPMsg::del_header(const char *name) {
headers.erase(name);
}
int HTTPReq::parse(const char *buf, size_t len) { int HTTPReq::parse(const char *buf, size_t len) {
std::string str(buf, len); std::string str(buf, len);
return parse(str); return parse(str);
@ -256,7 +284,7 @@ namespace http {
return false; return false;
} }
long int HTTPRes::length() { long int HTTPMsg::length() {
unsigned long int length = 0; unsigned long int length = 0;
auto it = headers.find("Content-Length"); auto it = headers.find("Content-Length");
if (it == headers.end()) if (it == headers.end())
@ -311,12 +339,24 @@ namespace http {
} }
std::string HTTPRes::to_string() { std::string HTTPRes::to_string() {
if (version == "HTTP/1.1" && headers.count("Date") == 0) {
std::string date;
gen_rfc1123_date(date);
add_header("Date", date.c_str());
}
if (status == "OK" && code != 200)
status = HTTPCodeToStatus(code); // update
if (body.length() > 0 && headers.count("Content-Length") == 0)
add_header("Content-Length", std::to_string(body.length()).c_str());
/* build response */
std::stringstream ss; std::stringstream ss;
ss << version << " " << code << " " << status << CRLF; ss << version << " " << code << " " << status << CRLF;
for (auto & h : headers) { for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF; ss << h.first << ": " << h.second << CRLF;
} }
ss << CRLF; ss << CRLF;
if (body.length() > 0)
ss << body;
return ss.str(); return ss.str();
} }

40
HTTP.h

@ -54,8 +54,18 @@ namespace http {
std::string to_string (); std::string to_string ();
}; };
struct HTTPReq { struct HTTPMsg {
std::map<std::string, std::string> headers; std::map<std::string, std::string> headers;
void add_header(const char *name, std::string & value, bool replace = false);
void add_header(const char *name, const char *value, bool replace = false);
void del_header(const char *name);
/** @brief Returns declared message length or -1 if unknown */
long int length();
};
struct HTTPReq : HTTPMsg {
std::string version; std::string version;
std::string method; std::string method;
std::string uri; std::string uri;
@ -75,11 +85,16 @@ namespace http {
std::string to_string(); std::string to_string();
}; };
struct HTTPRes { struct HTTPRes : HTTPMsg {
std::map<std::string, std::string> headers;
std::string version; std::string version;
std::string status; std::string status;
unsigned short int code; unsigned short int code;
/** simplifies response generation
* If this variable is set:
* a) Content-Length header will be added if missing
* b) contents of body will be included in response
*/
std::string body;
HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {} HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {}
@ -91,14 +106,17 @@ namespace http {
int parse(const char *buf, size_t len); int parse(const char *buf, size_t len);
int parse(const std::string& buf); int parse(const std::string& buf);
/** @brief Serialize HTTP response to string */ /**
* @brief Serialize HTTP response to string
* @note If version is set to HTTP/1.1, and Date header is missing,
* it will be generated based on current time and added to headers
* @note If body member is set and Content-Length header is missing,
* this header will be added, based on body's length
*/
std::string to_string(); std::string to_string();
/** @brief Checks that response declared as chunked data */ /** @brief Checks that response declared as chunked data */
bool is_chunked(); bool is_chunked();
/** @brief Returns declared response length or -1 if unknown */
long int length();
}; };
/** /**
@ -115,6 +133,14 @@ namespace http {
* @return Decoded string * @return Decoded string
*/ */
std::string UrlDecode(const std::string& data, bool null = false); std::string UrlDecode(const std::string& data, bool null = false);
/**
* @brief Merge HTTP response content with Transfer-Encoding: chunked
* @param in Input stream
* @param out Output stream
* @return true on success, false otherwise
*/
bool MergeChunkedResponse (std::istream& in, std::ostream& out);
} // http } // http
} // i2p } // i2p

35
HTTPProxy.cpp

@ -4,6 +4,13 @@
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <string> #include <string>
#include <atomic> #include <atomic>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include "I2PService.h"
#include "Destination.h"
#include "HTTPProxy.h" #include "HTTPProxy.h"
#include "util.h" #include "util.h"
#include "Identity.h" #include "Identity.h"
@ -36,7 +43,7 @@ namespace proxy
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate(); void Terminate();
void AsyncSockRead(); void AsyncSockRead();
void HTTPRequestFailed(/*std::string message*/); void HTTPRequestFailed(const char *message);
void RedirectToJumpService(); void RedirectToJumpService();
void ExtractRequest(); void ExtractRequest();
bool IsI2PAddress(); bool IsI2PAddress();
@ -49,6 +56,7 @@ namespace proxy
uint8_t m_http_buff[http_buffer_size]; uint8_t m_http_buff[http_buffer_size];
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock; std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::string m_request; //Data left to be sent std::string m_request; //Data left to be sent
std::string m_Response;
std::string m_url; //URL std::string m_url; //URL
std::string m_method; //Method std::string m_method; //Method
std::string m_version; //HTTP version std::string m_version; //HTTP version
@ -91,10 +99,17 @@ namespace proxy
/* All hope is lost beyond this point */ /* All hope is lost beyond this point */
//TODO: handle this apropriately //TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/) void HTTPProxyHandler::HTTPRequestFailed(const char *message)
{ {
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n"; std::size_t size = std::strlen(message);
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()), std::stringstream ss;
ss << "HTTP/1.0 500 Internal Server Error\r\n"
<< "Content-Type: text/plain\r\n";
ss << "Content-Length: " << std::to_string(size + 2) << "\r\n"
<< "\r\n"; /* end of headers */
ss << message << "\r\n";
m_Response = ss.str();
boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
} }
@ -105,7 +120,8 @@ namespace proxy
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n"; response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response.str (),response.str ().length ()), m_Response = response.str ();
boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
} }
@ -139,7 +155,7 @@ namespace proxy
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
{ {
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version); LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
HTTPRequestFailed(); //TODO: send right stuff HTTPRequestFailed("unsupported HTTP version");
return false; return false;
} }
return true; return true;
@ -276,13 +292,13 @@ namespace proxy
case '\n': EnterState(DONE); break; case '\n': EnterState(DONE); break;
default: default:
LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff)); LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
HTTPRequestFailed(); //TODO: add correct code HTTPRequestFailed("rejected invalid request");
return false; return false;
} }
break; break;
default: default:
LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state); LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
HTTPRequestFailed(); //TODO: add correct code 500 HTTPRequestFailed("invalid parser state");
return false; return false;
} }
http_buff++; http_buff++;
@ -338,7 +354,7 @@ namespace proxy
else else
{ {
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info"); LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable HTTPRequestFailed("error when creating the stream, check logs");
} }
} }
@ -351,6 +367,5 @@ namespace proxy
{ {
return std::make_shared<HTTPProxyHandler> (this, socket); return std::make_shared<HTTPProxyHandler> (this, socket);
} }
} }
} }

7
HTTPProxy.h

@ -1,13 +1,6 @@
#ifndef HTTP_PROXY_H__ #ifndef HTTP_PROXY_H__
#define HTTP_PROXY_H__ #define HTTP_PROXY_H__
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include "I2PService.h"
#include "Destination.h"
namespace i2p namespace i2p
{ {
namespace proxy namespace proxy

27
HTTPServer.cpp

@ -1,4 +1,3 @@
#include <ctime>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <thread> #include <thread>
@ -241,15 +240,15 @@ namespace http {
if ((num = seconds / 86400) > 0) { if ((num = seconds / 86400) > 0) {
s << num << " days, "; s << num << " days, ";
seconds -= num; seconds -= num * 86400;
} }
if ((num = seconds / 3600) > 0) { if ((num = seconds / 3600) > 0) {
s << num << " hours, "; s << num << " hours, ";
seconds -= num; seconds -= num * 3600;
} }
if ((num = seconds / 60) > 0) { if ((num = seconds / 60) > 0) {
s << num << " min, "; s << num << " min, ";
seconds -= num; seconds -= num * 60;
} }
s << seconds << " seconds"; s << seconds << " seconds";
} }
@ -749,7 +748,7 @@ namespace http {
if (needAuth && !CheckAuth(req)) { if (needAuth && !CheckAuth(req)) {
res.code = 401; res.code = 401;
res.headers.insert(std::pair<std::string, std::string>("WWW-Authenticate", "Basic realm=\"WebAdmin\"")); res.add_header("WWW-Authenticate", "Basic realm=\"WebAdmin\"");
SendReply(res, content); SendReply(res, content);
return; return;
} }
@ -763,6 +762,8 @@ namespace http {
else else
ShowStatus (s); ShowStatus (s);
ShowPageTail (s); ShowPageTail (s);
res.code = 200;
content = s.str (); content = s.str ();
SendReply (res, content); SendReply (res, content);
} }
@ -845,21 +846,11 @@ namespace http {
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content) void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
{ {
std::time_t time_now = std::time(nullptr); reply.add_header("Content-Type", "text/html");
char time_buff[128]; reply.body = content;
std::strftime(time_buff, sizeof(time_buff), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&time_now));
reply.status = HTTPCodeToStatus(reply.code);
reply.headers.insert(std::pair<std::string, std::string>("Date", time_buff));
reply.headers.insert(std::pair<std::string, std::string>("Content-Type", "text/html"));
reply.headers.insert(std::pair<std::string, std::string>("Content-Length", std::to_string(content.size())));
std::string res = reply.to_string(); std::string res = reply.to_string();
std::vector<boost::asio::const_buffer> buffers; boost::asio::async_write (*m_Socket, boost::asio::buffer(res),
buffers.push_back(boost::asio::buffer(res));
buffers.push_back(boost::asio::buffer(content));
boost::asio::async_write (*m_Socket, buffers,
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1)); std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
} }

6
I2CP.cpp

@ -8,6 +8,12 @@ namespace i2p
{ {
namespace client namespace client
{ {
I2CPDestination::I2CPDestination (I2CPSession& owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic):
LeaseSetDestination (isPublic), m_Owner (owner), m_Identity (identity)
{
}
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket): I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Owner (owner), m_Socket (socket), m_Owner (owner), m_Socket (socket),
m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0) m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0)

25
I2CP.h

@ -5,6 +5,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Destination.h"
namespace i2p namespace i2p
{ {
@ -19,6 +20,28 @@ namespace client
const uint8_t I2CP_GET_DATE_MESSAGE = 32; const uint8_t I2CP_GET_DATE_MESSAGE = 32;
class I2CPSession;
class I2CPDestination: public LeaseSetDestination
{
public:
I2CPDestination (I2CPSession& owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic);
protected:
// implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { /* TODO */};
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len) {};
private:
I2CPSession& m_Owner;
std::shared_ptr<const i2p::data::IdentityEx> m_Identity;
};
class I2CPServer; class I2CPServer;
class I2CPSession: public std::enable_shared_from_this<I2CPSession> class I2CPSession: public std::enable_shared_from_this<I2CPSession>
{ {
@ -44,6 +67,8 @@ namespace client
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket; std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
uint8_t m_Buffer[I2CP_SESSION_BUFFER_SIZE], * m_NextMessage; uint8_t m_Buffer[I2CP_SESSION_BUFFER_SIZE], * m_NextMessage;
size_t m_NextMessageLen, m_NextMessageOffset; size_t m_NextMessageLen, m_NextMessageOffset;
std::shared_ptr<I2CPDestination> m_Destination;
}; };
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);

27
I2NPProtocol.cpp

@ -249,7 +249,23 @@ namespace i2p
return m; return m;
} }
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken) std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
size_t size = DATABASE_STORE_HEADER_SIZE;
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
size += leaseSet->GetBufferLen ();
m->len += size;
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{ {
if (!leaseSet) return nullptr; if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage (); auto m = NewI2NPShortMessage ();
@ -258,14 +274,13 @@ namespace i2p
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE; size_t size = DATABASE_STORE_HEADER_SIZE;
if (replyToken) if (replyToken && replyTunnel)
{ {
auto leases = leaseSet->GetNonExpiredLeases (); if (replyTunnel)
if (leases.size () > 0)
{ {
htobe32buf (payload + size, leases[0]->tunnelID); htobe32buf (payload + size, replyTunnel->GetNextTunnelID ());
size += 4; // reply tunnelID size += 4; // reply tunnelID
memcpy (payload + size, leases[0]->tunnelGateway, 32); memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32);
size += 32; // reply tunnel gateway size += 32; // reply tunnel gateway
} }
else else

3
I2NPProtocol.h

@ -224,7 +224,8 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers); std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0); std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0); std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg); bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);

157
I2PControl.cpp

@ -2,8 +2,6 @@
#include <sstream> #include <sstream>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/pem.h> #include <openssl/pem.h>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
@ -16,6 +14,7 @@
#include "Crypto.h" #include "Crypto.h"
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
#include "HTTP.h"
#include "Config.h" #include "Config.h"
#include "NetDb.h" #include "NetDb.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -189,71 +188,64 @@ namespace client
if (ecode) { if (ecode) {
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ()); LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
return; return;
}
/* try to parse received data */
std::stringstream json;
std::string response;
bool isHTTP = false;
if (memcmp (buf->data (), "POST", 4) == 0) {
long int remains = 0;
isHTTP = true;
i2p::http::HTTPReq req;
std::size_t len = req.parse(buf->data(), bytes_transferred);
if (len <= 0) {
LogPrint(eLogError, "I2PControl: incomplete/malformed POST request");
return;
}
/* append to json chunk of data from 1st request */
json.write(buf->begin() + len, bytes_transferred - len);
remains = req.length() - len;
/* if request has Content-Length header, fetch rest of data and store to json buffer */
while (remains > 0) {
len = ((long int) buf->size() < remains) ? buf->size() : remains;
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len));
json.write(buf->begin(), bytes_transferred);
remains -= bytes_transferred;
}
} else { } else {
try json.write(buf->begin(), bytes_transferred);
{ }
bool isHtml = !memcmp (buf->data (), "POST", 4); LogPrint(eLogDebug, "I2PControl: json from request: ", json.str());
std::stringstream ss;
ss.write (buf->data (), bytes_transferred);
if (isHtml)
{
std::string header;
size_t contentLength = 0;
while (!ss.eof () && header != "\r")
{
std::getline(ss, header);
auto colon = header.find (':');
if (colon != std::string::npos && header.substr (0, colon) == "Content-Length")
contentLength = std::stoi (header.substr (colon + 1));
}
if (ss.eof ())
{
LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected");
return; // TODO:
}
std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read
if (rem > 0)
{
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem));
ss.write (buf->data (), bytes_transferred);
}
}
std::ostringstream response;
#if GCC47_BOOST149 #if GCC47_BOOST149
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7"); LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
response << "{\"id\":null,\"error\":"; BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost");
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
response << "\"jsonrpc\":\"2.0\"}";
#else #else
boost::property_tree::ptree pt; /* now try to parse json itself */
boost::property_tree::read_json (ss, pt); try {
boost::property_tree::ptree pt;
std::string id = pt.get<std::string>("id"); boost::property_tree::read_json (json, pt);
std::string method = pt.get<std::string>("method");
auto it = m_MethodHandlers.find (method); std::string id = pt.get<std::string>("id");
if (it != m_MethodHandlers.end ()) std::string method = pt.get<std::string>("method");
{ auto it = m_MethodHandlers.find (method);
response << "{\"id\":" << id << ",\"result\":{"; if (it != m_MethodHandlers.end ()) {
(this->*(it->second))(pt.get_child ("params"), response); std::ostringstream ss;
response << "},\"jsonrpc\":\"2.0\"}"; ss << "{\"id\":" << id << ",\"result\":{";
} else { (this->*(it->second))(pt.get_child ("params"), ss);
LogPrint (eLogWarning, "I2PControl: unknown method ", method); ss << "},\"jsonrpc\":\"2.0\"}";
response << "{\"id\":null,\"error\":"; response = ss.str();
response << "{\"code\":-32601,\"message\":\"Method not found\"},"; } else {
response << "\"jsonrpc\":\"2.0\"}"; LogPrint (eLogWarning, "I2PControl: unknown method ", method);
} BuildErrorResponse(response, 32601, "Method not found");
#endif
SendResponse (socket, buf, response, isHtml);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
}
catch (...)
{
LogPrint (eLogError, "I2PControl: handle request unknown exception");
} }
} catch (std::exception& ex) {
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
BuildErrorResponse(response, 32603, ex.what());
} catch (...) {
LogPrint (eLogError, "I2PControl: handle request unknown exception");
} }
#endif
SendResponse (socket, buf, response, isHTTP);
} }
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
@ -275,27 +267,28 @@ namespace client
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
} }
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
std::stringstream ss;
ss << "{\"id\":null,\"error\":";
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
ss << "\"jsonrpc\":\"2.0\"}";
content = ss.str();
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket, void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml) std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
{ {
size_t len = response.str ().length (), offset = 0; if (isHTTP) {
if (isHtml) i2p::http::HTTPRes res;
{ res.code = 200;
std::ostringstream header; res.add_header("Content-Type", "application/json");
header << "HTTP/1.1 200 OK\r\n"; res.add_header("Connection", "close");
header << "Connection: close\r\n"; res.body = content;
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n"; std::string tmp = res.to_string();
header << "Content-Type: application/json\r\n"; content = tmp;
header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
header.imbue(std::locale (header.getloc(), facet));
header << boost::posix_time::second_clock::local_time() << "\r\n";
header << "\r\n";
offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset);
} }
memcpy (buf->data () + offset, response.str ().c_str (), len); std::copy(content.begin(), content.end(), buf->begin());
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
boost::asio::transfer_all (), boost::asio::transfer_all (),
std::bind(&I2PControlService::HandleResponseSent, this, std::bind(&I2PControlService::HandleResponseSent, this,
std::placeholders::_1, std::placeholders::_2, socket, buf)); std::placeholders::_1, std::placeholders::_2, socket, buf));
@ -322,7 +315,7 @@ namespace client
} }
InsertParam (results, "API", api); InsertParam (results, "API", api);
results << ","; results << ",";
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ()); std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
m_Tokens.insert (token); m_Tokens.insert (token);
InsertParam (results, "Token", token); InsertParam (results, "Token", token);
} }

3
I2PControl.h

@ -45,8 +45,9 @@ namespace client
void ReadRequest (std::shared_ptr<ssl_socket> socket); void ReadRequest (std::shared_ptr<ssl_socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf); std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void BuildErrorResponse (std::string & content, int code, const char *message);
void SendResponse (std::shared_ptr<ssl_socket> socket, void SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml); std::shared_ptr<I2PControlBuffer> buf, std::string& response, bool isHtml);
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf); std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);

8
Identity.h

@ -178,16 +178,12 @@ namespace data
public: public:
virtual ~LocalDestination() {}; virtual ~LocalDestination() {};
virtual const PrivateKeys& GetPrivateKeys () const = 0;
virtual const uint8_t * GetEncryptionPrivateKey () const = 0; virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0; virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
std::shared_ptr<const IdentityEx> GetIdentity () const { return GetPrivateKeys ().GetPublic (); };
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
GetPrivateKeys ().Sign (buf, len, signature);
};
}; };
} }
} }

96
LeaseSet.cpp

@ -4,7 +4,7 @@
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "NetDb.h" #include "NetDb.h"
#include "TunnelPool.h" #include "Tunnel.h"
#include "LeaseSet.h" #include "LeaseSet.h"
namespace i2p namespace i2p
@ -21,56 +21,6 @@ namespace data
ReadFromBuffer (); ReadFromBuffer ();
} }
LeaseSet::LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool):
m_IsValid (true), m_StoreLeases (true), m_ExpirationTime (0)
{
if (!pool) return;
// header
auto localDestination = pool->GetLocalDestination ();
if (!localDestination)
{
m_Buffer = nullptr;
m_BufferLen = 0;
m_IsValid = false;
LogPrint (eLogError, "LeaseSet: Destination for local LeaseSet doesn't exist");
return;
}
m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE];
m_BufferLen = localDestination->GetIdentity ()->ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256);
m_BufferLen += 256;
auto signingKeyLen = localDestination->GetIdentity ()->GetSigningPublicKeyLen ();
memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
m_BufferLen += signingKeyLen;
int numTunnels = pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
if (numTunnels > 16) numTunnels = 16; // 16 tunnels maximum
auto tunnels = pool->GetInboundTunnels (numTunnels);
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
m_BufferLen++;
// leases
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: tunnels)
{
memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32);
m_BufferLen += 32; // gateway id
htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ());
m_BufferLen += 4; // tunnel id
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - it->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + m_BufferLen, ts);
m_BufferLen += 8; // end date
}
// signature
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += localDestination->GetIdentity ()->GetSignatureLen ();
LogPrint (eLogDebug, "LeaseSet: Local LeaseSet of ", tunnels.size (), " leases created");
ReadFromBuffer ();
}
void LeaseSet::Update (const uint8_t * buf, size_t len) void LeaseSet::Update (const uint8_t * buf, size_t len)
{ {
if (len > m_BufferLen) if (len > m_BufferLen)
@ -195,7 +145,7 @@ namespace data
if (size > len) return 0; if (size > len) return 0;
uint8_t num = buf[size]; uint8_t num = buf[size];
size++; // num size++; // num
if (size + num*44 > len) return 0; if (size + num*LEASE_SIZE > len) return 0;
uint64_t timestamp= 0 ; uint64_t timestamp= 0 ;
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
@ -243,6 +193,48 @@ namespace data
if (IsEmpty ()) return true; if (IsEmpty ()) return true;
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
return ts > m_ExpirationTime; return ts > m_ExpirationTime;
}
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
m_ExpirationTime (0), m_Identity (identity)
{
int num = tunnels.size ();
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
// identity
auto signingKeyLen = m_Identity->GetSigningPublicKeyLen ();
m_BufferLen = m_Identity->GetFullLen () + 256 + signingKeyLen + 1 + num*LEASE_SIZE + m_Identity->GetSignatureLen ();
m_Buffer = new uint8_t[m_BufferLen];
auto offset = m_Identity->ToBuffer (m_Buffer, m_BufferLen);
memcpy (m_Buffer + offset, encryptionPublicKey, 256);
offset += 256;
memset (m_Buffer + offset, 0, signingKeyLen);
offset += signingKeyLen;
// num leases
m_Buffer[offset] = num;
offset++;
// leases
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
for (int i = 0; i < num; i++)
{
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
offset += 32; // gateway id
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
offset += 4; // tunnel id
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + offset, ts);
offset += 8; // end date
}
// we don't sign it yet. must be signed later on
}
bool LocalLeaseSet::IsExpired () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
return ts > m_ExpirationTime;
} }
} }
} }

31
LeaseSet.h

@ -12,7 +12,7 @@ namespace i2p
namespace tunnel namespace tunnel
{ {
class TunnelPool; class InboundTunnel;
} }
namespace data namespace data
@ -37,14 +37,14 @@ namespace data
}; };
}; };
const int MAX_LS_BUFFER_SIZE = 3072; const size_t MAX_LS_BUFFER_SIZE = 3072;
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
const uint8_t MAX_NUM_LEASES = 16; const uint8_t MAX_NUM_LEASES = 16;
class LeaseSet: public RoutingDestination class LeaseSet: public RoutingDestination
{ {
public: public:
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true); LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true);
LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool);
~LeaseSet () { delete[] m_Buffer; }; ~LeaseSet () { delete[] m_Buffer; };
void Update (const uint8_t * buf, size_t len); void Update (const uint8_t * buf, size_t len);
bool IsNewer (const uint8_t * buf, size_t len) const; bool IsNewer (const uint8_t * buf, size_t len) const;
@ -82,6 +82,31 @@ namespace data
uint8_t * m_Buffer; uint8_t * m_Buffer;
size_t m_BufferLen; size_t m_BufferLen;
}; };
class LocalLeaseSet
{
public:
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
~LocalLeaseSet () { delete[] m_Buffer; };
const uint8_t * GetBuffer () const { return m_Buffer; };
uint8_t * GetSignature () { return m_Buffer + m_BufferLen - GetSignatureLen (); };
size_t GetBufferLen () const { return m_BufferLen; };
size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); };
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
bool IsExpired () const;
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.GetBufferLen () && !memcmp (other.GetBuffer (), other.GetBuffer (), m_BufferLen); };
private:
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_Buffer;
size_t m_BufferLen;
};
} }
} }

6
RouterContext.h

@ -30,6 +30,7 @@ namespace i2p
RouterContext (); RouterContext ();
void Init (); void Init ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
{ {
@ -73,13 +74,14 @@ namespace i2p
void UpdateStats (); void UpdateStats ();
// implements LocalDestination // implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); }; const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); };
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetStandardIdentity ().publicKey; }; const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetStandardIdentity ().publicKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
void SetLeaseSetUpdated () {}; void SetLeaseSetUpdated () {};
// implements GarlicDestination // implements GarlicDestination
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () { return nullptr; }; std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const; std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from); void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);

4
Tunnel.cpp

@ -562,7 +562,7 @@ namespace tunnel
case eTunnelStatePending: case eTunnelStatePending:
if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT)
{ {
LogPrint (eLogWarning, "Tunnel: pending build request ", it->first, " timeout, deleted"); LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " timeout, deleted");
// update stats // update stats
auto config = tunnel->GetTunnelConfig (); auto config = tunnel->GetTunnelConfig ();
if (config) if (config)
@ -587,7 +587,7 @@ namespace tunnel
it++; it++;
break; break;
case eTunnelStateBuildFailed: case eTunnelStateBuildFailed:
LogPrint (eLogWarning, "Tunnel: pending build request ", it->first, " failed, deleted"); LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted");
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; m_NumFailedTunnelCreations++;
break; break;

3
debian/i2pd.1 vendored

@ -36,6 +36,9 @@ Where to write pidfile (don\'t write by default)
\fB\-\-log=\fR \fB\-\-log=\fR
Logs destination: \fIstdout\fR, \fIfile\fR, \fIsyslog\fR (\fIstdout\fR if not set, \fIfile\fR - otherwise, for compatibility) Logs destination: \fIstdout\fR, \fIfile\fR, \fIsyslog\fR (\fIstdout\fR if not set, \fIfile\fR - otherwise, for compatibility)
.TP .TP
\fB\-\-logfile\fR
Path to logfile (default - autodetect)
.TP
\fB\-\-loglevel=\fR \fB\-\-loglevel=\fR
Log messages above this level (\fIdebug\fR, \fBinfo\fR, \fIwarn\fR, \fIerror\fR) Log messages above this level (\fIdebug\fR, \fBinfo\fR, \fIwarn\fR, \fIerror\fR)
.TP .TP

14
tests/test-http-req.cpp

@ -3,11 +3,12 @@
using namespace i2p::http; using namespace i2p::http;
int main(int argc, char *argv[]) { int main() {
HTTPReq *req; HTTPReq *req;
int ret = 0, len = 0; int ret = 0, len = 0;
const char *buf; const char *buf;
/* test: parsing request with body */
buf = buf =
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
"User-Agent: curl/7.26.0\r\n" "User-Agent: curl/7.26.0\r\n"
@ -31,6 +32,7 @@ int main(int argc, char *argv[]) {
assert(req->headers.find("User-Agent")->second == "curl/7.26.0"); assert(req->headers.find("User-Agent")->second == "curl/7.26.0");
delete req; delete req;
/* test: parsing request without body */
buf = buf =
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
"\r\n"; "\r\n";
@ -44,6 +46,7 @@ int main(int argc, char *argv[]) {
assert(req->headers.size() == 0); assert(req->headers.size() == 0);
delete req; delete req;
/* test: parsing request without body */
buf = buf =
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"\r\n"; "\r\n";
@ -52,6 +55,7 @@ int main(int argc, char *argv[]) {
assert((ret = req->parse(buf, len)) == -1); /* no host header */ assert((ret = req->parse(buf, len)) == -1); /* no host header */
delete req; delete req;
/* test: parsing incomplete request */
buf = buf =
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
""; "";
@ -60,9 +64,11 @@ int main(int argc, char *argv[]) {
assert((ret = req->parse(buf, len)) == 0); /* request not completed */ assert((ret = req->parse(buf, len)) == 0); /* request not completed */
delete req; delete req;
/* test: parsing slightly malformed request */
buf = buf =
"GET http://inr.i2p HTTP/1.1\r\n" "GET http://inr.i2p HTTP/1.1\r\n"
"Host: stats.i2p\r\n" "Host: stats.i2p\r\n"
"Accept-Encoding: \r\n"
"Accept: */*\r\n" "Accept: */*\r\n"
"\r\n"; "\r\n";
len = strlen(buf); len = strlen(buf);
@ -71,9 +77,13 @@ int main(int argc, char *argv[]) {
assert(req->method == "GET"); assert(req->method == "GET");
assert(req->uri == "http://inr.i2p"); assert(req->uri == "http://inr.i2p");
assert(req->host == "stats.i2p"); assert(req->host == "stats.i2p");
assert(req->headers.size() == 2); assert(req->headers.size() == 3);
assert(req->headers.count("Host") == 1); assert(req->headers.count("Host") == 1);
assert(req->headers.count("Accept") == 1); assert(req->headers.count("Accept") == 1);
assert(req->headers.count("Accept-Encoding") == 1);
assert(req->headers["Host"] == "stats.i2p");
assert(req->headers["Accept"] == "*/*");
assert(req->headers["Accept-Encoding"] == "");
delete req; delete req;
return 0; return 0;

15
tests/test-http-res.cpp

@ -3,11 +3,12 @@
using namespace i2p::http; using namespace i2p::http;
int main(int argc, char *argv[]) { int main() {
HTTPRes *res; HTTPRes *res;
int ret = 0, len = 0; int ret = 0, len = 0;
const char *buf; const char *buf;
/* test: parsing valid response without body */
buf = buf =
"HTTP/1.1 304 Not Modified\r\n" "HTTP/1.1 304 Not Modified\r\n"
"Date: Thu, 14 Apr 2016 00:00:00 GMT\r\n" "Date: Thu, 14 Apr 2016 00:00:00 GMT\r\n"
@ -31,6 +32,18 @@ int main(int argc, char *argv[]) {
assert(res->length() == 536); assert(res->length() == 536);
delete res; delete res;
/* test: building request */
buf =
"HTTP/1.0 304 Not Modified\r\n"
"Content-Length: 0\r\n"
"\r\n";
res = new HTTPRes;
res->version = "HTTP/1.0";
res->code = 304;
res->status = "Not Modified";
res->add_header("Content-Length", "0");
assert(res->to_string() == buf);
return 0; return 0;
} }

2
tests/test-http-url.cpp

@ -3,7 +3,7 @@
using namespace i2p::http; using namespace i2p::http;
int main(int argc, char *argv[]) { int main() {
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
URL *url; URL *url;

2
tests/test-http-url_decode.cpp

@ -3,7 +3,7 @@
using namespace i2p::http; using namespace i2p::http;
int main(int argc, char *argv[]) { int main() {
std::string in("/%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0/"); std::string in("/%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0/");
std::string out = UrlDecode(in); std::string out = UrlDecode(in);

Loading…
Cancel
Save