1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
|
/*
* Copyright (c) 2013-2025, 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
*/
#ifndef DESTINATION_H__
#define DESTINATION_H__
#include <string.h>
#include <thread>
#include <mutex>
#include <memory>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <functional>
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
#include "Crypto.h"
#include "CryptoKey.h"
#include "LeaseSet.h"
#include "Garlic.h"
#include "NetDb.hpp"
#include "Streaming.h"
#include "Datagram.h"
#include "util.h"
namespace i2p
{
namespace client
{
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18;
const uint8_t PROTOCOL_TYPE_DATAGRAM2 = 19;
const uint8_t PROTOCOL_TYPE_DATAGRAM3 = 20;
const int PUBLISH_CONFIRMATION_TIMEOUT = 1800; // in milliseconds
const int PUBLISH_VERIFICATION_TIMEOUT = 5; // in seconds after successful publish
const int PUBLISH_VERIFICATION_TIMEOUT_VARIANCE = 3; // in seconds
const int PUBLISH_MIN_INTERVAL = 20; // in seconds
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
const int LEASESET_REQUEST_TIMEOUT = 1600; // in milliseconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 12000; // in milliseconds
const int DESTINATION_CLEANUP_TIMEOUT = 44; // in seconds
const int DESTINATION_CLEANUP_TIMEOUT_VARIANCE = 30; // in seconds
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
// I2CP
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
const char I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE[] = "inbound.lengthVariance";
const int DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE = 0;
const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance";
const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0;
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
const int DEFAULT_TAGS_TO_SEND = 40;
const char I2CP_PARAM_RATCHET_INBOUND_TAGS[] = "crypto.ratchet.inboundTags";
const char I2CP_PARAM_RATCHET_OUTBOUND_TAGS[] = "crypto.ratchet.outboundTags"; // not used yet
const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname";
const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname";
const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";
const int DEFAULT_LEASESET_TYPE = 3;
const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType";
const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType";
const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn
const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
// latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max";
const int DEFAULT_MAX_TUNNEL_LATENCY = 0;
// streaming
const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay";
const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds
const char I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED[] = "i2p.streaming.maxOutboundSpeed"; // bytes/sec
const int DEFAULT_MAX_OUTBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
const char I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED[] = "i2p.streaming.maxInboundSpeed"; // bytes/sec
const int DEFAULT_MAX_INBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true;
const char I2CP_PARAM_STREAMING_PROFILE[] = "i2p.streaming.profile";
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK;
const char I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS[] = "i2p.streaming.maxConcurrentStreams";
const int DEFAULT_MAX_CONCURRENT_STREAMS = 2048;
const char I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE[] = "i2p.streaming.maxWindowSize";
const char I2CP_PARAM_STREAMING_DONT_SIGN[] = "i2p.streaming.dontSign";
const int DEFAULT_DONT_SIGN = false;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
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
struct LeaseSetRequest
{
LeaseSetRequest (boost::asio::io_context& service): requestTime (0), requestTimeoutTimer (service) {};
std::unordered_set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
std::list<RequestComplete> requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
{
for (auto& it: requestComplete) it (ls);
requestComplete.clear ();
}
};
public:
LeaseSetDestination (boost::asio::io_context& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~LeaseSetDestination ();
const std::string& GetNickname () const { return m_Nickname; };
auto& GetService () { return m_Service; };
virtual void Start ();
virtual void Stop ();
/** i2cp reconfigure */
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete = nullptr);
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override;
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override { return m_Pool; }
// override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag) override;
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
void SetLeaseSetUpdated (bool post) override;
bool IsPublic () const { return m_IsPublic; };
void SetPublic (bool pub) { m_IsPublic = pub; };
protected:
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
int GetAuthType () const { return m_AuthType; };
static bool GetBoolParamValue (std::string_view value);
virtual void CleanupDestination () {}; // additional clean up in derived classes
virtual i2p::data::CryptoKeyType GetPreferredCryptoType () const = 0;
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from) = 0;
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
private:
void UpdateLeaseSet ();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt ();
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (uint32_t msgID);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void SendNextLeaseSetRequest (const i2p::data::IdentHash& key, 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 ();
private:
boost::asio::io_context& m_Service;
mutable std::mutex m_RemoteLeaseSetsMutex;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::list<std::shared_ptr<I2NPMessage> > m_IncomingMsgsQueue;
mutable std::mutex m_IncomingMsgsQueueMutex;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::mutex m_LeaseSetMutex;
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds
std::unordered_set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer;
std::string m_Nickname;
int m_LeaseSetType, m_AuthType;
std::unique_ptr<i2p::data::Tag<32> > m_LeaseSetPrivKey; // non-null if presented
public:
// for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet () const { return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; };
bool IsPerClientAuth () const { return m_AuthType > 0; };
};
class ClientDestination: public LeaseSetDestination
{
public:
ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
void Start () override;
void Stop () override;
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
// ref counter
int Acquire () { return ++m_RefCounter; };
int Release () { return --m_RefCounter; };
int GetRefCounter () const { return m_RefCounter; };
// streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (uint16_t port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (uint16_t port = 0) const;
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination (uint16_t port);
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, uint16_t port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, uint16_t port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::IdentHash& dest, uint16_t port = 0); // sync
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, uint16_t port = 0); // sync
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, uint16_t port = 0);
void SendPing (const i2p::data::IdentHash& to);
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; }
int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; }
int GetStreamingMaxConcurrentStreams () const { return m_StreamingMaxConcurrentStreams; }
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
bool IsStreamingDontSign () const { return m_IsStreamingDontSign; }
int GetStreamingMaxWindowSize () const { return m_StreamingMaxWindowSize; }
// datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true,
i2p::datagram::DatagramVersion version = i2p::datagram::eDatagramV1);
// implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override { return m_Keys.GetPublic (); };
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override;
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const override;
protected:
// GarlicDestionation
i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override;
// LeaseSetDestination
void CleanupDestination () override;
i2p::data::CryptoKeyType GetPreferredCryptoType () const override { return m_PreferredCryptoType; }
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) override;
private:
std::shared_ptr<ClientDestination> GetSharedFromThis () {
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
}
void PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys);
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
template<typename Dest>
std::shared_ptr<i2p::stream::Stream> CreateStreamSync (const Dest& dest, uint16_t port);
private:
i2p::data::PrivateKeys m_Keys;
std::map<i2p::data::CryptoKeyType, std::shared_ptr<i2p::crypto::LocalEncryptionKey> > m_EncryptionKeys; // last is most preferable
i2p::data::CryptoKeyType m_PreferredCryptoType;
int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams, m_StreamingMaxWindowSize;
bool m_IsStreamingAnswerPings, m_IsStreamingDontSign;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
std::shared_ptr<i2p::stream::StreamingDestination> m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels
i2p::datagram::DatagramDestination * m_DatagramDestination;
int m_RefCounter; // how many clients(tunnels) use this destination
uint64_t m_LastPublishedTimestamp;
boost::asio::deadline_timer m_ReadyChecker;
std::shared_ptr<std::vector<i2p::data::AuthPublicKey> > m_AuthKeys; // we don't need them for I2CP
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
bool DeleteStream (uint32_t recvStreamID);
};
class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination
{
public:
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~RunnableClientDestination ();
void Start ();
void Stop ();
};
}
}
#endif
|