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 }