TransitTunnel.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 "I2PEndian.h" 11 #include "Crypto.h" 12 #include "Log.h" 13 #include "Identity.h" 14 #include "RouterInfo.h" 15 #include "RouterContext.h" 16 #include "I2NPProtocol.h" 17 #include "Garlic.h" 18 #include "ECIESX25519AEADRatchetSession.h" 19 #include "Tunnel.h" 20 #include "Transports.h" 21 #include "TransitTunnel.h" 22 23 namespace i2p 24 { 25 namespace tunnel 26 { 27 TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, 28 const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, 29 const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): 30 TunnelBase (receiveTunnelID, nextTunnelID, nextIdent), 31 m_LayerKey (layerKey), m_IVKey (ivKey) 32 { 33 } 34 35 void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) 36 { 37 if (!m_Encryption) 38 { 39 m_Encryption.reset (new i2p::crypto::TunnelEncryption); 40 m_Encryption->SetKeys (m_LayerKey, m_IVKey); 41 } 42 m_Encryption->Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); 43 i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE); 44 } 45 46 std::string TransitTunnel::GetNextPeerName () const 47 { 48 return i2p::data::GetIdentHashAbbreviation (GetNextIdentHash ()); 49 } 50 51 void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) 52 { 53 LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ()); 54 } 55 56 void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) 57 { 58 LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ()); 59 } 60 61 TransitTunnelParticipant::~TransitTunnelParticipant () 62 { 63 } 64 65 void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) 66 { 67 EncryptTunnelMsg (tunnelMsg, tunnelMsg); 68 69 m_NumTransmittedBytes += tunnelMsg->GetLength (); 70 htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ()); 71 // update header, expiration and size remain the same 72 tunnelMsg->SetMsgID (i2p::tunnel::tunnels.GetRng ()()); // assign new msgID 73 tunnelMsg->UpdateChks (); // new checksum TODO: remove later 74 m_TunnelDataMsgs.push_back (tunnelMsg); 75 } 76 77 void TransitTunnelParticipant::FlushTunnelDataMsgs () 78 { 79 if (!m_TunnelDataMsgs.empty ()) 80 { 81 auto num = m_TunnelDataMsgs.size (); 82 if (num > 1) 83 LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num); 84 if (!m_Sender) m_Sender = std::make_unique<TunnelTransportSender>(); 85 m_Sender->SendMessagesTo (GetNextIdentHash (), m_TunnelDataMsgs); // send and clear 86 } 87 } 88 89 std::string TransitTunnelParticipant::GetNextPeerName () const 90 { 91 if (m_Sender) 92 { 93 auto transport = m_Sender->GetCurrentTransport (); 94 if (transport) 95 return TransitTunnel::GetNextPeerName () + "-" + 96 i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); 97 } 98 return TransitTunnel::GetNextPeerName (); 99 } 100 101 void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) 102 { 103 TunnelMessageBlock block; 104 block.deliveryType = eDeliveryTypeLocal; 105 block.data = msg; 106 std::lock_guard<std::mutex> l(m_SendMutex); 107 m_Gateway.PutTunnelDataMsg (block); 108 } 109 110 void TransitTunnelGateway::FlushTunnelDataMsgs () 111 { 112 std::lock_guard<std::mutex> l(m_SendMutex); 113 m_Gateway.SendBuffer (); 114 } 115 116 std::string TransitTunnelGateway::GetNextPeerName () const 117 { 118 const auto& sender = m_Gateway.GetSender (); 119 if (sender) 120 { 121 auto transport = sender->GetCurrentTransport (); 122 if (transport) 123 return TransitTunnel::GetNextPeerName () + "-" + 124 i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); 125 } 126 return TransitTunnel::GetNextPeerName (); 127 } 128 129 void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) 130 { 131 auto newMsg = CreateEmptyTunnelDataMsg (true); 132 EncryptTunnelMsg (tunnelMsg, newMsg); 133 134 LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); 135 std::lock_guard<std::mutex> l(m_HandleMutex); 136 if (!m_Endpoint) m_Endpoint = std::make_unique<TunnelEndpoint>(false); // transit endpoint is always outbound 137 m_Endpoint->HandleDecryptedTunnelDataMsg (newMsg); 138 } 139 140 void TransitTunnelEndpoint::FlushTunnelDataMsgs () 141 { 142 if (m_Endpoint) 143 { 144 std::lock_guard<std::mutex> l(m_HandleMutex); 145 m_Endpoint->FlushI2NPMsgs (); 146 } 147 } 148 149 void TransitTunnelEndpoint::Cleanup () 150 { 151 if (m_Endpoint) 152 { 153 std::lock_guard<std::mutex> l(m_HandleMutex); 154 m_Endpoint->Cleanup (); 155 } 156 } 157 158 std::string TransitTunnelEndpoint::GetNextPeerName () const 159 { 160 if (!m_Endpoint) return ""; 161 auto hash = m_Endpoint->GetCurrentHash (); 162 if (hash) 163 { 164 const auto& sender = m_Endpoint->GetSender (); 165 if (sender) 166 { 167 auto transport = sender->GetCurrentTransport (); 168 if (transport) 169 return i2p::data::GetIdentHashAbbreviation (*hash) + "-" + 170 i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); 171 else 172 return i2p::data::GetIdentHashAbbreviation (*hash); 173 } 174 } 175 return ""; 176 } 177 178 std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, 179 const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, 180 const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey, 181 bool isGateway, bool isEndpoint) 182 { 183 if (isEndpoint) 184 { 185 LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created"); 186 return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); 187 } 188 else if (isGateway) 189 { 190 LogPrint (eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created"); 191 return std::make_shared<TransitTunnelGateway> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); 192 } 193 else 194 { 195 LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); 196 return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); 197 } 198 } 199 200 TransitTunnels::TransitTunnels (): 201 m_IsRunning (false), m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL) 202 { 203 } 204 205 TransitTunnels::~TransitTunnels () 206 { 207 Stop (); 208 } 209 210 void TransitTunnels::Start () 211 { 212 m_IsRunning = true; 213 m_Thread.reset (new std::thread (std::bind (&TransitTunnels::Run, this))); 214 } 215 216 void TransitTunnels::Stop () 217 { 218 m_IsRunning = false; 219 m_TunnelBuildMsgQueue.WakeUp (); 220 if (m_Thread) 221 { 222 m_Thread->join (); 223 m_Thread = nullptr; 224 } 225 m_TransitTunnels.clear (); 226 } 227 228 void TransitTunnels::Run () 229 { 230 i2p::util::SetThreadName("TBM"); 231 uint64_t lastTs = 0; 232 std::list<std::shared_ptr<I2NPMessage> > msgs; 233 while (m_IsRunning) 234 { 235 try 236 { 237 if (m_TunnelBuildMsgQueue.Wait (TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL, 0)) 238 { 239 m_TunnelBuildMsgQueue.GetWholeQueue (msgs); 240 while (!msgs.empty ()) 241 { 242 auto msg = msgs.front (); msgs.pop_front (); 243 if (!msg) continue; 244 uint8_t typeID = msg->GetTypeID (); 245 switch (typeID) 246 { 247 case eI2NPShortTunnelBuild: 248 HandleShortTransitTunnelBuildMsg (std::move (msg)); 249 break; 250 case eI2NPVariableTunnelBuild: 251 HandleVariableTransitTunnelBuildMsg (std::move (msg)); 252 break; 253 default: 254 LogPrint (eLogWarning, "TransitTunnel: Unexpected message type ", (int) typeID); 255 } 256 if (!m_IsRunning) break; 257 } 258 } 259 if (m_IsRunning) 260 { 261 uint64_t ts = i2p::util::GetSecondsSinceEpoch (); 262 if (ts >= lastTs + TUNNEL_MANAGE_INTERVAL || ts + TUNNEL_MANAGE_INTERVAL < lastTs) 263 { 264 ManageTransitTunnels (ts); 265 lastTs = ts; 266 } 267 } 268 } 269 catch (std::exception& ex) 270 { 271 LogPrint (eLogError, "TransitTunnel: Runtime exception: ", ex.what ()); 272 } 273 } 274 } 275 276 void TransitTunnels::PostTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg) 277 { 278 if (msg) 279 { 280 if (m_TunnelBuildMsgQueue.GetSize () < TRANSIT_TUNNELS_BUILD_MSG_QUEUE_MAX_SIZE) 281 m_TunnelBuildMsgQueue.Put (msg); 282 else 283 m_TunnelBuildMsgQueue.WakeUp (); // tell TBM thread to process queue 284 } 285 } 286 287 void TransitTunnels::HandleShortTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg) 288 { 289 if (!msg) return; 290 uint8_t * buf = msg->GetPayload(); 291 size_t len = msg->GetPayloadLength(); 292 int num = buf[0]; 293 LogPrint (eLogDebug, "TransitTunnel: ShortTunnelBuild ", num, " records"); 294 if (num > i2p::tunnel::MAX_NUM_RECORDS) 295 { 296 LogPrint (eLogError, "TransitTunnel: Too many records in ShortTunnelBuild message ", num); 297 return; 298 } 299 if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) 300 { 301 LogPrint (eLogError, "TransitTunnel: ShortTunnelBuild message of ", num, " records is too short ", len); 302 return; 303 } 304 const uint8_t * record = buf + 1; 305 for (int i = 0; i < num; i++) 306 { 307 if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) 308 { 309 LogPrint (eLogDebug, "TransitTunnel: Short request record ", i, " is ours"); 310 uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; 311 if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) 312 { 313 LogPrint (eLogWarning, "TransitTunnel: Can't decrypt short request record ", i); 314 return; 315 } 316 if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES 317 { 318 LogPrint (eLogWarning, "TransitTunnel: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); 319 return; 320 } 321 auto& noiseState = i2p::context.GetCurrentNoiseState (); 322 uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305 323 i2p::crypto::AESKey layerKey, ivKey; // AES 324 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); 325 memcpy (replyKey, noiseState.m_CK + 32, 32); 326 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); 327 memcpy (layerKey, noiseState.m_CK + 32, 32); 328 bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; 329 if (isEndpoint) 330 { 331 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); 332 memcpy (ivKey, noiseState.m_CK + 32, 32); 333 } 334 else 335 { 336 if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours 337 { 338 LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in short request record"); 339 return; 340 } 341 memcpy (ivKey, noiseState.m_CK , 32); 342 } 343 344 // check if we accept this tunnel 345 std::shared_ptr<i2p::tunnel::TransitTunnel> transitTunnel; 346 uint8_t retCode = 0; 347 if (i2p::context.AcceptsTunnels () && !i2p::context.IsLimitedConnectivity ()) 348 { 349 auto congestionLevel = i2p::context.GetCongestionLevel (false); 350 if (congestionLevel < CONGESTION_LEVEL_FULL) 351 { 352 if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) 353 { 354 // random reject depending on congestion level 355 int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; 356 if (congestionLevel > level) 357 retCode = 30; 358 } 359 } 360 else 361 retCode = 30; 362 } 363 else 364 retCode = 30; 365 366 if (!retCode) 367 { 368 i2p::data::IdentHash nextIdent(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET); 369 bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; 370 if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent)) 371 { 372 // create new transit tunnel 373 transitTunnel = CreateTransitTunnel ( 374 bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), 375 nextIdent, 376 bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), 377 layerKey, ivKey, 378 clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, 379 isEndpoint); 380 if (!AddTransitTunnel (transitTunnel)) 381 retCode = 30; 382 } 383 else 384 // decline tunnel going to duplicated router 385 retCode = 30; 386 } 387 388 // encrypt reply 389 uint8_t nonce[12]; 390 memset (nonce, 0, 12); 391 uint8_t * reply = buf + 1; 392 for (int j = 0; j < num; j++) 393 { 394 nonce[4] = j; // nonce is record # 395 if (j == i) 396 { 397 memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options 398 reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; 399 if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, 400 noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt 401 { 402 LogPrint (eLogWarning, "TransitTunnel: Short reply AEAD encryption failed"); 403 return; 404 } 405 } 406 else 407 i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); 408 reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; 409 } 410 // send reply 411 auto onDrop = [transitTunnel]() 412 { 413 if (transitTunnel) 414 { 415 LogPrint (eLogDebug, "TransitTunnel: Failed to send reply for transit tunnel ", transitTunnel->GetTunnelID ()); 416 auto t = transitTunnel->GetCreationTime (); 417 if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT) 418 // make transit tunnel expired 419 transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT); 420 } 421 }; 422 if (isEndpoint) 423 { 424 auto replyMsg = NewI2NPShortMessage (); 425 replyMsg->Concat (buf, len); 426 replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); 427 if (transitTunnel) replyMsg->onDrop = onDrop; 428 if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), 429 clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? 430 { 431 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); 432 uint64_t tag; 433 memcpy (&tag, noiseState.m_CK, 8); 434 // we send it to reply tunnel 435 i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 436 CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), 437 i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); 438 } 439 else 440 { 441 // IBGW is local 442 uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); 443 auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); 444 if (tunnel) 445 { 446 tunnel->SendTunnelDataMsg (replyMsg); 447 tunnel->FlushTunnelDataMsgs (); 448 } 449 else 450 LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); 451 } 452 } 453 else 454 { 455 auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, 456 bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); 457 if (transitTunnel) msg->onDrop = onDrop; 458 i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg); 459 } 460 return; 461 } 462 record += SHORT_TUNNEL_BUILD_RECORD_SIZE; 463 } 464 } 465 466 bool TransitTunnels::HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) 467 { 468 for (int i = 0; i < num; i++) 469 { 470 uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; 471 if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) 472 { 473 LogPrint (eLogDebug, "TransitTunnel: Build request record ", i, " is ours"); 474 if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) 475 { 476 LogPrint (eLogWarning, "TransitTunnel: Failed to decrypt tunnel build record"); 477 return false; 478 } 479 if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours 480 !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint 481 { 482 LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in tunnel build record"); 483 return false; 484 } 485 uint8_t retCode = 0; 486 // decide if we should accept tunnel 487 bool accept = i2p::context.AcceptsTunnels () && !i2p::context.IsLimitedConnectivity (); 488 if (accept) 489 { 490 auto congestionLevel = i2p::context.GetCongestionLevel (false); 491 if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) 492 { 493 if (congestionLevel < CONGESTION_LEVEL_FULL) 494 { 495 // random reject depending on congestion level 496 int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; 497 if (congestionLevel > level) 498 accept = false; 499 } 500 else 501 accept = false; 502 } 503 } 504 505 if (accept) 506 { 507 i2p::data::IdentHash nextIdent(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET); 508 bool isEndpoint = clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; 509 if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent)) 510 { 511 auto transitTunnel = CreateTransitTunnel ( 512 bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), 513 nextIdent, 514 bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), 515 clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, 516 clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, 517 clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, 518 isEndpoint); 519 if (!AddTransitTunnel (transitTunnel)) 520 retCode = 30; 521 } 522 else 523 // decline tunnel going to duplicated router 524 retCode = 30; 525 } 526 else 527 retCode = 30; // always reject with bandwidth reason (30) 528 529 // replace record to reply 530 memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options 531 record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; 532 // encrypt reply 533 i2p::crypto::CBCEncryption encryption; 534 for (int j = 0; j < num; j++) 535 { 536 uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; 537 if (j == i) 538 { 539 uint8_t nonce[12]; 540 memset (nonce, 0, 12); 541 auto& noiseState = i2p::context.GetCurrentNoiseState (); 542 if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, 543 noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt 544 { 545 LogPrint (eLogWarning, "TransitTunnel: Reply AEAD encryption failed"); 546 return false; 547 } 548 } 549 else 550 { 551 encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); 552 encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, reply); 553 } 554 } 555 return true; 556 } 557 } 558 return false; 559 } 560 561 void TransitTunnels::HandleVariableTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg) 562 { 563 if (!msg) return; 564 uint8_t * buf = msg->GetPayload(); 565 size_t len = msg->GetPayloadLength(); 566 int num = buf[0]; 567 LogPrint (eLogDebug, "TransitTunnel: VariableTunnelBuild ", num, " records"); 568 if (num > i2p::tunnel::MAX_NUM_RECORDS) 569 { 570 LogPrint (eLogError, "TransitTunnle: Too many records in VaribleTunnelBuild message ", num); 571 return; 572 } 573 if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) 574 { 575 LogPrint (eLogError, "TransitTunnel: VaribleTunnelBuild message of ", num, " records is too short ", len); 576 return; 577 } 578 uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; 579 if (HandleBuildRequestRecords (num, buf + 1, clearText)) 580 { 581 if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel 582 { 583 // so we send it to reply tunnel 584 i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 585 CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), 586 eI2NPVariableTunnelBuildReply, buf, len, 587 bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); 588 } 589 else 590 i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 591 CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, 592 bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); 593 } 594 } 595 596 bool TransitTunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel) 597 { 598 if (tunnels.AddTunnel (tunnel)) 599 m_TransitTunnels.push_back (tunnel); 600 else 601 { 602 LogPrint (eLogError, "TransitTunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); 603 return false; 604 } 605 return true; 606 } 607 608 void TransitTunnels::ManageTransitTunnels (uint64_t ts) 609 { 610 for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) 611 { 612 auto tunnel = *it; 613 if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || 614 ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime () || 615 (!tunnel->GetNumTransmittedBytes () && ts > tunnel->GetCreationTime () + 2*TUNNEL_EXPIRATION_THRESHOLD)) // inactive? 616 { 617 LogPrint (eLogDebug, "TransitTunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired or inactive"); 618 tunnels.RemoveTunnel (tunnel->GetTunnelID ()); 619 it = m_TransitTunnels.erase (it); 620 } 621 else 622 { 623 tunnel->Cleanup (); 624 it++; 625 } 626 } 627 } 628 629 int TransitTunnels::GetTransitTunnelsExpirationTimeout () 630 { 631 int timeout = 0; 632 uint32_t ts = i2p::util::GetSecondsSinceEpoch (); 633 // TODO: possible race condition with I2PControl 634 for (const auto& it : m_TransitTunnels) 635 { 636 int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; 637 if (t > timeout) timeout = t; 638 } 639 return timeout; 640 } 641 } 642 }