i2pd/libi2pd/Destination.cpp
Anatolii Cherednichenko 55534ea002 Reformat code
2022-08-30 02:11:28 +03:00

1253 lines
64 KiB
C++

/*
* Copyright (c) 2013-2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <algorithm>
#include <cassert>
#include <string>
#include <set>
#include <vector>
#include <boost/algorithm/string.hpp>
#include "Crypto.h"
#include "Log.h"
#include "FS.h"
#include "Timestamp.h"
#include "NetDb.hpp"
#include "Destination.h"
namespace i2p {
namespace client {
LeaseSetDestination::LeaseSetDestination(boost::asio::io_service &service,
bool isPublic, const std::map<std::string, std::string> *params) :
m_Service(service), m_IsPublic(isPublic), m_PublishReplyToken(0),
m_LastSubmissionTime(0), m_PublishConfirmationTimer(m_Service),
m_PublishVerificationTimer(m_Service), m_PublishDelayTimer(m_Service), m_CleanupTimer(m_Service),
m_LeaseSetType(DEFAULT_LEASESET_TYPE), m_AuthType(i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE) {
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
int numTags = DEFAULT_TAGS_TO_SEND;
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
try {
if (params) {
auto it = params->find(I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
if (it != params->end())
inLen = std::stoi(it->second);
it = params->find(I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
if (it != params->end())
outLen = std::stoi(it->second);
it = params->find(I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
if (it != params->end())
inQty = std::stoi(it->second);
it = params->find(I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
if (it != params->end())
outQty = std::stoi(it->second);
it = params->find(I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE);
if (it != params->end())
inVar = std::stoi(it->second);
it = params->find(I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE);
if (it != params->end())
outVar = std::stoi(it->second);
it = params->find(I2CP_PARAM_TAGS_TO_SEND);
if (it != params->end())
numTags = std::stoi(it->second);
LogPrint(eLogInfo, "Destination: Parameters for tunnel set to: ", inQty, " inbound (", inLen,
" hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
it = params->find(I2CP_PARAM_RATCHET_INBOUND_TAGS);
if (it != params->end())
SetNumRatchetInboundTags(std::stoi(it->second));
it = params->find(I2CP_PARAM_EXPLICIT_PEERS);
if (it != params->end()) {
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
std::stringstream ss(it->second);
std::string b64;
while (std::getline(ss, b64, ',')) {
i2p::data::IdentHash ident;
ident.FromBase64(b64);
explicitPeers->push_back(ident);
LogPrint(eLogInfo, "Destination: Added to explicit peers list: ", b64);
}
}
it = params->find(I2CP_PARAM_INBOUND_NICKNAME);
if (it != params->end()) m_Nickname = it->second;
else // try outbound
{
it = params->find(I2CP_PARAM_OUTBOUND_NICKNAME);
if (it != params->end()) m_Nickname = it->second;
// otherwise we set default nickname in Start when we know local address
}
it = params->find(I2CP_PARAM_DONT_PUBLISH_LEASESET);
if (it != params->end()) {
// oveeride isPublic
m_IsPublic = (it->second != "true");
}
it = params->find(I2CP_PARAM_LEASESET_TYPE);
if (it != params->end())
m_LeaseSetType = std::stoi(it->second);
if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) {
// authentication for encrypted LeaseSet
it = params->find(I2CP_PARAM_LEASESET_AUTH_TYPE);
if (it != params->end()) {
auto authType = std::stoi(it->second);
if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE &&
authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
m_AuthType = authType;
else
LogPrint(eLogError, "Destination: Unknown auth type ", authType);
}
}
it = params->find(I2CP_PARAM_LEASESET_PRIV_KEY);
if (it != params->end()) {
m_LeaseSetPrivKey.reset(new i2p::data::Tag<32>());
if (m_LeaseSetPrivKey->FromBase64(it->second) != 32) {
LogPrint(eLogError, "Destination: Invalid value i2cp.leaseSetPrivKey ", it->second);
m_LeaseSetPrivKey.reset(nullptr);
}
}
}
}
catch (std::exception &ex) {
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
}
SetNumTags(numTags);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool(inLen, outLen, inQty, outQty, inVar, outVar);
if (explicitPeers)
m_Pool->SetExplicitPeers(explicitPeers);
if (params) {
auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY);
if (itr != params->end()) {
auto maxlatency = std::stoi(itr->second);
itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY);
if (itr != params->end()) {
auto minlatency = std::stoi(itr->second);
if (minlatency > 0 && maxlatency > 0) {
// set tunnel pool latency
LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minlatency, "ms, ",
maxlatency, "ms]");
m_Pool->RequireLatency(minlatency, maxlatency);
}
}
}
}
}
LeaseSetDestination::~LeaseSetDestination() {
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool(m_Pool);
for (auto &it: m_LeaseSetRequests)
it.second->Complete(nullptr);
}
void LeaseSetDestination::Start() {
if (m_Nickname.empty())
m_Nickname = i2p::data::GetIdentHashAbbreviation(GetIdentHash()); // set default nickname
LoadTags();
m_Pool->SetLocalDestination(shared_from_this());
m_Pool->SetActive(true);
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::Stop() {
m_CleanupTimer.cancel();
m_PublishConfirmationTimer.cancel();
m_PublishVerificationTimer.cancel();
if (m_Pool) {
m_Pool->SetLocalDestination(nullptr);
i2p::tunnel::tunnels.StopTunnelPool(m_Pool);
}
SaveTags();
CleanUp(); // GarlicDestination
}
bool LeaseSetDestination::Reconfigure(std::map<std::string, std::string> params) {
auto itr = params.find("i2cp.dontPublishLeaseSet");
if (itr != params.end()) {
m_IsPublic = itr->second != "true";
}
int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency;
std::map<std::string, int &> intOpts = {
{I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen},
{I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen},
{I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant},
{I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant},
{I2CP_PARAM_TAGS_TO_SEND, numTags},
{I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency},
{I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency}
};
auto pool = GetTunnelPool();
inLen = pool->GetNumInboundHops();
outLen = pool->GetNumOutboundHops();
inQuant = pool->GetNumInboundTunnels();
outQuant = pool->GetNumOutboundTunnels();
minLatency = 0;
maxLatency = 0;
for (auto &opt: intOpts) {
itr = params.find(opt.first);
if (itr != params.end()) {
opt.second = std::stoi(itr->second);
}
}
pool->RequireLatency(minLatency, maxLatency);
return pool->Reconfigure(inLen, outLen, inQuant, outQuant);
}
std::shared_ptr<i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet(const i2p::data::IdentHash &ident) {
std::shared_ptr<i2p::data::LeaseSet> remoteLS;
{
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find(ident);
if (it != m_RemoteLeaseSets.end())
remoteLS = it->second;
}
if (remoteLS) {
if (!remoteLS->IsExpired()) {
if (remoteLS->ExpiresSoon()) {
LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire");
// update now before expiration for smooth handover
auto s = shared_from_this();
RequestDestination(ident, [s, ident](std::shared_ptr<i2p::data::LeaseSet> ls) {
if (ls && !ls->IsExpired()) {
ls->PopulateLeases();
{
std::lock_guard<std::mutex> _lock(s->m_RemoteLeaseSetsMutex);
s->m_RemoteLeaseSets[ident] = ls;
}
}
});
}
return remoteLS;
} else {
LogPrint(eLogWarning, "Destination: Remote LeaseSet expired");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
m_RemoteLeaseSets.erase(ident);
return nullptr;
}
} else {
auto ls = i2p::data::netdb.FindLeaseSet(ident);
if (ls && !ls->IsExpired()) {
ls->PopulateLeases(); // since we don't store them in netdb
std::lock_guard<std::mutex> _lock(m_RemoteLeaseSetsMutex);
m_RemoteLeaseSets[ident] = ls;
return ls;
}
}
return nullptr;
}
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet() {
if (!m_Pool) return nullptr;
if (!m_LeaseSet)
UpdateLeaseSet();
auto ls = GetLeaseSetMt();
return (ls && ls->GetInnerLeaseSet()) ? ls->GetInnerLeaseSet() : ls; // always non-encrypted
}
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSetMt() {
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
return m_LeaseSet;
}
void LeaseSetDestination::SetLeaseSet(std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet) {
{
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
m_LeaseSet = newLeaseSet;
}
i2p::garlic::GarlicDestination::SetLeaseSetUpdated();
if (m_IsPublic) {
auto s = shared_from_this();
m_Service.post([s](void) {
s->m_PublishVerificationTimer.cancel();
s->Publish();
});
}
}
void LeaseSetDestination::UpdateLeaseSet() {
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 tunnels = m_Pool->GetInboundTunnels(numTunnels);
if (!tunnels.empty())
CreateNewLeaseSet(tunnels);
else
LogPrint(eLogInfo, "Destination: No inbound tunnels for LeaseSet");
}
bool LeaseSetDestination::SubmitSessionKey(const uint8_t *key, const uint8_t *tag) {
struct {
uint8_t k[32], t[32];
} data;
memcpy(data.k, key, 32);
memcpy(data.t, tag, 32);
auto s = shared_from_this();
m_Service.post([s, data](void) {
s->AddSessionKey(data.k, data.t);
});
return true;
}
void LeaseSetDestination::SubmitECIESx25519Key(const uint8_t *key, uint64_t tag) {
struct {
uint8_t k[32];
uint64_t t;
} data;
memcpy(data.k, key, 32);
data.t = tag;
auto s = shared_from_this();
m_Service.post([s, data](void) {
s->AddECIESx25519Key(data.k, data.t);
});
}
void LeaseSetDestination::ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg) {
m_Service.post(std::bind(&LeaseSetDestination::HandleGarlicMessage, shared_from_this(), msg));
}
void LeaseSetDestination::ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg) {
uint32_t msgID = bufbe32toh(msg->GetPayload() + DELIVERY_STATUS_MSGID_OFFSET);
m_Service.post(std::bind(&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this(), msgID));
}
void LeaseSetDestination::HandleI2NPMessage(const uint8_t *buf, size_t len) {
I2NPMessageType typeID = (I2NPMessageType) (buf[I2NP_HEADER_TYPEID_OFFSET]);
uint32_t msgID = bufbe32toh(buf + I2NP_HEADER_MSGID_OFFSET);
LeaseSetDestination::HandleCloveI2NPMessage(typeID, buf + I2NP_HEADER_SIZE,
GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID);
}
bool LeaseSetDestination::HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len,
uint32_t msgID) {
switch (typeID) {
case eI2NPData:
HandleDataMessage(payload, len);
break;
case eI2NPDeliveryStatus:
// we assume tunnel tests non-encrypted
HandleDeliveryStatusMessage(bufbe32toh(payload + DELIVERY_STATUS_MSGID_OFFSET));
break;
case eI2NPDatabaseStore:
HandleDatabaseStoreMessage(payload, len);
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage(payload, len);
break;
case eI2NPShortTunnelBuildReply: // might come as garlic encrypted
i2p::HandleI2NPMessage(CreateI2NPMessage(typeID, payload, len, msgID));
break;
default:
LogPrint(eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
return false;
}
return true;
}
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;
if (replyToken) {
LogPrint(eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
offset += 36;
}
i2p::data::IdentHash key(buf + DATABASE_STORE_KEY_OFFSET);
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
switch (buf[DATABASE_STORE_TYPE_OFFSET]) {
case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1
case i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2: // 3
{
LogPrint(eLogDebug, "Destination: Remote LeaseSet");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find(key);
if (it != m_RemoteLeaseSets.end() &&
it->second->GetStoreType() == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type
{
leaseSet = it->second;
if (leaseSet->IsNewer(buf + offset, len - offset)) {
leaseSet->Update(buf + offset, len - offset);
if (leaseSet->IsValid() && leaseSet->GetIdentHash() == key && !leaseSet->IsExpired())
LogPrint(eLogDebug, "Destination: Remote LeaseSet updated");
else {
LogPrint(eLogDebug, "Destination: Remote LeaseSet update failed");
m_RemoteLeaseSets.erase(it);
leaseSet = nullptr;
}
} else
LogPrint(eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
} else {
// add or replace
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
leaseSet = std::make_shared<i2p::data::LeaseSet>(buf + offset, len - offset); // LeaseSet
else
leaseSet = std::make_shared<i2p::data::LeaseSet2>(buf[DATABASE_STORE_TYPE_OFFSET],
buf + offset, len - offset, true,
GetPreferredCryptoType()); // LeaseSet2
if (leaseSet->IsValid() && leaseSet->GetIdentHash() == key && !leaseSet->IsExpired()) {
if (leaseSet->GetIdentHash() != GetIdentHash()) {
LogPrint(eLogDebug, "Destination: New remote LeaseSet added");
m_RemoteLeaseSets[key] = leaseSet;
} else
LogPrint(eLogDebug, "Destination: Own remote LeaseSet dropped");
} else {
LogPrint(eLogError, "Destination: New remote LeaseSet failed");
leaseSet = nullptr;
}
}
break;
}
case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5
{
auto it2 = m_LeaseSetRequests.find(key);
if (it2 != m_LeaseSetRequests.end() && it2->second->requestedBlindedKey) {
auto ls2 = std::make_shared<i2p::data::LeaseSet2>(buf + offset, len - offset,
it2->second->requestedBlindedKey,
m_LeaseSetPrivKey
? ((const uint8_t *) *m_LeaseSetPrivKey)
: nullptr, GetPreferredCryptoType());
if (ls2->IsValid()) {
m_RemoteLeaseSets[ls2->GetIdentHash()] = ls2; // ident is not key
m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup
leaseSet = ls2;
}
} else
LogPrint(eLogInfo, "Destination: Couldn't find request for encrypted LeaseSet2");
break;
}
default:
LogPrint(eLogError, "Destination: Unexpected client's DatabaseStore type ",
buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
}
auto it1 = m_LeaseSetRequests.find(key);
if (it1 != m_LeaseSetRequests.end()) {
it1->second->requestTimeoutTimer.cancel();
if (it1->second) it1->second->Complete(leaseSet);
m_LeaseSetRequests.erase(it1);
}
}
void LeaseSetDestination::HandleDatabaseSearchReplyMessage(const uint8_t *buf, size_t len) {
i2p::data::IdentHash key(buf);
int num = buf[32]; // num
LogPrint(eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64(), " num=", num);
auto it = m_LeaseSetRequests.find(key);
if (it != m_LeaseSetRequests.end()) {
auto request = it->second;
bool found = false;
if (request->excluded.size() < MAX_NUM_FLOODFILLS_PER_REQUEST) {
for (int i = 0; i < num; i++) {
i2p::data::IdentHash peerHash(buf + 33 + i * 32);
if (!request->excluded.count(peerHash) && !i2p::data::netdb.FindRouter(peerHash)) {
LogPrint(eLogInfo, "Destination: Found new floodfill, request it");
i2p::data::netdb.RequestDestination(peerHash, nullptr, false); // through exploratory
}
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill(key, request->excluded);
if (floodfill) {
LogPrint(eLogInfo, "Destination: Requesting ", key.ToBase64(), " at ",
floodfill->GetIdentHash().ToBase64());
if (SendLeaseSetRequest(key, floodfill, request))
found = true;
}
}
if (!found) {
LogPrint(eLogInfo, "Destination: ", key.ToBase64(), " was not found on ",
MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
request->Complete(nullptr);
m_LeaseSetRequests.erase(key);
}
} else
LogPrint(eLogWarning, "Destination: Request for ", key.ToBase64(), " not found");
}
void LeaseSetDestination::HandleDeliveryStatusMessage(uint32_t msgID) {
if (msgID == m_PublishReplyToken) {
LogPrint(eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
m_ExcludedFloodfills.clear();
m_PublishReplyToken = 0;
// schedule verification
m_PublishVerificationTimer.expires_from_now(boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait(std::bind(&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this(), std::placeholders::_1));
} else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage(msgID);
}
void LeaseSetDestination::SetLeaseSetUpdated() {
UpdateLeaseSet();
}
void LeaseSetDestination::Publish() {
auto leaseSet = GetLeaseSetMt();
if (!leaseSet || !m_Pool) {
LogPrint(eLogError, "Destination: Can't publish non-existing LeaseSet");
return;
}
if (m_PublishReplyToken) {
LogPrint(eLogDebug, "Destination: Publishing LeaseSet is pending");
return;
}
auto ts = i2p::util::GetSecondsSinceEpoch();
if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL) {
LogPrint(eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL,
" seconds");
m_PublishDelayTimer.cancel();
m_PublishDelayTimer.expires_from_now(boost::posix_time::seconds(PUBLISH_MIN_INTERVAL));
m_PublishDelayTimer.async_wait(std::bind(&LeaseSetDestination::HandlePublishDelayTimer,
shared_from_this(), std::placeholders::_1));
return;
}
if (!m_Pool->GetInboundTunnels().size() || !m_Pool->GetOutboundTunnels().size()) {
LogPrint(eLogError, "Destination: Can't publish LeaseSet. Destination is not ready");
return;
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill(leaseSet->GetIdentHash(), m_ExcludedFloodfills);
if (!floodfill) {
LogPrint(eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
m_ExcludedFloodfills.clear();
return;
}
auto outbound = m_Pool->GetNextOutboundTunnel(nullptr, floodfill->GetCompatibleTransports(false));
auto inbound = m_Pool->GetNextInboundTunnel(nullptr, floodfill->GetCompatibleTransports(true));
if (!outbound || !inbound) {
LogPrint(eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash().ToBase64(),
". Trying another floodfill");
m_ExcludedFloodfills.insert(floodfill->GetIdentHash());
floodfill = i2p::data::netdb.GetClosestFloodfill(leaseSet->GetIdentHash(), m_ExcludedFloodfills);
if (floodfill) {
outbound = m_Pool->GetNextOutboundTunnel(nullptr, floodfill->GetCompatibleTransports(false));
if (outbound) {
inbound = m_Pool->GetNextInboundTunnel(nullptr, floodfill->GetCompatibleTransports(true));
if (!inbound)
LogPrint(eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
} else
LogPrint(eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
} else
LogPrint(eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
if (!floodfill || !outbound || !inbound) {
m_ExcludedFloodfills.clear();
return;
}
}
m_ExcludedFloodfills.insert(floodfill->GetIdentHash());
LogPrint(eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash().ToBase32());
RAND_bytes((uint8_t * ) & m_PublishReplyToken, 4);
auto msg = WrapMessageForRouter(floodfill,
i2p::CreateDatabaseStoreMsg(leaseSet, m_PublishReplyToken, inbound));
m_PublishConfirmationTimer.expires_from_now(boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait(std::bind(&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this(), std::placeholders::_1));
outbound->SendTunnelDataMsg(floodfill->GetIdentHash(), 0, msg);
m_LastSubmissionTime = ts;
}
void LeaseSetDestination::HandlePublishConfirmationTimer(const boost::system::error_code &ecode) {
if (ecode != boost::asio::error::operation_aborted) {
if (m_PublishReplyToken) {
m_PublishReplyToken = 0;
if (GetIdentity()->GetCryptoKeyType() == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) {
LogPrint(eLogWarning, "Destination: Publish confirmation was not received in ",
PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again");
Publish();
} else {
LogPrint(eLogWarning, "Destination: Publish confirmation was not received in ",
PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ",
(int) GetIdentity()->GetCryptoKeyType());
// Java floodfill never sends confirmation back for unknown crypto type
// assume it successive and try to verify
m_PublishVerificationTimer.expires_from_now(
boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait(
std::bind(&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this(), std::placeholders::_1));
}
}
}
}
void LeaseSetDestination::HandlePublishVerificationTimer(const boost::system::error_code &ecode) {
if (ecode != boost::asio::error::operation_aborted) {
auto ls = GetLeaseSetMt();
if (!ls) {
LogPrint(eLogWarning, "Destination: Couldn't verify LeaseSet for ", GetIdentHash().ToBase32());
return;
}
auto s = shared_from_this();
RequestLeaseSet(ls->GetStoreHash(),
[s, ls](std::shared_ptr<const i2p::data::LeaseSet> leaseSet) {
if (leaseSet) {
if (*ls == *leaseSet) {
// we got latest LeasetSet
LogPrint(eLogDebug, "Destination: Published LeaseSet verified for ",
s->GetIdentHash().ToBase32());
s->m_PublishVerificationTimer.expires_from_now(
boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait(
std::bind(&LeaseSetDestination::HandlePublishVerificationTimer, s,
std::placeholders::_1));
return;
} else
LogPrint(eLogDebug,
"Destination: LeaseSet is different than just published for ",
s->GetIdentHash().ToBase32());
} else
LogPrint(eLogWarning, "Destination: Couldn't find published LeaseSet for ",
s->GetIdentHash().ToBase32());
// we have to publish again
s->Publish();
});
}
}
void LeaseSetDestination::HandlePublishDelayTimer(const boost::system::error_code &ecode) {
if (ecode != boost::asio::error::operation_aborted)
Publish();
}
bool
LeaseSetDestination::RequestDestination(const i2p::data::IdentHash &dest, RequestComplete requestComplete) {
if (!m_Pool || !IsReady()) {
if (requestComplete)
m_Service.post([requestComplete](void) { requestComplete(nullptr); });
return false;
}
m_Service.post(std::bind(&LeaseSetDestination::RequestLeaseSet, shared_from_this(), dest, requestComplete,
nullptr));
return true;
}
bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet(
std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete) {
if (!dest || !m_Pool || !IsReady()) {
if (requestComplete)
m_Service.post([requestComplete](void) { requestComplete(nullptr); });
return false;
}
auto storeHash = dest->GetStoreHash();
auto leaseSet = FindLeaseSet(storeHash);
if (leaseSet) {
if (requestComplete)
m_Service.post([requestComplete, leaseSet](void) { requestComplete(leaseSet); });
return true;
}
m_Service.post(
std::bind(&LeaseSetDestination::RequestLeaseSet, shared_from_this(), storeHash, requestComplete,
dest));
return true;
}
void LeaseSetDestination::CancelDestinationRequest(const i2p::data::IdentHash &dest, bool notify) {
auto s = shared_from_this();
m_Service.post([dest, notify, s](void) {
auto it = s->m_LeaseSetRequests.find(dest);
if (it != s->m_LeaseSetRequests.end()) {
auto requestComplete = it->second;
s->m_LeaseSetRequests.erase(it);
if (notify && requestComplete) requestComplete->Complete(nullptr);
}
});
}
void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet(
std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify) {
if (dest)
CancelDestinationRequest(dest->GetStoreHash(), notify);
}
void LeaseSetDestination::RequestLeaseSet(const i2p::data::IdentHash &dest, RequestComplete requestComplete,
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey) {
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->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
if (requestComplete)
request->requestComplete.push_back(requestComplete);
auto ts = i2p::util::GetSecondsSinceEpoch();
auto ret = m_LeaseSetRequests.insert(
std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest, request));
if (ret.second) // inserted
{
request->requestTime = ts;
if (!SendLeaseSetRequest(dest, floodfill, request)) {
// request failed
m_LeaseSetRequests.erase(ret.first);
if (requestComplete) requestComplete(nullptr);
}
} else // duplicate
{
LogPrint(eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64(), " is pending already");
if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) {
// something went wrong
m_LeaseSetRequests.erase(ret.first);
if (requestComplete) requestComplete(nullptr);
} else if (requestComplete)
ret.first->second->requestComplete.push_back(requestComplete);
}
} 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(nullptr,
nextFloodfill->GetCompatibleTransports(true));
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(nullptr,
nextFloodfill->GetCompatibleTransports(false));
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->requestTimeoutTimer.cancel();
bool isECIES = SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) &&
nextFloodfill->GetVersion() >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46;
uint8_t replyKey[32], replyTag[32];
RAND_bytes(replyKey, 32); // random session key
RAND_bytes(replyTag, isECIES ? 8 : 32); // random session tag
if (isECIES)
AddECIESx25519Key(replyKey, replyTag);
else
AddSessionKey(replyKey, replyTag);
auto msg = WrapMessageForRouter(nextFloodfill, CreateLeaseSetDatabaseLookupMsg(dest,
request->excluded,
request->replyTunnel,
replyKey, replyTag,
isECIES));
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;
m_LeaseSetRequests.erase(it);
if (requestComplete) requestComplete->Complete(nullptr);
}
}
}
}
void LeaseSetDestination::HandleCleanupTimer(const boost::system::error_code &ecode) {
if (ecode != boost::asio::error::operation_aborted) {
CleanupExpiredTags();
CleanupRemoteLeaseSets();
CleanupDestination();
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();
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
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;
}
}
i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType() const {
if (SupportsEncryptionType(i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
}
ClientDestination::ClientDestination(boost::asio::io_service &service, const i2p::data::PrivateKeys &keys,
bool isPublic, const std::map<std::string, std::string> *params) :
LeaseSetDestination(service, isPublic, params),
m_Keys(keys), m_StreamingAckDelay(DEFAULT_INITIAL_ACK_DELAY),
m_IsStreamingAnswerPings(DEFAULT_ANSWER_PINGS),
m_DatagramDestination(nullptr), m_RefCounter(0),
m_ReadyChecker(service) {
if (keys.IsOfflineSignature() && GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_LEASESET)
SetLeaseSetType(
i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
// extract encryption type params for LS2
std::set<i2p::data::CryptoKeyType> encryptionKeyTypes;
if (params) {
auto it = params->find(I2CP_PARAM_LEASESET_ENCRYPTION_TYPE);
if (it != params->end()) {
// comma-separated values
std::vector<std::string> values;
boost::split(values, it->second, boost::is_any_of(","));
for (auto &it1: values) {
try {
encryptionKeyTypes.insert(std::stoi(it1));
}
catch (std::exception &ex) {
LogPrint(eLogInfo, "Destination: Unexpected crypto type ", it1, ". ", ex.what());
continue;
}
}
}
}
// if no param or valid crypto type use from identity
bool isSingleKey = false;
if (encryptionKeyTypes.empty()) {
isSingleKey = true;
encryptionKeyTypes.insert(GetIdentity()->GetCryptoKeyType());
}
for (auto &it: encryptionKeyTypes) {
auto encryptionKey = new EncryptionKey(it);
if (IsPublic())
PersistTemporaryKeys(encryptionKey, isSingleKey);
else
encryptionKey->GenerateKeys();
encryptionKey->CreateDecryptor();
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) {
m_ECIESx25519EncryptionKey.reset(encryptionKey);
if (GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_LEASESET)
SetLeaseSetType(i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2
} else
m_StandardEncryptionKey.reset(encryptionKey);
}
if (IsPublic())
LogPrint(eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32(), " created");
try {
if (params) {
// extract streaming params
auto it = params->find(I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end())
m_StreamingAckDelay = std::stoi(it->second);
it = params->find(I2CP_PARAM_STREAMING_ANSWER_PINGS);
if (it != params->end())
m_IsStreamingAnswerPings = (it->second == "true");
if (GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) {
// authentication for encrypted LeaseSet
auto authType = GetAuthType();
if (authType > 0) {
m_AuthKeys = std::make_shared<std::vector<i2p::data::AuthPublicKey> >();
if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH)
ReadAuthKey(I2CP_PARAM_LEASESET_CLIENT_DH, params);
else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
ReadAuthKey(I2CP_PARAM_LEASESET_CLIENT_PSK, params);
else
LogPrint(eLogError, "Destination: Unexpected auth type ", authType);
if (m_AuthKeys->size())
LogPrint(eLogInfo, "Destination: ", m_AuthKeys->size(), " auth keys read");
else {
LogPrint(eLogError, "Destination: No auth keys read for auth type ", authType);
m_AuthKeys = nullptr;
}
}
}
}
}
catch (std::exception &ex) {
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
}
}
ClientDestination::~ClientDestination() {
}
void ClientDestination::Start() {
LeaseSetDestination::Start();
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination>(GetSharedFromThis()); // TODO:
m_StreamingDestination->Start();
for (auto &it: m_StreamingDestinationsByPorts)
it.second->Start();
}
void ClientDestination::Stop() {
LeaseSetDestination::Stop();
m_ReadyChecker.cancel();
m_StreamingDestination->Stop();
//m_StreamingDestination->SetOwner (nullptr);
m_StreamingDestination = nullptr;
for (auto &it: m_StreamingDestinationsByPorts) {
it.second->Stop();
//it.second->SetOwner (nullptr);
}
m_StreamingDestinationsByPorts.clear();
if (m_DatagramDestination) {
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
}
}
void ClientDestination::HandleDataMessage(const uint8_t *buf, size_t len) {
uint32_t length = bufbe32toh(buf);
if (length > len - 4) {
LogPrint(eLogError, "Destination: Data message length ", length, " exceeds buffer length ", len);
return;
}
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;
case PROTOCOL_TYPE_RAW:
// raw datagram
if (m_DatagramDestination)
m_DatagramDestination->HandleDataMessagePayload(fromPort, toPort, buf, length, true);
else
LogPrint(eLogError, "Destination: Missing raw 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<const i2p::data::LeaseSet> ls) {
if (ls)
streamRequestComplete(s->CreateStream(ls, port));
else
streamRequestComplete(nullptr);
});
}
}
void ClientDestination::CreateStream(StreamRequestComplete streamRequestComplete,
std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port) {
if (!streamRequestComplete) {
LogPrint(eLogError, "Destination: Request callback is not specified in CreateStream");
return;
}
auto s = GetSharedFromThis();
RequestDestinationWithEncryptedLeaseSet(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;
}
void ClientDestination::SendPing(const i2p::data::IdentHash &to) {
if (m_StreamingDestination) {
auto leaseSet = FindLeaseSet(to);
if (leaseSet)
m_StreamingDestination->SendPing(leaseSet);
else {
auto s = m_StreamingDestination;
RequestDestination(to,
[s](std::shared_ptr<const i2p::data::LeaseSet> ls) {
if (ls) s->SendPing(ls);
});
}
}
}
void ClientDestination::SendPing(std::shared_ptr<const i2p::data::BlindedPublicKey> to) {
auto s = m_StreamingDestination;
RequestDestinationWithEncryptedLeaseSet(to,
[s](std::shared_ptr<const i2p::data::LeaseSet> ls) {
if (ls) s->SendPing(ls);
});
}
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;
}
void ClientDestination::AcceptOnce(const i2p::stream::StreamingDestination::Acceptor &acceptor) {
if (m_StreamingDestination)
m_StreamingDestination->AcceptOnce(acceptor);
}
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;
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::RemoveStreamingDestination(int port) {
if (port) {
auto it = m_StreamingDestinationsByPorts.find(port);
if (it != m_StreamingDestinationsByPorts.end()) {
auto ret = it->second;
m_StreamingDestinationsByPorts.erase(it);
return ret;
}
}
return nullptr;
}
i2p::datagram::DatagramDestination *ClientDestination::CreateDatagramDestination(bool gzip) {
if (m_DatagramDestination == nullptr)
m_DatagramDestination = new i2p::datagram::DatagramDestination(GetSharedFromThis(), gzip);
return m_DatagramDestination;
}
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams() const {
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
if (m_StreamingDestination) {
for (auto &it: m_StreamingDestination->GetStreams())
ret.push_back(it.second);
}
for (auto &it: m_StreamingDestinationsByPorts)
for (auto &it1: it.second->GetStreams())
ret.push_back(it1.second);
return ret;
}
void ClientDestination::PersistTemporaryKeys(EncryptionKey *keys, bool isSingleKey) {
if (!keys) return;
std::string ident = GetIdentHash().ToBase32();
std::string path = i2p::fs::DataDirPath("destinations",
isSingleKey ? (ident + ".dat") : (ident + "." +
std::to_string(keys->keyType) +
".dat"));
std::ifstream f(path, std::ifstream::binary);
if (f) {
f.read((char *) keys->pub, 256);
f.read((char *) keys->priv, 256);
return;
}
LogPrint(eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
memset(keys->priv, 0, 256);
memset(keys->pub, 0, 256);
keys->GenerateKeys();
// TODO:: persist crypto key type
std::ofstream f1(path, std::ofstream::binary | std::ofstream::out);
if (f1) {
f1.write((char *) keys->pub, 256);
f1.write((char *) keys->priv, 256);
return;
}
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
}
void
ClientDestination::CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels) {
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
if (GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_LEASESET) {
if (m_StandardEncryptionKey) {
leaseSet = std::make_shared<i2p::data::LocalLeaseSet>(GetIdentity(), m_StandardEncryptionKey->pub,
tunnels);
// sign
Sign(leaseSet->GetBuffer(), leaseSet->GetBufferLen() - leaseSet->GetSignatureLen(),
leaseSet->GetSignature());
} else
LogPrint(eLogError, "Destinations: Wrong encryption key type for LeaseSet type 1");
} else {
// standard LS2 (type 3) first
i2p::data::LocalLeaseSet2::KeySections keySections;
if (m_ECIESx25519EncryptionKey)
keySections.push_back({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub});
if (m_StandardEncryptionKey)
keySections.push_back({m_StandardEncryptionKey->keyType,
(uint16_t) m_StandardEncryptionKey->decryptor->GetPublicKeyLen(),
m_StandardEncryptionKey->pub});
bool isPublishedEncrypted = GetLeaseSetType() == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2>(i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
m_Keys, keySections, tunnels, IsPublic(),
isPublishedEncrypted);
if (isPublishedEncrypted) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2>(ls2, m_Keys, GetAuthType(), m_AuthKeys);
leaseSet = ls2;
}
SetLeaseSet(leaseSet);
}
void ClientDestination::CleanupDestination() {
if (m_DatagramDestination) m_DatagramDestination->CleanUp();
}
bool ClientDestination::Decrypt(const uint8_t *encrypted, uint8_t *data,
i2p::data::CryptoKeyType preferredCrypto) const {
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor)
return m_ECIESx25519EncryptionKey->decryptor->Decrypt(encrypted, data);
if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor)
return m_StandardEncryptionKey->decryptor->Decrypt(encrypted, data);
else
LogPrint(eLogError, "Destinations: Decryptor is not set");
return false;
}
bool ClientDestination::SupportsEncryptionType(i2p::data::CryptoKeyType keyType) const {
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool) m_ECIESx25519EncryptionKey
: (bool) m_StandardEncryptionKey;
}
const uint8_t *ClientDestination::GetEncryptionPublicKey(i2p::data::CryptoKeyType keyType) const {
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr;
return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr;
}
void
ClientDestination::ReadAuthKey(const std::string &group, const std::map<std::string, std::string> *params) {
for (auto it: *params)
if (it.first.length() >= group.length() && !it.first.compare(0, group.length(), group)) {
auto pos = it.second.find(':');
if (pos != std::string::npos) {
i2p::data::AuthPublicKey pubKey;
if (pubKey.FromBase64(it.second.substr(pos + 1)))
m_AuthKeys->push_back(pubKey);
else
LogPrint(eLogError, "Destination: Unexpected auth key ", it.second.substr(pos + 1));
}
}
}
bool ClientDestination::DeleteStream(uint32_t recvStreamID) {
if (m_StreamingDestination->DeleteStream(recvStreamID))
return true;
for (auto it: m_StreamingDestinationsByPorts)
if (it.second->DeleteStream(recvStreamID))
return true;
return false;
}
RunnableClientDestination::RunnableClientDestination(const i2p::data::PrivateKeys &keys, bool isPublic,
const std::map<std::string, std::string> *params) :
RunnableService("Destination"),
ClientDestination(GetIOService(), keys, isPublic, params) {
}
RunnableClientDestination::~RunnableClientDestination() {
if (IsRunning())
Stop();
}
void RunnableClientDestination::Start() {
if (!IsRunning()) {
ClientDestination::Start();
StartIOService();
}
}
void RunnableClientDestination::Stop() {
if (IsRunning()) {
ClientDestination::Stop();
StopIOService();
}
}
}
}