mirror of
https://github.com/PurpleI2P/i2pd
synced 2024-11-10 08:00:38 +03:00
commit
5e52b3609c
@ -5,7 +5,6 @@
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <openssl/rand.h>
|
||||
#include "Base.h"
|
||||
#include "util.h"
|
||||
@ -716,12 +715,14 @@ namespace client
|
||||
if (status == 200) // OK
|
||||
{
|
||||
bool isChunked = false, isGzip = false;
|
||||
m_Etag = ""; m_LastModified = "";
|
||||
std::string header, statusMessage;
|
||||
std::getline (response, statusMessage);
|
||||
// read until new line meaning end of header
|
||||
while (!response.eof () && header != "\r")
|
||||
{
|
||||
std::getline (response, header);
|
||||
if (response.fail ()) break;
|
||||
auto colon = header.find (':');
|
||||
if (colon != std::string::npos)
|
||||
{
|
||||
@ -741,7 +742,7 @@ namespace client
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, "Addressbook: received ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
|
||||
if (!response.eof ())
|
||||
if (!response.eof () && !response.fail ())
|
||||
{
|
||||
success = true;
|
||||
if (!isChunked)
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Destination.h"
|
||||
#include "I2PService.h"
|
||||
#include "HTTPProxy.h"
|
||||
#include "SOCKS.h"
|
||||
#include "I2PTunnel.h"
|
||||
|
539
Destination.cpp
539
Destination.cpp
@ -13,17 +13,11 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
||||
const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service),
|
||||
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
|
||||
m_PublishReplyToken (0), m_PublishConfirmationTimer (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 outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||
@ -103,11 +97,9 @@ namespace client
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
|
||||
if (explicitPeers)
|
||||
m_Pool->SetExplicitPeers (explicitPeers);
|
||||
if (m_IsPublic)
|
||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
||||
}
|
||||
|
||||
ClientDestination::~ClientDestination ()
|
||||
LeaseSetDestination::~LeaseSetDestination ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
Stop ();
|
||||
@ -116,11 +108,9 @@ namespace client
|
||||
m_LeaseSetRequests.clear ();
|
||||
if (m_Pool)
|
||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
||||
if (m_DatagramDestination)
|
||||
delete m_DatagramDestination;
|
||||
}
|
||||
|
||||
void ClientDestination::Run ()
|
||||
void LeaseSetDestination::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
@ -135,26 +125,29 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::Start ()
|
||||
bool LeaseSetDestination::Start ()
|
||||
{
|
||||
if (!m_IsRunning)
|
||||
{
|
||||
m_IsRunning = true;
|
||||
if (m_IsPublic)
|
||||
PersistTemporaryKeys ();
|
||||
else
|
||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
m_Pool->SetActive (true);
|
||||
m_Thread = new std::thread (std::bind (&ClientDestination::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_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientDestination::Stop ()
|
||||
bool LeaseSetDestination::Stop ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
{
|
||||
@ -162,16 +155,6 @@ namespace client
|
||||
m_PublishConfirmationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
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)
|
||||
{
|
||||
m_Pool->SetLocalDestination (nullptr);
|
||||
@ -184,10 +167,13 @@ namespace client
|
||||
delete m_Thread;
|
||||
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);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
@ -210,7 +196,7 @@ namespace client
|
||||
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_LeaseSet)
|
||||
@ -218,12 +204,17 @@ namespace client
|
||||
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
|
||||
{
|
||||
@ -239,17 +230,17 @@ namespace client
|
||||
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];
|
||||
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);
|
||||
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);
|
||||
int num = buf[32]; // num
|
||||
@ -379,7 +370,7 @@ namespace client
|
||||
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);
|
||||
if (msgID == m_PublishReplyToken)
|
||||
@ -389,14 +380,14 @@ namespace client
|
||||
m_PublishReplyToken = 0;
|
||||
// schedule verification
|
||||
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));
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
|
||||
}
|
||||
|
||||
void ClientDestination::SetLeaseSetUpdated ()
|
||||
void LeaseSetDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||
UpdateLeaseSet ();
|
||||
@ -407,7 +398,7 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::Publish ()
|
||||
void LeaseSetDestination::Publish ()
|
||||
{
|
||||
if (!m_LeaseSet || !m_Pool)
|
||||
{
|
||||
@ -425,6 +416,12 @@ namespace client
|
||||
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
|
||||
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);
|
||||
if (!floodfill)
|
||||
{
|
||||
@ -435,14 +432,14 @@ namespace client
|
||||
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
|
||||
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
|
||||
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.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
|
||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
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)
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
@ -471,7 +468,7 @@ namespace client
|
||||
// we got latest LeasetSet
|
||||
LogPrint (eLogDebug, "Destination: published LeaseSet verified");
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -483,6 +480,242 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
if (!m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
return false;
|
||||
}
|
||||
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([dest, s](void)
|
||||
{
|
||||
auto it = s->m_LeaseSetRequests.find (dest);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
s->m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
auto request = std::make_shared<LeaseSetRequest> (m_Service);
|
||||
request->requestComplete = requestComplete;
|
||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
m_LeaseSetRequests.erase (dest);
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else // duplicate
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
// TODO: queue up requests
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
|
||||
{
|
||||
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
|
||||
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
|
||||
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
|
||||
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
|
||||
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
|
||||
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
|
||||
|
||||
if (request->replyTunnel && request->outboundTunnel)
|
||||
{
|
||||
request->excluded.insert (nextFloodfill->GetIdentHash ());
|
||||
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
request->requestTimeoutTimer.cancel ();
|
||||
|
||||
uint8_t replyKey[32], replyTag[32];
|
||||
RAND_bytes (replyKey, 32); // random session key
|
||||
RAND_bytes (replyTag, 32); // random session tag
|
||||
AddSessionKey (replyKey, replyTag);
|
||||
|
||||
auto msg = WrapMessage (nextFloodfill,
|
||||
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
|
||||
request->replyTunnel, replyKey, replyTag));
|
||||
request->outboundTunnel->SendTunnelDataMsg (
|
||||
{
|
||||
i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
nextFloodfill->GetIdentHash (), 0, msg
|
||||
}
|
||||
});
|
||||
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
|
||||
request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
|
||||
shared_from_this (), std::placeholders::_1, dest));
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto it = m_LeaseSetRequests.find (dest);
|
||||
if (it != m_LeaseSetRequests.end ())
|
||||
{
|
||||
bool done = false;
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||
{
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
// reset tunnels, because one them might fail
|
||||
it->second->outboundTunnel = nullptr;
|
||||
it->second->replyTunnel = nullptr;
|
||||
done = !SendLeaseSetRequest (dest, floodfill, it->second);
|
||||
}
|
||||
else
|
||||
done = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (done)
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
CleanupExpiredTags ();
|
||||
CleanupRemoteLeaseSets ();
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void LeaseSetDestination::CleanupRemoteLeaseSets ()
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
|
||||
{
|
||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||
it = m_RemoteLeaseSets.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void LeaseSetDestination::PersistTemporaryKeys ()
|
||||
{
|
||||
std::string ident = GetIdentHash().ToBase32();
|
||||
std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat"));
|
||||
std::ifstream f(path, std::ifstream::binary);
|
||||
|
||||
if (f) {
|
||||
f.read ((char *)m_EncryptionPublicKey, 256);
|
||||
f.read ((char *)m_EncryptionPrivateKey, 256);
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p");
|
||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
|
||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
||||
if (f1) {
|
||||
f1.write ((char *)m_EncryptionPublicKey, 256);
|
||||
f1.write ((char *)m_EncryptionPrivateKey, 256);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
@ -512,8 +745,8 @@ namespace client
|
||||
default:
|
||||
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
|
||||
{
|
||||
if (!streamRequestComplete)
|
||||
@ -526,7 +759,7 @@ namespace client
|
||||
streamRequestComplete(CreateStream (leaseSet, port));
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
auto s = GetSharedFromThis ();
|
||||
RequestDestination (dest,
|
||||
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
@ -579,7 +812,7 @@ namespace client
|
||||
|
||||
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);
|
||||
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
|
||||
if (port)
|
||||
m_StreamingDestinationsByPorts[port] = dest;
|
||||
else // update default
|
||||
@ -590,198 +823,10 @@ namespace client
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
|
||||
{
|
||||
if (!m_DatagramDestination)
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
|
||||
return m_DatagramDestination;
|
||||
}
|
||||
|
||||
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
if (!m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
return false;
|
||||
}
|
||||
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([dest, s](void)
|
||||
{
|
||||
auto it = s->m_LeaseSetRequests.find (dest);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
s->m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
auto request = std::make_shared<LeaseSetRequest> (m_Service);
|
||||
request->requestComplete = requestComplete;
|
||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
m_LeaseSetRequests.erase (dest);
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else // duplicate
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
// TODO: queue up requests
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
|
||||
{
|
||||
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
|
||||
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
|
||||
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
|
||||
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
|
||||
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
|
||||
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
|
||||
|
||||
if (request->replyTunnel && request->outboundTunnel)
|
||||
{
|
||||
request->excluded.insert (nextFloodfill->GetIdentHash ());
|
||||
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
request->requestTimeoutTimer.cancel ();
|
||||
|
||||
uint8_t replyKey[32], replyTag[32];
|
||||
RAND_bytes (replyKey, 32); // random session key
|
||||
RAND_bytes (replyTag, 32); // random session tag
|
||||
AddSessionKey (replyKey, replyTag);
|
||||
|
||||
auto msg = WrapMessage (nextFloodfill,
|
||||
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
|
||||
request->replyTunnel, replyKey, replyTag));
|
||||
request->outboundTunnel->SendTunnelDataMsg (
|
||||
{
|
||||
i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
nextFloodfill->GetIdentHash (), 0, msg
|
||||
}
|
||||
});
|
||||
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
|
||||
request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer,
|
||||
shared_from_this (), std::placeholders::_1, dest));
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto it = m_LeaseSetRequests.find (dest);
|
||||
if (it != m_LeaseSetRequests.end ())
|
||||
{
|
||||
bool done = false;
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||
{
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
// reset tunnels, because one them might fail
|
||||
it->second->outboundTunnel = nullptr;
|
||||
it->second->replyTunnel = nullptr;
|
||||
done = !SendLeaseSetRequest (dest, floodfill, it->second);
|
||||
}
|
||||
else
|
||||
done = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (done)
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
CleanupExpiredTags ();
|
||||
CleanupRemoteLeaseSets ();
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::CleanupRemoteLeaseSets ()
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
|
||||
{
|
||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||
it = m_RemoteLeaseSets.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::PersistTemporaryKeys ()
|
||||
{
|
||||
std::string ident = GetIdentHash().ToBase32();
|
||||
std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat"));
|
||||
std::ifstream f(path, std::ifstream::binary);
|
||||
|
||||
if (f) {
|
||||
f.read ((char *)m_EncryptionPublicKey, 256);
|
||||
f.read ((char *)m_EncryptionPrivateKey, 256);
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p");
|
||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
|
||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
||||
if (f1) {
|
||||
f1.write ((char *)m_EncryptionPublicKey, 256);
|
||||
f1.write ((char *)m_EncryptionPrivateKey, 256);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
|
||||
{
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
|
||||
|
107
Destination.h
107
Destination.h
@ -49,8 +49,8 @@ namespace client
|
||||
|
||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||
|
||||
class ClientDestination: public i2p::garlic::GarlicDestination,
|
||||
public std::enable_shared_from_this<ClientDestination>
|
||||
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
|
||||
public std::enable_shared_from_this<LeaseSetDestination>
|
||||
{
|
||||
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
|
||||
// leaseSet = nullptr means not found
|
||||
@ -68,11 +68,11 @@ namespace client
|
||||
|
||||
public:
|
||||
|
||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~LeaseSetDestination ();
|
||||
|
||||
virtual void Start ();
|
||||
virtual void Stop ();
|
||||
virtual bool Start ();
|
||||
virtual bool Stop ();
|
||||
bool IsRunning () const { return m_IsRunning; };
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
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);
|
||||
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
|
||||
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
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
// implements LocalDestination
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
|
||||
|
||||
|
||||
// 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; }
|
||||
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 SetLeaseSetUpdated ();
|
||||
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
protected:
|
||||
|
||||
// I2CP
|
||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||
|
||||
private:
|
||||
|
||||
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);
|
||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||
void CleanupRemoteLeaseSets ();
|
||||
void PersistTemporaryKeys ();
|
||||
void CleanupRemoteLeaseSets ();
|
||||
|
||||
void PersistTemporaryKeys ();
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
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<LeaseSetRequest> > m_LeaseSetRequests;
|
||||
|
||||
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;
|
||||
uint32_t m_PublishReplyToken;
|
||||
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;
|
||||
|
||||
@ -159,6 +143,59 @@ namespace client
|
||||
|
||||
// for HTTP only
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ namespace data
|
||||
int numCertificates = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
2
Garlic.h
2
Garlic.h
@ -163,7 +163,7 @@ namespace garlic
|
||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
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 void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
|
||||
|
||||
|
44
HTTP.cpp
44
HTTP.cpp
@ -8,6 +8,7 @@
|
||||
|
||||
#include "HTTP.h"
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
@ -44,9 +45,10 @@ namespace http {
|
||||
bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) {
|
||||
std::size_t pos = 0;
|
||||
std::size_t len = 2; /* strlen(": ") */
|
||||
std::size_t max = line.length();
|
||||
if ((pos = line.find(": ", pos)) == std::string::npos)
|
||||
return false;
|
||||
while (isspace(line.at(pos + len)))
|
||||
while ((pos + len) < max && isspace(line.at(pos + len)))
|
||||
len++;
|
||||
std::string name = line.substr(0, pos);
|
||||
std::string value = line.substr(pos + len);
|
||||
@ -54,6 +56,13 @@ namespace http {
|
||||
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) {
|
||||
std::string url(str, len ? len : strlen(str));
|
||||
return parse(url);
|
||||
@ -184,6 +193,25 @@ namespace http {
|
||||
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) {
|
||||
std::string str(buf, len);
|
||||
return parse(str);
|
||||
@ -256,7 +284,7 @@ namespace http {
|
||||
return false;
|
||||
}
|
||||
|
||||
long int HTTPRes::length() {
|
||||
long int HTTPMsg::length() {
|
||||
unsigned long int length = 0;
|
||||
auto it = headers.find("Content-Length");
|
||||
if (it == headers.end())
|
||||
@ -311,12 +339,24 @@ namespace http {
|
||||
}
|
||||
|
||||
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;
|
||||
ss << version << " " << code << " " << status << CRLF;
|
||||
for (auto & h : headers) {
|
||||
ss << h.first << ": " << h.second << CRLF;
|
||||
}
|
||||
ss << CRLF;
|
||||
if (body.length() > 0)
|
||||
ss << body;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
40
HTTP.h
40
HTTP.h
@ -54,8 +54,18 @@ namespace http {
|
||||
std::string to_string ();
|
||||
};
|
||||
|
||||
struct HTTPReq {
|
||||
struct HTTPMsg {
|
||||
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 method;
|
||||
std::string uri;
|
||||
@ -75,11 +85,16 @@ namespace http {
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
struct HTTPRes {
|
||||
std::map<std::string, std::string> headers;
|
||||
struct HTTPRes : HTTPMsg {
|
||||
std::string version;
|
||||
std::string status;
|
||||
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) {}
|
||||
|
||||
@ -91,14 +106,17 @@ namespace http {
|
||||
int parse(const char *buf, size_t len);
|
||||
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();
|
||||
|
||||
/** @brief Checks that response declared as chunked data */
|
||||
bool is_chunked();
|
||||
|
||||
/** @brief Returns declared response length or -1 if unknown */
|
||||
long int length();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -115,6 +133,14 @@ namespace http {
|
||||
* @return Decoded string
|
||||
*/
|
||||
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
|
||||
} // i2p
|
||||
|
||||
|
@ -4,6 +4,13 @@
|
||||
#include <boost/regex.hpp>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <mutex>
|
||||
|
||||
#include "I2PService.h"
|
||||
#include "Destination.h"
|
||||
#include "HTTPProxy.h"
|
||||
#include "util.h"
|
||||
#include "Identity.h"
|
||||
@ -36,7 +43,7 @@ namespace proxy
|
||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void Terminate();
|
||||
void AsyncSockRead();
|
||||
void HTTPRequestFailed(/*std::string message*/);
|
||||
void HTTPRequestFailed(const char *message);
|
||||
void RedirectToJumpService();
|
||||
void ExtractRequest();
|
||||
bool IsI2PAddress();
|
||||
@ -49,6 +56,7 @@ namespace proxy
|
||||
uint8_t m_http_buff[http_buffer_size];
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
||||
std::string m_request; //Data left to be sent
|
||||
std::string m_Response;
|
||||
std::string m_url; //URL
|
||||
std::string m_method; //Method
|
||||
std::string m_version; //HTTP version
|
||||
@ -91,10 +99,17 @@ namespace proxy
|
||||
|
||||
/* All hope is lost beyond this point */
|
||||
//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";
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
|
||||
std::size_t size = std::strlen(message);
|
||||
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));
|
||||
}
|
||||
|
||||
@ -105,7 +120,8 @@ namespace proxy
|
||||
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";
|
||||
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));
|
||||
}
|
||||
|
||||
@ -139,7 +155,7 @@ namespace proxy
|
||||
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
|
||||
{
|
||||
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
|
||||
HTTPRequestFailed(); //TODO: send right stuff
|
||||
HTTPRequestFailed("unsupported HTTP version");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -276,13 +292,13 @@ namespace proxy
|
||||
case '\n': EnterState(DONE); break;
|
||||
default:
|
||||
LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
|
||||
HTTPRequestFailed(); //TODO: add correct code
|
||||
HTTPRequestFailed("rejected invalid request");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
|
||||
HTTPRequestFailed(); //TODO: add correct code 500
|
||||
HTTPRequestFailed("invalid parser state");
|
||||
return false;
|
||||
}
|
||||
http_buff++;
|
||||
@ -338,7 +354,7 @@ namespace proxy
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,6 @@
|
||||
#ifndef 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 proxy
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
@ -241,15 +240,15 @@ namespace http {
|
||||
|
||||
if ((num = seconds / 86400) > 0) {
|
||||
s << num << " days, ";
|
||||
seconds -= num;
|
||||
seconds -= num * 86400;
|
||||
}
|
||||
if ((num = seconds / 3600) > 0) {
|
||||
s << num << " hours, ";
|
||||
seconds -= num;
|
||||
seconds -= num * 3600;
|
||||
}
|
||||
if ((num = seconds / 60) > 0) {
|
||||
s << num << " min, ";
|
||||
seconds -= num;
|
||||
seconds -= num * 60;
|
||||
}
|
||||
s << seconds << " seconds";
|
||||
}
|
||||
@ -749,7 +748,7 @@ namespace http {
|
||||
|
||||
if (needAuth && !CheckAuth(req)) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -763,6 +762,8 @@ namespace http {
|
||||
else
|
||||
ShowStatus (s);
|
||||
ShowPageTail (s);
|
||||
|
||||
res.code = 200;
|
||||
content = s.str ();
|
||||
SendReply (res, content);
|
||||
}
|
||||
@ -845,21 +846,11 @@ namespace http {
|
||||
|
||||
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
|
||||
{
|
||||
std::time_t time_now = std::time(nullptr);
|
||||
char time_buff[128];
|
||||
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())));
|
||||
reply.add_header("Content-Type", "text/html");
|
||||
reply.body = content;
|
||||
|
||||
std::string res = reply.to_string();
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
|
||||
buffers.push_back(boost::asio::buffer(res));
|
||||
buffers.push_back(boost::asio::buffer(content));
|
||||
|
||||
boost::asio::async_write (*m_Socket, buffers,
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(res),
|
||||
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
|
6
I2CP.cpp
6
I2CP.cpp
@ -8,6 +8,12 @@ namespace i2p
|
||||
{
|
||||
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):
|
||||
m_Owner (owner), m_Socket (socket),
|
||||
m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0)
|
||||
|
25
I2CP.h
25
I2CP.h
@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Destination.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@ -19,6 +20,28 @@ namespace client
|
||||
|
||||
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 I2CPSession: public std::enable_shared_from_this<I2CPSession>
|
||||
{
|
||||
@ -44,6 +67,8 @@ namespace client
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
||||
uint8_t m_Buffer[I2CP_SESSION_BUFFER_SIZE], * m_NextMessage;
|
||||
size_t m_NextMessageLen, m_NextMessageOffset;
|
||||
|
||||
std::shared_ptr<I2CPDestination> m_Destination;
|
||||
};
|
||||
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);
|
||||
|
||||
|
@ -249,7 +249,23 @@ namespace i2p
|
||||
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;
|
||||
auto m = NewI2NPShortMessage ();
|
||||
@ -258,14 +274,13 @@ namespace i2p
|
||||
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
|
||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
|
||||
size_t size = DATABASE_STORE_HEADER_SIZE;
|
||||
if (replyToken)
|
||||
if (replyToken && replyTunnel)
|
||||
{
|
||||
auto leases = leaseSet->GetNonExpiredLeases ();
|
||||
if (leases.size () > 0)
|
||||
if (replyTunnel)
|
||||
{
|
||||
htobe32buf (payload + size, leases[0]->tunnelID);
|
||||
htobe32buf (payload + size, replyTunnel->GetNextTunnelID ());
|
||||
size += 4; // reply tunnelID
|
||||
memcpy (payload + size, leases[0]->tunnelGateway, 32);
|
||||
memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32);
|
||||
size += 32; // reply tunnel gateway
|
||||
}
|
||||
else
|
||||
|
@ -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> 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 HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
|
||||
|
163
I2PControl.cpp
163
I2PControl.cpp
@ -2,8 +2,6 @@
|
||||
#include <sstream>
|
||||
#include <openssl/x509.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/property_tree/ini_parser.hpp>
|
||||
|
||||
@ -16,6 +14,7 @@
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "HTTP.h"
|
||||
#include "Config.h"
|
||||
#include "NetDb.h"
|
||||
#include "RouterContext.h"
|
||||
@ -189,71 +188,64 @@ namespace client
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
|
||||
return;
|
||||
} else {
|
||||
try
|
||||
{
|
||||
bool isHtml = !memcmp (buf->data (), "POST", 4);
|
||||
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
|
||||
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
|
||||
response << "{\"id\":null,\"error\":";
|
||||
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
|
||||
response << "\"jsonrpc\":\"2.0\"}";
|
||||
#else
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (ss, pt);
|
||||
|
||||
std::string id = pt.get<std::string>("id");
|
||||
std::string method = pt.get<std::string>("method");
|
||||
auto it = m_MethodHandlers.find (method);
|
||||
if (it != m_MethodHandlers.end ())
|
||||
{
|
||||
response << "{\"id\":" << id << ",\"result\":{";
|
||||
(this->*(it->second))(pt.get_child ("params"), response);
|
||||
response << "},\"jsonrpc\":\"2.0\"}";
|
||||
} else {
|
||||
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
|
||||
response << "{\"id\":null,\"error\":";
|
||||
response << "{\"code\":-32601,\"message\":\"Method not found\"},";
|
||||
response << "\"jsonrpc\":\"2.0\"}";
|
||||
}
|
||||
#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");
|
||||
}
|
||||
}
|
||||
/* 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 {
|
||||
json.write(buf->begin(), bytes_transferred);
|
||||
}
|
||||
LogPrint(eLogDebug, "I2PControl: json from request: ", json.str());
|
||||
#if GCC47_BOOST149
|
||||
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
|
||||
BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost");
|
||||
#else
|
||||
/* now try to parse json itself */
|
||||
try {
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (json, pt);
|
||||
|
||||
std::string id = pt.get<std::string>("id");
|
||||
std::string method = pt.get<std::string>("method");
|
||||
auto it = m_MethodHandlers.find (method);
|
||||
if (it != m_MethodHandlers.end ()) {
|
||||
std::ostringstream ss;
|
||||
ss << "{\"id\":" << id << ",\"result\":{";
|
||||
(this->*(it->second))(pt.get_child ("params"), ss);
|
||||
ss << "},\"jsonrpc\":\"2.0\"}";
|
||||
response = ss.str();
|
||||
} else {
|
||||
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
|
||||
BuildErrorResponse(response, 32601, "Method not found");
|
||||
}
|
||||
} 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
|
||||
@ -275,27 +267,28 @@ namespace client
|
||||
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,
|
||||
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 (isHtml)
|
||||
{
|
||||
std::ostringstream header;
|
||||
header << "HTTP/1.1 200 OK\r\n";
|
||||
header << "Connection: close\r\n";
|
||||
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
|
||||
header << "Content-Type: application/json\r\n";
|
||||
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);
|
||||
if (isHTTP) {
|
||||
i2p::http::HTTPRes res;
|
||||
res.code = 200;
|
||||
res.add_header("Content-Type", "application/json");
|
||||
res.add_header("Connection", "close");
|
||||
res.body = content;
|
||||
std::string tmp = res.to_string();
|
||||
content = tmp;
|
||||
}
|
||||
memcpy (buf->data () + offset, response.str ().c_str (), len);
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
|
||||
std::copy(content.begin(), content.end(), buf->begin());
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
|
||||
boost::asio::transfer_all (),
|
||||
std::bind(&I2PControlService::HandleResponseSent, this,
|
||||
std::placeholders::_1, std::placeholders::_2, socket, buf));
|
||||
@ -322,7 +315,7 @@ namespace client
|
||||
}
|
||||
InsertParam (results, "API", api);
|
||||
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);
|
||||
InsertParam (results, "Token", token);
|
||||
}
|
||||
|
@ -45,8 +45,9 @@ namespace client
|
||||
void ReadRequest (std::shared_ptr<ssl_socket> socket);
|
||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
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,
|
||||
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,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
|
||||
|
@ -178,16 +178,12 @@ namespace data
|
||||
public:
|
||||
|
||||
virtual ~LocalDestination() {};
|
||||
virtual const PrivateKeys& GetPrivateKeys () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPrivateKey () 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 (); };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
GetPrivateKeys ().Sign (buf, len, signature);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
96
LeaseSet.cpp
96
LeaseSet.cpp
@ -4,7 +4,7 @@
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Tunnel.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
namespace i2p
|
||||
@ -21,56 +21,6 @@ namespace data
|
||||
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)
|
||||
{
|
||||
if (len > m_BufferLen)
|
||||
@ -195,7 +145,7 @@ namespace data
|
||||
if (size > len) return 0;
|
||||
uint8_t num = buf[size];
|
||||
size++; // num
|
||||
if (size + num*44 > len) return 0;
|
||||
if (size + num*LEASE_SIZE > len) return 0;
|
||||
uint64_t timestamp= 0 ;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
@ -243,6 +193,48 @@ namespace data
|
||||
if (IsEmpty ()) return true;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
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
31
LeaseSet.h
@ -12,7 +12,7 @@ namespace i2p
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class TunnelPool;
|
||||
class InboundTunnel;
|
||||
}
|
||||
|
||||
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;
|
||||
class LeaseSet: public RoutingDestination
|
||||
{
|
||||
public:
|
||||
|
||||
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true);
|
||||
LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool);
|
||||
~LeaseSet () { delete[] m_Buffer; };
|
||||
void Update (const uint8_t * buf, size_t len);
|
||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
@ -82,6 +82,31 @@ namespace data
|
||||
uint8_t * m_Buffer;
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ namespace i2p
|
||||
RouterContext ();
|
||||
void Init ();
|
||||
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
|
||||
{
|
||||
@ -73,13 +74,14 @@ namespace i2p
|
||||
void UpdateStats ();
|
||||
|
||||
// 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 * 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 () {};
|
||||
|
||||
// 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;
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
||||
|
@ -562,7 +562,7 @@ namespace tunnel
|
||||
case eTunnelStatePending:
|
||||
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
|
||||
auto config = tunnel->GetTunnelConfig ();
|
||||
if (config)
|
||||
@ -587,7 +587,7 @@ namespace tunnel
|
||||
it++;
|
||||
break;
|
||||
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);
|
||||
m_NumFailedTunnelCreations++;
|
||||
break;
|
||||
|
3
debian/i2pd.1
vendored
3
debian/i2pd.1
vendored
@ -36,6 +36,9 @@ Where to write pidfile (don\'t write by default)
|
||||
\fB\-\-log=\fR
|
||||
Logs destination: \fIstdout\fR, \fIfile\fR, \fIsyslog\fR (\fIstdout\fR if not set, \fIfile\fR - otherwise, for compatibility)
|
||||
.TP
|
||||
\fB\-\-logfile\fR
|
||||
Path to logfile (default - autodetect)
|
||||
.TP
|
||||
\fB\-\-loglevel=\fR
|
||||
Log messages above this level (\fIdebug\fR, \fBinfo\fR, \fIwarn\fR, \fIerror\fR)
|
||||
.TP
|
||||
|
@ -3,11 +3,12 @@
|
||||
|
||||
using namespace i2p::http;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main() {
|
||||
HTTPReq *req;
|
||||
int ret = 0, len = 0;
|
||||
const char *buf;
|
||||
|
||||
/* test: parsing request with body */
|
||||
buf =
|
||||
"GET / HTTP/1.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");
|
||||
delete req;
|
||||
|
||||
/* test: parsing request without body */
|
||||
buf =
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"\r\n";
|
||||
@ -44,6 +46,7 @@ int main(int argc, char *argv[]) {
|
||||
assert(req->headers.size() == 0);
|
||||
delete req;
|
||||
|
||||
/* test: parsing request without body */
|
||||
buf =
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n";
|
||||
@ -52,6 +55,7 @@ int main(int argc, char *argv[]) {
|
||||
assert((ret = req->parse(buf, len)) == -1); /* no host header */
|
||||
delete req;
|
||||
|
||||
/* test: parsing incomplete request */
|
||||
buf =
|
||||
"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 */
|
||||
delete req;
|
||||
|
||||
/* test: parsing slightly malformed request */
|
||||
buf =
|
||||
"GET http://inr.i2p HTTP/1.1\r\n"
|
||||
"Host: stats.i2p\r\n"
|
||||
"Accept-Encoding: \r\n"
|
||||
"Accept: */*\r\n"
|
||||
"\r\n";
|
||||
len = strlen(buf);
|
||||
@ -71,9 +77,13 @@ int main(int argc, char *argv[]) {
|
||||
assert(req->method == "GET");
|
||||
assert(req->uri == "http://inr.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("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;
|
||||
|
||||
return 0;
|
||||
|
@ -3,11 +3,12 @@
|
||||
|
||||
using namespace i2p::http;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main() {
|
||||
HTTPRes *res;
|
||||
int ret = 0, len = 0;
|
||||
const char *buf;
|
||||
|
||||
/* test: parsing valid response without body */
|
||||
buf =
|
||||
"HTTP/1.1 304 Not Modified\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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using namespace i2p::http;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main() {
|
||||
std::map<std::string, std::string> params;
|
||||
URL *url;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
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 out = UrlDecode(in);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user