/ libi2pd / I2NPProtocol.cpp
I2NPProtocol.cpp
  1  /*
  2  * Copyright (c) 2013-2026, The PurpleI2P Project
  3  *
  4  * This file is part of Purple i2pd project and licensed under BSD3
  5  *
  6  * See full license text in LICENSE file at top of project tree
  7  */
  8  
  9  #include <string.h>
 10  #include <atomic>
 11  #include "Base.h"
 12  #include "Log.h"
 13  #include "I2PEndian.h"
 14  #include "Timestamp.h"
 15  #include "RouterContext.h"
 16  #include "NetDb.hpp"
 17  #include "Tunnel.h"
 18  #include "TransitTunnel.h"
 19  #include "I2NPProtocol.h"
 20  #include "version.h"
 21  
 22  namespace i2p
 23  {
 24  	std::shared_ptr<I2NPMessage> NewI2NPMessage ()
 25  	{
 26  		return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> >();
 27  	}
 28  
 29  	std::shared_ptr<I2NPMessage> NewI2NPShortMessage ()
 30  	{
 31  		return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
 32  	}
 33  
 34  	std::shared_ptr<I2NPMessage> NewI2NPMediumMessage ()
 35  	{
 36  		return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MEDIUM_MESSAGE_SIZE> >();
 37  	}
 38  
 39  	std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint)
 40  	{
 41  		return i2p::tunnel::tunnels.NewI2NPTunnelMessage (endpoint);
 42  	}
 43  
 44  	std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
 45  	{
 46  		len += I2NP_HEADER_SIZE + 2;
 47  		if (len <= I2NP_MAX_SHORT_MESSAGE_SIZE) return NewI2NPShortMessage ();
 48  		if (len <= I2NP_MAX_MEDIUM_MESSAGE_SIZE) return NewI2NPMediumMessage ();
 49  		return NewI2NPMessage ();
 50  	}
 51  
 52  	void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID, bool checksum)
 53  	{
 54  		SetTypeID (msgType);
 55  		if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4);
 56  		SetMsgID (replyMsgID);
 57  		SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
 58  		UpdateSize ();
 59  		if (checksum) UpdateChks ();
 60  	}
 61  
 62  	void I2NPMessage::RenewI2NPMessageHeader ()
 63  	{
 64  		uint32_t msgID;
 65  		RAND_bytes ((uint8_t *)&msgID, 4);
 66  		SetMsgID (msgID);
 67  		SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
 68  	}
 69  
 70  	bool I2NPMessage::IsExpired (uint64_t ts) const
 71  	{
 72  		auto exp = GetExpiration ();
 73  		return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
 74  	}
 75  
 76  	bool I2NPMessage::IsExpired () const
 77  	{
 78  		return IsExpired (i2p::util::GetMillisecondsSinceEpoch ());
 79  	}
 80  
 81  	std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
 82  	{
 83  		auto msg = NewI2NPMessage (len);
 84  		if (msg->Concat (buf, len) < len)
 85  			LogPrint (eLogError, "I2NP: Message length ", len, " exceeds max length ", msg->maxLen);
 86  		msg->FillI2NPMessageHeader (msgType, replyMsgID);
 87  		return msg;
 88  	}
 89  
 90  	std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
 91  	{
 92  		auto msg = NewI2NPMessage ();
 93  		if (msg->offset + len < msg->maxLen)
 94  		{
 95  			memcpy (msg->GetBuffer (), buf, len);
 96  			msg->len = msg->offset + len;
 97  			msg->from = from;
 98  		}
 99  		else
100  			LogPrint (eLogError, "I2NP: Message length ", len, " exceeds max length");
101  		return msg;
102  	}
103  
104  	std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg)
105  	{
106  		if (!msg) return nullptr;
107  		auto newMsg = NewI2NPMessage (msg->len);
108  		newMsg->offset = msg->offset;
109  		*newMsg = *msg;
110  		return newMsg;
111  	}
112  
113  	std::shared_ptr<I2NPMessage> CreateTunnelTestMsg (uint32_t msgID)
114  	{
115  		auto m = NewI2NPShortMessage ();
116  		uint8_t * buf = m->GetPayload ();
117  		htobe32buf (buf + TUNNEL_TEST_MSGID_OFFSET, msgID);
118  		htobe64buf (buf + TUNNEL_TEST_TIMESTAMP_OFFSET, i2p::util::GetMonotonicMicroseconds ());
119  		m->len += TUNNEL_TEST_SIZE;
120  		m->FillI2NPMessageHeader (eI2NPTunnelTest);
121  		return m;
122  	}
123  
124  	std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
125  	{
126  		auto m = NewI2NPShortMessage ();
127  		uint8_t * buf = m->GetPayload ();
128  		if (msgID)
129  		{
130  			htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
131  			htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ());
132  		}
133  		else // for SSU establishment
134  		{
135  			RAND_bytes ((uint8_t *)&msgID, 4);
136  			htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
137  			htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ());
138  		}
139  		m->len += DELIVERY_STATUS_SIZE;
140  		m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
141  		return m;
142  	}
143  
144  	std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
145  		uint32_t replyTunnelID, bool exploratory, std::unordered_set<i2p::data::IdentHash> * excludedPeers)
146  	{
147  		int cnt = excludedPeers ? excludedPeers->size () : 0;
148  		auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage ();
149  		uint8_t * buf = m->GetPayload ();
150  		memcpy (buf, key, 32); // key
151  		buf += 32;
152  		memcpy (buf, from, 32); // from
153  		buf += 32;
154  		uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP;
155  		if (replyTunnelID)
156  		{
157  			*buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag
158  			htobe32buf (buf+1, replyTunnelID);
159  			buf += 5;
160  		}
161  		else
162  		{
163  			*buf = flag; // flag
164  			buf++;
165  		}
166  
167  		if (excludedPeers)
168  		{
169  			htobe16buf (buf, cnt);
170  			buf += 2;
171  			for (auto& it: *excludedPeers)
172  			{
173  				memcpy (buf, it, 32);
174  				buf += 32;
175  			}
176  		}
177  		else
178  		{
179  			// nothing to exclude
180  			htobuf16 (buf, 0);
181  			buf += 2;
182  		}
183  
184  		m->len += (buf - m->GetPayload ());
185  		m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
186  		return m;
187  	}
188  
189  	std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
190  		const std::unordered_set<i2p::data::IdentHash>& excludedFloodfills,
191  		std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey,
192  			const uint8_t * replyTag, bool replyECIES)
193  	{
194  		int cnt = excludedFloodfills.size ();
195  		auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage ();
196  		uint8_t * buf = m->GetPayload ();
197  		memcpy (buf, dest, 32); // key
198  		buf += 32;
199  		memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
200  		buf += 32;
201  		*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
202  		*buf |= (replyECIES ? DATABASE_LOOKUP_ECIES_FLAG : DATABASE_LOOKUP_ENCRYPTION_FLAG);
203  		buf ++;
204  		htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
205  		buf += 4;
206  
207  		// excluded
208  		if (cnt > 512)
209  		{
210  			LogPrint (eLogWarning, "I2NP: Too many peers to exclude ", cnt, " for DatabaseLookup");
211  			cnt = 0;
212  		}
213  		htobe16buf (buf, cnt);
214  		buf += 2;
215  		if (cnt > 0)
216  		{
217  			for (auto& it: excludedFloodfills)
218  			{
219  				memcpy (buf, it, 32);
220  				buf += 32;
221  			}
222  		}
223  		// encryption
224  		memcpy (buf, replyKey, 32);
225  		buf[32] = 1; // 1 tag
226  		if (replyECIES)
227  		{
228  			memcpy (buf + 33, replyTag, 8); // 8 bytes tag
229  			buf += 41;
230  		}
231  		else
232  		{
233  			memcpy (buf + 33, replyTag, 32); // 32 bytes tag
234  			buf += 65;
235  		}
236  
237  		m->len += (buf - m->GetPayload ());
238  		m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
239  		return m;
240  	}
241  
242  	std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
243  		std::vector<i2p::data::IdentHash> routers)
244  	{
245  		auto m = NewI2NPShortMessage ();
246  		uint8_t * buf = m->GetPayload ();
247  		size_t len = 0;
248  		memcpy (buf, ident, 32);
249  		len += 32;
250  		buf[len] = routers.size ();
251  		len++;
252  		for (const auto& it: routers)
253  		{
254  			memcpy (buf + len, it, 32);
255  			len += 32;
256  		}
257  		memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
258  		len += 32;
259  		m->len += len;
260  		m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply);
261  		return m;
262  	}
263  
264  	std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router,
265  		uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
266  	{
267  		if (!router) // we send own RouterInfo
268  			router = context.GetSharedRouterInfo ();
269  
270  		if (!router->GetBuffer ())
271  		{
272  			LogPrint (eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore");
273  			return nullptr;
274  		}
275  
276  		auto m = NewI2NPShortMessage ();
277  		uint8_t * payload = m->GetPayload ();
278  
279  		memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
280  		payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo
281  		htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
282  		uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
283  		if (replyToken)
284  		{
285  			if (replyTunnel)
286  			{
287  				htobe32buf (buf, replyTunnel->GetNextTunnelID ());
288  				buf += 4; // reply tunnelID
289  				memcpy (buf, replyTunnel->GetNextIdentHash (), 32);
290  				buf += 32; // reply tunnel gateway
291  			}
292  			else
293  			{
294  				memset (buf, 0, 4); // zero tunnelID means direct reply
295  				buf += 4;
296  				memcpy (buf, context.GetIdentHash (), 32);
297  				buf += 32;
298  			}
299  		}
300  
301  		uint8_t * sizePtr = buf;
302  		buf += 2;
303  		m->len += (buf - payload); // payload size
304  		size_t size = 0;
305  		if (router->GetBufferLen () + (buf - payload) <= 940) // fits one tunnel message
306  			size = i2p::data::GzipNoCompression (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
307  		else
308  		{
309  			i2p::data::GzipDeflator deflator;
310  			size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
311  		}
312  		if (size)
313  		{
314  			htobe16buf (sizePtr, size); // size
315  			m->len += size;
316  		}
317  		else
318  			m = nullptr;
319  		if (m)
320  			m->FillI2NPMessageHeader (eI2NPDatabaseStore);
321  		return m;
322  	}
323  
324  	std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
325  	{
326  		if (!leaseSet) return nullptr;
327  		auto m = NewI2NPShortMessage ();
328  		uint8_t * payload = m->GetPayload ();
329  		memcpy (payload + DATABASE_STORE_KEY_OFFSET, storeHash, 32);
330  		payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // 1 for LeaseSet
331  		htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
332  		size_t size = DATABASE_STORE_HEADER_SIZE;
333  		memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
334  		size += leaseSet->GetBufferLen ();
335  		m->len += size;
336  		m->FillI2NPMessageHeader (eI2NPDatabaseStore);
337  		return m;
338  	}
339  
340  	std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
341  	{
342  		if (!leaseSet) return nullptr;
343  		auto m = NewI2NPShortMessage ();
344  		uint8_t * payload = m->GetPayload ();
345  		memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetStoreHash (), 32);
346  		payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // LeaseSet or LeaseSet2
347  		htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
348  		size_t size = DATABASE_STORE_HEADER_SIZE;
349  		if (replyToken && replyTunnel)
350  		{
351  			if (replyTunnel)
352  			{
353  				htobe32buf (payload + size, replyTunnel->GetNextTunnelID ());
354  				size += 4; // reply tunnelID
355  				memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32);
356  				size += 32; // reply tunnel gateway
357  			}
358  			else
359  				htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
360  		}
361  		memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
362  		size += leaseSet->GetBufferLen ();
363  		m->len += size;
364  		m->FillI2NPMessageHeader (eI2NPDatabaseStore);
365  		return m;
366  	}
367  
368  	bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg)
369  	{
370  		if (!msg || msg->GetTypeID () != eI2NPDatabaseStore) return false;
371  		return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
372  	}
373  
374  	std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
375  	{
376  		auto msg = NewI2NPTunnelMessage (false);
377  		msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
378  		msg->FillI2NPMessageHeader (eI2NPTunnelData);
379  		return msg;
380  	}
381  
382  	std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
383  	{
384  		auto msg = NewI2NPTunnelMessage (false);
385  		htobe32buf (msg->GetPayload (), tunnelID);
386  		msg->len += 4; // tunnelID
387  		msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
388  		msg->FillI2NPMessageHeader (eI2NPTunnelData);
389  		return msg;
390  	}
391  
392  	std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint)
393  	{
394  		auto msg = NewI2NPTunnelMessage (endpoint);
395  		msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
396  		return msg;
397  	}
398  
399  	std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
400  	{
401  		auto msg = NewI2NPMessage (len);
402  		uint8_t * payload = msg->GetPayload ();
403  		htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
404  		htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
405  		msg->len += TUNNEL_GATEWAY_HEADER_SIZE;
406  		if (msg->Concat (buf, len) < len)
407  			LogPrint (eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen);
408  		msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
409  		return msg;
410  	}
411  
412  	std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
413  	{
414  		if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
415  		{
416  			// message is capable to be used without copying
417  			uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE;
418  			htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
419  			int len = msg->GetLength ();
420  			htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
421  			msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
422  			msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
423  			msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
424  			return msg;
425  		}
426  		else
427  		{
428  			auto newMsg = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
429  			if (msg->onDrop) newMsg->onDrop = msg->onDrop;
430  			return newMsg;
431  		}
432  	}
433  
434  	std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
435  		const uint8_t * buf, size_t len, uint32_t replyMsgID)
436  	{
437  		auto msg = NewI2NPMessage (len);
438  		size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
439  		msg->offset += gatewayMsgOffset;
440  		msg->len += gatewayMsgOffset;
441  		if (msg->Concat (buf, len) < len)
442  			LogPrint (eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen);
443  		msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message
444  		len = msg->GetLength ();
445  		msg->offset -= gatewayMsgOffset;
446  		uint8_t * payload = msg->GetPayload ();
447  		htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
448  		htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
449  		msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message
450  		return msg;
451  	}
452  
453  	size_t GetI2NPMessageLength (const uint8_t * msg, size_t len)
454  	{
455  		if (len < I2NP_HEADER_SIZE_OFFSET + 2)
456  		{
457  			LogPrint (eLogError, "I2NP: Message length ", len, " is smaller than header");
458  			return len;
459  		}
460  		auto l = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
461  		if (l > len)
462  		{
463  			LogPrint (eLogError, "I2NP: Message length ", l, " exceeds buffer length ", len);
464  			l = len;
465  		}
466  		return l;
467  	}
468  
469  	void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
470  	{
471  		if (msg)
472  		{
473  			uint8_t typeID = msg->GetTypeID ();
474  			LogPrint (eLogDebug, "I2NP: Handling message with type ", (int)typeID);
475  			switch (typeID)
476  			{
477  				case eI2NPTunnelData:
478  					if (!msg->from)
479  						i2p::tunnel::tunnels.PostTunnelData (msg);
480  				break;
481  				case eI2NPTunnelGateway:
482  					if (!msg->from)
483  						i2p::tunnel::tunnels.PostTunnelData (msg);
484  				break;
485  				case eI2NPGarlic:
486  				{
487  					if (msg->from && msg->from->GetTunnelPool ())
488  						msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
489  					else
490  						i2p::context.ProcessGarlicMessage (msg);
491  					break;
492  				}
493  				case eI2NPDatabaseStore:
494  					// forward to netDb if came directly or through exploratory tunnel as response to our request
495  					if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
496  						i2p::data::netdb.PostI2NPMsg (msg);
497  				break;
498  				case eI2NPDatabaseSearchReply:
499  					if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
500  						i2p::data::netdb.PostDatabaseSearchReplyMsg (msg);
501  				break;
502  				case eI2NPDatabaseLookup:
503  					// forward to netDb if floodfill and came directly
504  					if (!msg->from && i2p::context.IsFloodfill ())
505  						i2p::data::netdb.PostI2NPMsg (msg);
506  				break;
507  				case eI2NPDeliveryStatus:
508  				{
509  					if (msg->from && msg->from->GetTunnelPool ())
510  						msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg);
511  					else
512  						i2p::context.ProcessDeliveryStatusMessage (msg);
513  					break;
514  				}
515  				case eI2NPTunnelTest:
516  					if (msg->from && msg->from->GetTunnelPool ())
517  						msg->from->GetTunnelPool ()->ProcessTunnelTest (msg);
518  				break;
519  				case eI2NPVariableTunnelBuild:
520  				case eI2NPTunnelBuild:
521  				case eI2NPShortTunnelBuild:
522  					// forward to tunnel thread
523  					if (!msg->from)
524  						i2p::tunnel::tunnels.PostTunnelData (msg);
525  				break;
526  				case eI2NPVariableTunnelBuildReply:
527  				case eI2NPTunnelBuildReply:
528  				case eI2NPShortTunnelBuildReply:
529  					// forward to tunnel thread
530  					i2p::tunnel::tunnels.PostTunnelData (msg);
531  				break;
532  				default:
533  					LogPrint(eLogError, "I2NP: Unexpected I2NP message with type ", int(typeID), " during handling; skipping");
534  			}
535  		}
536  	}
537  
538  	I2NPMessagesHandler::~I2NPMessagesHandler ()
539  	{
540  		Flush ();
541  	}
542  
543  	void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage>&& msg)
544  	{
545  		if (msg)
546  		{
547  			switch (msg->GetTypeID ())
548  			{
549  				case eI2NPTunnelData:
550  					m_TunnelMsgs.push_back (msg);
551  				break;
552  				case eI2NPTunnelGateway:
553  					m_TunnelGatewayMsgs.push_back (msg);
554  				break;
555  				case eI2NPVariableTunnelBuild:
556  				case eI2NPTunnelBuild:
557  				case eI2NPShortTunnelBuild:
558  				{
559  					auto ts = i2p::util::GetMonotonicMilliseconds ();
560  					if (!m_LastTunnelBuildMessageTimestamp ||
561  						ts > m_LastTunnelBuildMessageTimestamp + TUNNEL_BUILD_MESSAGES_MIN_INTERVAL ||
562  						m_NumDroppedTunnelBuildMessages > MAX_NUM_DROPPED_TUNNEL_BUILD_MESSAGES)
563  					{
564  						m_NumThrottledTunnelBuildMessages = 0;
565  						if (m_NumDroppedTunnelBuildMessages > 0)
566  						{
567  							LogPrint (eLogWarning, "I2NP: ", m_NumDroppedTunnelBuildMessages, " tunnel build messages dropped");
568  							m_NumDroppedTunnelBuildMessages = 0;
569  						}
570  						HandleI2NPMessage (msg);
571  					}
572  					else
573  					{
574  						if (m_NumThrottledTunnelBuildMessages < MAX_NUM_THROTTLED_TUNNEL_BUILD_MESSAGES)
575  						{
576  							// let TBM go through
577  							m_NumThrottledTunnelBuildMessages++;
578  							HandleI2NPMessage (msg);
579  						}
580  						else // drop TBM
581  							m_NumDroppedTunnelBuildMessages++;
582  					}
583  					m_LastTunnelBuildMessageTimestamp = ts;
584  					break;
585  				}
586  				default:
587  					HandleI2NPMessage (msg);
588  			}
589  		}
590  	}
591  
592  	void I2NPMessagesHandler::Flush ()
593  	{
594  		if (!m_TunnelMsgs.empty ())
595  			i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
596  		if (!m_TunnelGatewayMsgs.empty ())
597  			i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
598  	}
599  }