File: Destination.h

package info (click to toggle)
i2pd 2.58.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,612 kB
  • sloc: cpp: 59,663; makefile: 224; sh: 138
file content (354 lines) | stat: -rw-r--r-- 17,617 bytes parent folder | download
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