Destination.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 <algorithm> 10 #include <cassert> 11 #include <string> 12 #include <set> 13 #include <vector> 14 #include <charconv> 15 #include <boost/algorithm/string.hpp> 16 #include "Crypto.h" 17 #include "ECIESX25519AEADRatchetSession.h" 18 #include "Log.h" 19 #include "FS.h" 20 #include "Timestamp.h" 21 #include "NetDb.hpp" 22 #include "Destination.h" 23 24 namespace i2p 25 { 26 namespace client 27 { 28 LeaseSetDestination::LeaseSetDestination (boost::asio::io_context& service, 29 bool isPublic, const i2p::util::Mapping * params): 30 m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), 31 m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), 32 m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service), 33 m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE) 34 { 35 int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH; 36 int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY; 37 int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; 38 int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY; 39 int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE; 40 int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE; 41 int numTags = DEFAULT_TAGS_TO_SEND; 42 bool isHighBandwidth = true; 43 std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers; 44 std::string_view explicitPeersStr, trustedRoutersStr; 45 try 46 { 47 if (params) 48 { 49 params->Get (I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen); 50 params->Get (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen); 51 params->Get (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQty); 52 params->Get (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQty); 53 params->Get (I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, inVar); 54 params->Get (I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, outVar); 55 params->Get (I2CP_PARAM_TAGS_TO_SEND, numTags); 56 LogPrint (eLogInfo, "Destination: Parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags"); 57 int ratchetsInboundTags = 0; 58 if (params->Get (I2CP_PARAM_RATCHET_INBOUND_TAGS, ratchetsInboundTags)) 59 { 60 if (ratchetsInboundTags && ratchetsInboundTags < i2p::garlic::ECIESX25519_MIN_NUM_GENERATED_TAGS) 61 ratchetsInboundTags = i2p::garlic::ECIESX25519_MIN_NUM_GENERATED_TAGS; 62 SetNumRatchetInboundTags (ratchetsInboundTags); 63 } 64 explicitPeersStr = (*params)[I2CP_PARAM_EXPLICIT_PEERS]; 65 trustedRoutersStr = (*params)[I2CP_PARAM_TRUSTED_ROUTERS]; 66 m_Nickname = (*params)[I2CP_PARAM_INBOUND_NICKNAME]; 67 if (m_Nickname.empty ()) // try outbound 68 m_Nickname = (*params)[I2CP_PARAM_OUTBOUND_NICKNAME]; 69 // otherwise we set default nickname in Start when we know local address 70 params->Get (I2CP_PARAM_DONT_PUBLISH_LEASESET, m_IsPublic); // override isPublic 71 params->Get (I2CP_PARAM_LEASESET_TYPE, m_LeaseSetType); 72 if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) 73 { 74 // authentication for encrypted LeaseSet 75 int authType = 0; 76 if (params->Get (I2CP_PARAM_LEASESET_AUTH_TYPE, authType)) 77 { 78 if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK) 79 m_AuthType = authType; 80 else 81 LogPrint (eLogError, "Destination: Unknown auth type: ", authType); 82 } 83 } 84 auto leaseSetPrivKey = (*params)[I2CP_PARAM_LEASESET_PRIV_KEY]; 85 if (!leaseSetPrivKey.empty ()) 86 { 87 m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>()); 88 if (m_LeaseSetPrivKey->FromBase64 (leaseSetPrivKey) != 32) 89 { 90 LogPrint(eLogCritical, "Destination: Invalid value i2cp.leaseSetPrivKey: ", leaseSetPrivKey); 91 m_LeaseSetPrivKey.reset (nullptr); 92 } 93 } 94 int streamingProfile = 0; 95 if (params->Get (I2CP_PARAM_STREAMING_PROFILE, streamingProfile)) 96 isHighBandwidth = streamingProfile != STREAMING_PROFILE_INTERACTIVE; 97 } 98 } 99 catch (std::exception & ex) 100 { 101 LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); 102 } 103 SetNumTags (numTags); 104 m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar, isHighBandwidth); 105 if (!explicitPeersStr.empty ()) 106 m_Pool->SetExplicitPeers (i2p::data::ExtractIdentHashes (explicitPeersStr)); 107 if (!trustedRoutersStr.empty ()) 108 m_Pool->SetTrustedRouters (i2p::data::ExtractIdentHashes (trustedRoutersStr)); 109 if(params) 110 { 111 int maxLatency = 0; 112 if (params->Get (I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency)) 113 { 114 int minLatency = 0; 115 if (params->Get (I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency)) 116 { 117 if (minLatency > 0 && maxLatency > 0) 118 { 119 // set tunnel pool latency 120 LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minLatency, "ms, ", maxLatency, "ms]"); 121 m_Pool->RequireLatency(minLatency, maxLatency); 122 } 123 } 124 } 125 } 126 } 127 128 LeaseSetDestination::~LeaseSetDestination () 129 { 130 if (m_Pool) 131 i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); 132 for (auto& it: m_LeaseSetRequests) 133 it.second->Complete (nullptr); 134 } 135 136 void LeaseSetDestination::Start () 137 { 138 if (m_Nickname.empty ()) 139 m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname 140 LoadTags (); 141 m_Pool->SetLocalDestination (shared_from_this ()); 142 m_Pool->SetActive (true); 143 m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT)); 144 m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, 145 shared_from_this (), std::placeholders::_1)); 146 } 147 148 void LeaseSetDestination::Stop () 149 { 150 m_CleanupTimer.cancel (); 151 m_PublishConfirmationTimer.cancel (); 152 m_PublishVerificationTimer.cancel (); 153 if (m_Pool) 154 { 155 m_Pool->SetLocalDestination (nullptr); 156 i2p::tunnel::tunnels.StopTunnelPool (m_Pool); 157 } 158 SaveTags (); 159 CleanUp (); // GarlicDestination 160 } 161 162 bool LeaseSetDestination::Reconfigure(const i2p::util::Mapping& params) 163 { 164 params.Get (I2CP_PARAM_DONT_PUBLISH_LEASESET, m_IsPublic); 165 166 auto numTags = GetNumTags (); 167 params.Get (I2CP_PARAM_TAGS_TO_SEND, numTags); 168 auto numRatchetInboundTags = GetNumRatchetInboundTags (); 169 params.Get (I2CP_PARAM_RATCHET_INBOUND_TAGS, numRatchetInboundTags); 170 auto pool = GetTunnelPool(); 171 auto inLen = pool->GetNumInboundHops(); 172 params.Get (I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen); 173 auto outLen = pool->GetNumOutboundHops(); 174 params.Get (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen); 175 auto inQuant = pool->GetNumInboundTunnels(); 176 params.Get (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant); 177 auto outQuant = pool->GetNumOutboundTunnels(); 178 params.Get (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant); 179 int minLatency = 0; 180 params.Get (I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency); 181 int maxLatency = 0; 182 params.Get (I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency); 183 184 SetNumTags (numTags); 185 SetNumRatchetInboundTags (numRatchetInboundTags); 186 pool->RequireLatency(minLatency, maxLatency); 187 return pool->Reconfigure(inLen, outLen, inQuant, outQuant); 188 } 189 190 std::shared_ptr<i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident) 191 { 192 std::shared_ptr<i2p::data::LeaseSet> remoteLS; 193 { 194 std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex); 195 auto it = m_RemoteLeaseSets.find (ident); 196 if (it != m_RemoteLeaseSets.end ()) 197 remoteLS = it->second; 198 } 199 200 if (remoteLS) 201 { 202 if (!remoteLS->IsExpired ()) 203 { 204 if (remoteLS->ExpiresSoon()) 205 { 206 LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire"); 207 // update now before expiration for smooth handover 208 auto s = shared_from_this (); 209 RequestDestination(ident, [s, ident] (std::shared_ptr<i2p::data::LeaseSet> ls) { 210 if(ls && !ls->IsExpired()) 211 { 212 ls->PopulateLeases(); 213 { 214 std::lock_guard<std::mutex> _lock(s->m_RemoteLeaseSetsMutex); 215 s->m_RemoteLeaseSets[ident] = ls; 216 } 217 } 218 }); 219 } 220 return remoteLS; 221 } 222 else 223 { 224 LogPrint (eLogWarning, "Destination: Remote LeaseSet expired"); 225 std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex); 226 m_RemoteLeaseSets.erase (ident); 227 return nullptr; 228 } 229 } 230 return nullptr; 231 } 232 233 std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet () 234 { 235 if (!m_Pool) return nullptr; 236 if (!m_LeaseSet) 237 UpdateLeaseSet (); 238 auto ls = GetLeaseSetMt (); 239 return (ls && ls->GetInnerLeaseSet ()) ? ls->GetInnerLeaseSet () : ls; // always non-encrypted 240 } 241 242 std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSetMt () 243 { 244 std::lock_guard<std::mutex> l(m_LeaseSetMutex); 245 return m_LeaseSet; 246 } 247 248 void LeaseSetDestination::SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet) 249 { 250 { 251 std::lock_guard<std::mutex> l(m_LeaseSetMutex); 252 m_LeaseSet = newLeaseSet; 253 } 254 i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); 255 if (m_IsPublic) 256 { 257 auto s = shared_from_this (); 258 boost::asio::post (m_Service, [s](void) 259 { 260 s->m_PublishVerificationTimer.cancel (); 261 s->Publish (); 262 }); 263 } 264 } 265 266 void LeaseSetDestination::UpdateLeaseSet () 267 { 268 int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels 269 if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum 270 auto tunnels = m_Pool->GetInboundTunnels (numTunnels); 271 if (!tunnels.empty ()) 272 CreateNewLeaseSet (tunnels); 273 else 274 LogPrint (eLogInfo, "Destination: No inbound tunnels for LeaseSet"); 275 } 276 277 bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) 278 { 279 struct 280 { 281 uint8_t k[32], t[32]; 282 } data; 283 memcpy (data.k, key, 32); 284 memcpy (data.t, tag, 32); 285 auto s = shared_from_this (); 286 boost::asio::post (m_Service, [s,data](void) 287 { 288 s->AddSessionKey (data.k, data.t); 289 }); 290 return true; 291 } 292 293 void LeaseSetDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) 294 { 295 struct 296 { 297 uint8_t k[32]; 298 uint64_t t; 299 } data; 300 memcpy (data.k, key, 32); 301 data.t = tag; 302 auto s = shared_from_this (); 303 boost::asio::post (m_Service, [s,data](void) 304 { 305 s->AddECIESx25519Key (data.k, data.t); 306 }); 307 } 308 309 void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) 310 { 311 if (!msg) return; 312 bool empty = false; 313 { 314 std::lock_guard<std::mutex> l(m_IncomingMsgsQueueMutex); 315 empty = m_IncomingMsgsQueue.empty (); 316 m_IncomingMsgsQueue.push_back (msg); 317 } 318 if (empty) 319 boost::asio::post (m_Service, [s = shared_from_this ()]() 320 { 321 std::list<std::shared_ptr<I2NPMessage> > receivedMsgs; 322 { 323 std::lock_guard<std::mutex> l(s->m_IncomingMsgsQueueMutex); 324 s->m_IncomingMsgsQueue.swap (receivedMsgs); 325 } 326 for (auto& it: receivedMsgs) 327 s->HandleGarlicMessage (it); 328 }); 329 } 330 331 void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) 332 { 333 uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); 334 boost::asio::post (m_Service, std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID)); 335 } 336 337 void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len) 338 { 339 I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]); 340 uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET); 341 LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, 342 GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID, nullptr); 343 } 344 345 bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, 346 size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) 347 { 348 switch (typeID) 349 { 350 case eI2NPData: 351 HandleDataMessage (payload, len, from); 352 break; 353 case eI2NPDeliveryStatus: 354 HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); 355 break; 356 case eI2NPTunnelTest: 357 if (m_Pool) 358 m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET)); 359 break; 360 case eI2NPDatabaseStore: 361 HandleDatabaseStoreMessage (payload, len, from); 362 break; 363 case eI2NPDatabaseSearchReply: 364 HandleDatabaseSearchReplyMessage (payload, len); 365 break; 366 case eI2NPShortTunnelBuildReply: // might come as garlic encrypted 367 i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID)); 368 break; 369 default: 370 LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID); 371 return false; 372 } 373 return true; 374 } 375 376 void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len, 377 i2p::garlic::ECIESX25519AEADRatchetSession * from) 378 { 379 if (len < DATABASE_STORE_HEADER_SIZE) 380 { 381 LogPrint (eLogError, "Destination: Database store msg is too short ", len); 382 return; 383 } 384 uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); 385 size_t offset = DATABASE_STORE_HEADER_SIZE; 386 if (replyToken) 387 { 388 LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore"); 389 offset += 36; 390 } 391 if (offset > len || len > i2p::data::MAX_LS_BUFFER_SIZE + offset) 392 { 393 LogPrint (eLogError, "Destination: Database store message is too long ", len); 394 return; 395 } 396 i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET); 397 std::shared_ptr<i2p::data::LeaseSet> leaseSet; 398 std::shared_ptr<LeaseSetRequest> request; 399 switch (buf[DATABASE_STORE_TYPE_OFFSET]) 400 { 401 case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1 402 case i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2: // 3 403 { 404 LogPrint (eLogDebug, "Destination: Remote LeaseSet"); 405 std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex); 406 auto it = m_RemoteLeaseSets.find (key); 407 if (it != m_RemoteLeaseSets.end () && 408 it->second->GetStoreType () == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type 409 { 410 leaseSet = it->second; 411 if (leaseSet->IsNewer (buf + offset, len - offset)) 412 { 413 leaseSet->Update (buf + offset, len - offset, shared_from_this(), true); 414 if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ()) 415 LogPrint (eLogDebug, "Destination: Remote LeaseSet updated"); 416 else 417 { 418 LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed"); 419 m_RemoteLeaseSets.erase (it); 420 leaseSet = nullptr; 421 } 422 } 423 else 424 LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated"); 425 } 426 else 427 { 428 // add or replace 429 if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET) 430 leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet 431 else 432 { 433 leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], 434 buf + offset, len - offset, true, shared_from_this (), 435 from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType () ); // LeaseSet2 436 if (from) 437 { 438 uint8_t pub[32]; 439 leaseSet->Encrypt (nullptr, pub); 440 if (!memcmp (from->GetRemoteStaticKey (), pub, 32)) 441 from->SetDestination (leaseSet->GetIdentHash ()); 442 else 443 { 444 LogPrint (eLogError, "Destination: Remote LeaseSet static key mismatch"); 445 leaseSet = nullptr; 446 } 447 } 448 } 449 if (leaseSet && leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ()) 450 { 451 if (leaseSet->GetIdentHash () != GetIdentHash ()) 452 { 453 LogPrint (eLogDebug, "Destination: New remote LeaseSet added"); 454 m_RemoteLeaseSets.insert_or_assign (key, leaseSet); 455 if (from) 456 from->SetDestination (key); 457 } 458 else 459 LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped"); 460 } 461 else 462 { 463 LogPrint (eLogError, "Destination: New remote LeaseSet failed"); 464 leaseSet = nullptr; 465 } 466 } 467 break; 468 } 469 case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5 470 { 471 auto it2 = m_LeaseSetRequests.find (key); 472 if (it2 != m_LeaseSetRequests.end ()) 473 { 474 request = it2->second; 475 m_LeaseSetRequests.erase (it2); 476 if (request->requestedBlindedKey) 477 { 478 auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, 479 request->requestedBlindedKey, shared_from_this (), 480 m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr, GetPreferredCryptoType ()); 481 if (ls2->IsValid () && !ls2->IsExpired ()) 482 { 483 leaseSet = ls2; 484 std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex); 485 m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key 486 m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup 487 } 488 else 489 LogPrint (eLogError, "Destination: New remote encrypted LeaseSet2 failed"); 490 } 491 else 492 { 493 // publishing verification doesn't have requestedBlindedKey 494 auto localLeaseSet = GetLeaseSetMt (); 495 if (localLeaseSet->GetStoreHash () == key) 496 { 497 auto ls = std::make_shared<i2p::data::LeaseSet2> (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, 498 localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false); 499 leaseSet = ls; 500 } 501 else 502 LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key"); 503 } 504 } 505 else 506 LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2"); 507 break; 508 } 509 default: 510 LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped"); 511 } 512 513 if (!request) 514 { 515 auto it1 = m_LeaseSetRequests.find (key); 516 if (it1 != m_LeaseSetRequests.end ()) 517 { 518 request = it1->second; 519 m_LeaseSetRequests.erase (it1); 520 } 521 } 522 if (request) 523 { 524 request->requestTimeoutTimer.cancel (); 525 request->Complete (leaseSet); 526 } 527 } 528 529 void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len) 530 { 531 i2p::data::IdentHash key (buf); 532 int num = buf[32]; // num 533 LogPrint (eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64 (), " num=", num); 534 auto it = m_LeaseSetRequests.find (key); 535 if (it != m_LeaseSetRequests.end ()) 536 { 537 auto request = it->second; 538 for (int i = 0; i < num; i++) 539 { 540 i2p::data::IdentHash peerHash (buf + 33 + i*32); 541 if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash)) 542 { 543 LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); 544 i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory 545 } 546 } 547 SendNextLeaseSetRequest (key, request); 548 } 549 else 550 LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); 551 } 552 553 void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key, 554 std::shared_ptr<LeaseSetRequest> request) 555 { 556 bool found = false; 557 if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) 558 { 559 auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded); 560 if (floodfill) 561 { 562 LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ()); 563 if (SendLeaseSetRequest (key, floodfill, request)) 564 found = true; 565 } 566 } 567 if (!found) 568 { 569 LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills"); 570 request->Complete (nullptr); 571 m_LeaseSetRequests.erase (key); 572 } 573 } 574 575 void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) 576 { 577 if (msgID == m_PublishReplyToken) 578 { 579 LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32()); 580 m_ExcludedFloodfills.clear (); 581 m_PublishReplyToken = 0; 582 // schedule verification 583 m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT + 584 GetRng ()() % PUBLISH_VERIFICATION_TIMEOUT_VARIANCE)); 585 m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, 586 shared_from_this (), std::placeholders::_1)); 587 } 588 else 589 i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); 590 } 591 592 void LeaseSetDestination::SetLeaseSetUpdated (bool post) 593 { 594 if (post) 595 boost::asio::post (m_Service, [s = shared_from_this ()]() { s->UpdateLeaseSet (); }); 596 else 597 UpdateLeaseSet (); 598 } 599 600 void LeaseSetDestination::Publish () 601 { 602 auto leaseSet = GetLeaseSetMt (); 603 if (!leaseSet || !m_Pool) 604 { 605 LogPrint (eLogError, "Destination: Can't publish non-existing LeaseSet"); 606 return; 607 } 608 if (m_PublishReplyToken) 609 { 610 LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending"); 611 return; 612 } 613 auto ts = i2p::util::GetSecondsSinceEpoch (); 614 if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL) 615 { 616 LogPrint (eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL, " seconds"); 617 m_PublishDelayTimer.cancel (); 618 m_PublishDelayTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_MIN_INTERVAL)); 619 m_PublishDelayTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishDelayTimer, 620 shared_from_this (), std::placeholders::_1)); 621 return; 622 } 623 auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); 624 if (!floodfill) 625 { 626 LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); 627 m_ExcludedFloodfills.clear (); 628 return; 629 } 630 auto outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); 631 auto inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); 632 if (!outbound || !inbound) 633 { 634 if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ()) 635 { 636 LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); 637 m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); 638 floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); 639 if (floodfill) 640 { 641 outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); 642 if (outbound) 643 { 644 inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); 645 if (!inbound) 646 LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels"); 647 } 648 else 649 LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); 650 } 651 else 652 LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); 653 } 654 else 655 LogPrint (eLogDebug, "Destination: No tunnels in pool"); 656 657 if (!floodfill || !outbound || !inbound) 658 { 659 // we can't publish now 660 m_ExcludedFloodfills.clear (); 661 m_PublishReplyToken = 1; // dummy non-zero value 662 // try again after a while 663 LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds"); 664 m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT)); 665 m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, 666 shared_from_this (), std::placeholders::_1)); 667 return; 668 } 669 } 670 m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); 671 LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); 672 RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); 673 auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound)); 674 auto s = shared_from_this (); 675 msg->onDrop = [s]() 676 { 677 boost::asio::post (s->GetService (), [s]() 678 { 679 s->m_PublishConfirmationTimer.cancel (); 680 s->HandlePublishConfirmationTimer (boost::system::error_code()); 681 }); 682 }; 683 m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT)); 684 m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, 685 shared_from_this (), std::placeholders::_1)); 686 outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg); 687 m_LastSubmissionTime = ts; 688 } 689 690 void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode) 691 { 692 if (ecode != boost::asio::error::operation_aborted) 693 { 694 if (m_PublishReplyToken) 695 { 696 LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds or failed. will try again"); 697 m_PublishReplyToken = 0; 698 Publish (); 699 } 700 } 701 } 702 703 void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode) 704 { 705 if (ecode != boost::asio::error::operation_aborted) 706 { 707 auto ls = GetLeaseSetMt (); 708 if (!ls) 709 { 710 LogPrint (eLogWarning, "Destination: Couldn't verify LeaseSet for ", GetIdentHash().ToBase32()); 711 return; 712 } 713 auto s = shared_from_this (); 714 RequestLeaseSet (ls->GetStoreHash (), 715 [s, ls](std::shared_ptr<const i2p::data::LeaseSet> leaseSet) 716 { 717 if (leaseSet) 718 { 719 if (*ls == *leaseSet) 720 { 721 // we got latest LeasetSet 722 LogPrint (eLogDebug, "Destination: Published LeaseSet verified for ", s->GetIdentHash().ToBase32()); 723 s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); 724 s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); 725 return; 726 } 727 else 728 LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", s->GetIdentHash().ToBase32()); 729 } 730 else 731 LogPrint (eLogWarning, "Destination: Couldn't find published LeaseSet for ", s->GetIdentHash().ToBase32()); 732 // we have to publish again 733 s->Publish (); 734 }); 735 } 736 } 737 738 void LeaseSetDestination::HandlePublishDelayTimer (const boost::system::error_code& ecode) 739 { 740 if (ecode != boost::asio::error::operation_aborted) 741 Publish (); 742 } 743 744 bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete) 745 { 746 if (!m_Pool || !IsReady ()) 747 { 748 if (requestComplete) 749 boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);}); 750 return false; 751 } 752 boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr)); 753 return true; 754 } 755 756 bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete) 757 { 758 if (!dest || !m_Pool || !IsReady ()) 759 { 760 if (requestComplete) 761 boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);}); 762 return false; 763 } 764 auto storeHash = dest->GetStoreHash (); 765 auto leaseSet = FindLeaseSet (storeHash); 766 if (leaseSet) 767 { 768 if (requestComplete) 769 boost::asio::post (m_Service, [requestComplete, leaseSet](void){requestComplete (leaseSet);}); 770 return true; 771 } 772 boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest)); 773 return true; 774 } 775 776 void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify) 777 { 778 auto s = shared_from_this (); 779 boost::asio::post (m_Service, [dest, notify, s](void) 780 { 781 auto it = s->m_LeaseSetRequests.find (dest); 782 if (it != s->m_LeaseSetRequests.end ()) 783 { 784 auto requestComplete = it->second; 785 s->m_LeaseSetRequests.erase (it); 786 if (notify && requestComplete) requestComplete->Complete (nullptr); 787 } 788 }); 789 } 790 791 void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify) 792 { 793 if (dest) 794 CancelDestinationRequest (dest->GetStoreHash (), notify); 795 } 796 797 void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey) 798 { 799 std::unordered_set<i2p::data::IdentHash> excluded; 800 auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); 801 if (floodfill) 802 { 803 auto request = std::make_shared<LeaseSetRequest> (m_Service); 804 request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2 805 if (requestComplete) 806 request->requestComplete.push_back (requestComplete); 807 auto ts = i2p::util::GetMillisecondsSinceEpoch (); 808 auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request)); 809 if (ret.second) // inserted 810 { 811 request->requestTime = ts; 812 if (!SendLeaseSetRequest (dest, floodfill, request)) 813 { 814 // try another 815 LogPrint (eLogWarning, "Destination: Couldn't send LeaseSet request to ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another"); 816 request->excluded.insert (floodfill->GetIdentHash ()); 817 floodfill = i2p::data::netdb.GetClosestFloodfill (dest, request->excluded); 818 if (!SendLeaseSetRequest (dest, floodfill, request)) 819 { 820 // request failed 821 LogPrint (eLogWarning, "Destination: LeaseSet request for ", dest.ToBase32 (), " was not sent"); 822 m_LeaseSetRequests.erase (ret.first); 823 if (requestComplete) requestComplete (nullptr); 824 } 825 } 826 } 827 else // duplicate 828 { 829 LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already"); 830 if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) 831 { 832 // something went wrong 833 m_LeaseSetRequests.erase (ret.first); 834 if (requestComplete) requestComplete (nullptr); 835 } 836 else if (requestComplete) 837 ret.first->second->requestComplete.push_back (requestComplete); 838 } 839 } 840 else 841 { 842 LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found"); 843 if (requestComplete) requestComplete (nullptr); 844 } 845 } 846 847 bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest, 848 std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request) 849 { 850 if (!request->replyTunnel || !request->replyTunnel->IsEstablished ()) 851 request->replyTunnel = m_Pool->GetNextInboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (false)); // outbound from floodfill 852 if (!request->replyTunnel) LogPrint (eLogWarning, "Destination: Can't send LeaseSet request, no compatible inbound tunnels found"); 853 if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ()) 854 request->outboundTunnel = m_Pool->GetNextOutboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (true)); // inbound from floodfill 855 if (!request->outboundTunnel) LogPrint (eLogWarning, "Destination: Can't send LeaseSet request, no compatible outbound tunnels found"); 856 857 if (request->replyTunnel && request->outboundTunnel) 858 { 859 request->excluded.insert (nextFloodfill->GetIdentHash ()); 860 request->requestTimeoutTimer.cancel (); 861 862 bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) && 863 nextFloodfill->GetVersion () >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46; 864 uint8_t replyKey[32], replyTag[32]; 865 RAND_bytes (replyKey, 32); // random session key 866 RAND_bytes (replyTag, isECIES ? 8 : 32); // random session tag 867 if (isECIES) 868 AddECIESx25519Key (replyKey, replyTag); 869 else 870 AddSessionKey (replyKey, replyTag); 871 872 auto msg = WrapMessageForRouter (nextFloodfill, 873 CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES)); 874 auto s = shared_from_this (); 875 msg->onDrop = [s, dest, request]() 876 { 877 boost::asio::post (s->GetService (), [s, dest, request]() 878 { 879 s->SendNextLeaseSetRequest (dest, request); 880 }); 881 }; 882 request->outboundTunnel->SendTunnelDataMsgs ( 883 { 884 i2p::tunnel::TunnelMessageBlock 885 { 886 i2p::tunnel::eDeliveryTypeRouter, 887 nextFloodfill->GetIdentHash (), 0, msg 888 } 889 }); 890 request->requestTimeoutTimer.expires_from_now (boost::posix_time::milliseconds(LEASESET_REQUEST_TIMEOUT)); 891 request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer, 892 shared_from_this (), std::placeholders::_1, dest)); 893 } 894 else 895 return false; 896 return true; 897 } 898 899 void LeaseSetDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest) 900 { 901 if (ecode != boost::asio::error::operation_aborted) 902 { 903 auto it = m_LeaseSetRequests.find (dest); 904 if (it != m_LeaseSetRequests.end ()) 905 { 906 bool done = false; 907 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); 908 if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) 909 { 910 auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded); 911 if (floodfill) 912 { 913 // reset tunnels, because one them might fail 914 it->second->outboundTunnel = nullptr; 915 it->second->replyTunnel = nullptr; 916 done = !SendLeaseSetRequest (dest, floodfill, it->second); 917 } 918 else 919 done = true; 920 } 921 else 922 { 923 LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); 924 done = true; 925 } 926 927 if (done) 928 { 929 auto requestComplete = it->second; 930 m_LeaseSetRequests.erase (it); 931 if (requestComplete) requestComplete->Complete (nullptr); 932 } 933 } 934 } 935 } 936 937 void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode) 938 { 939 if (ecode != boost::asio::error::operation_aborted) 940 { 941 CleanupExpiredTags (); 942 CleanupRemoteLeaseSets (); 943 CleanupDestination (); 944 m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT + 945 GetRng ()() % DESTINATION_CLEANUP_TIMEOUT_VARIANCE)); 946 m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, 947 shared_from_this (), std::placeholders::_1)); 948 } 949 } 950 951 void LeaseSetDestination::CleanupRemoteLeaseSets () 952 { 953 auto ts = i2p::util::GetMillisecondsSinceEpoch (); 954 std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex); 955 for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();) 956 { 957 if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired 958 { 959 LogPrint (eLogDebug, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); 960 it = m_RemoteLeaseSets.erase (it); 961 } 962 else 963 ++it; 964 } 965 } 966 967 ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys, 968 bool isPublic, const i2p::util::Mapping * params): 969 LeaseSetDestination (service, isPublic, params), 970 m_Keys (keys), m_PreferredCryptoType (0), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), 971 m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED), 972 m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED), 973 m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS), 974 m_StreamingMaxConnsPerMinute (DEFAULT_MAX_CONNS_PER_MINUTE), 975 m_StreamingMaxWindowSize (i2p::stream::MAX_WINDOW_SIZE), 976 m_StreamingMaxResends (i2p::stream::MAX_NUM_RESEND_ATTEMPTS), 977 m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_IsStreamingDontSign (DEFAULT_DONT_SIGN), 978 m_LastPort (0), m_DatagramDestination (nullptr), m_RefCounter (0), 979 m_LastPublishedTimestamp (0), m_ReadyChecker(service) 980 { 981 if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) 982 SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only 983 984 // extract encryption type params for LS2 985 std::set<i2p::data::CryptoKeyType> encryptionKeyTypes; 986 if (params) 987 { 988 auto encryptionTypesStr = (*params)[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE]; 989 if (!encryptionTypesStr.empty ()) 990 { 991 // comma-separated values 992 std::vector<std::string> values; 993 boost::split(values, encryptionTypesStr, boost::is_any_of(",")); 994 for (auto& it1: values) 995 { 996 try 997 { 998 i2p::data::CryptoKeyType cryptoType = std::stoi(it1); 999 #if !OPENSSL_PQ 1000 if (cryptoType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported 1001 #endif 1002 { 1003 if (!m_PreferredCryptoType && cryptoType) 1004 m_PreferredCryptoType = cryptoType; // first non-zero in the list 1005 encryptionKeyTypes.insert (cryptoType); 1006 } 1007 } 1008 catch (std::exception& ex) 1009 { 1010 LogPrint (eLogInfo, "Destination: Unexpected crypto type ", it1, ". ", ex.what ()); 1011 continue; 1012 } 1013 } 1014 } 1015 } 1016 // if no encryption type specified use 0,4 or 0,4,6 if post quantum 1017 if (encryptionKeyTypes.empty ()) 1018 { 1019 encryptionKeyTypes.insert ( { GetIdentity ()->GetCryptoKeyType (), 1020 #if OPENSSL_PQ 1021 i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD, 1022 #endif 1023 i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD }); 1024 #if OPENSSL_PQ 1025 m_PreferredCryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD; 1026 #else 1027 m_PreferredCryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; 1028 #endif 1029 } 1030 1031 for (auto& it: encryptionKeyTypes) 1032 { 1033 auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (it); 1034 if (IsPublic ()) 1035 PersistTemporaryKeys (encryptionKey); 1036 else 1037 encryptionKey->GenerateKeys (); 1038 encryptionKey->CreateDecryptor (); 1039 if (it > i2p::data::CRYPTO_KEY_TYPE_ELGAMAL && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) 1040 SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Only DSA can use LeaseSet1 1041 m_EncryptionKeys.emplace (it, encryptionKey); 1042 } 1043 1044 if (IsPublic ()) 1045 LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); 1046 1047 try 1048 { 1049 if (params) 1050 { 1051 // extract streaming params 1052 params->Get (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, m_StreamingAckDelay); 1053 params->Get (I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, m_StreamingOutboundSpeed); 1054 params->Get (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, m_StreamingInboundSpeed); 1055 params->Get (I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS, m_StreamingMaxConcurrentStreams); 1056 params->Get (I2CP_PARAM_STREAMING_MAX_CONNS_PER_MINUTE, m_StreamingMaxConnsPerMinute); 1057 if (params->Get (I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE, m_StreamingMaxWindowSize) && 1058 (m_StreamingMaxWindowSize < i2p::stream::MIN_WINDOW_SIZE)) 1059 m_StreamingMaxWindowSize = i2p::stream::MIN_WINDOW_SIZE; 1060 params->Get (I2CP_PARAM_STREAMING_ANSWER_PINGS, m_IsStreamingAnswerPings); 1061 params->Get (I2CP_PARAM_STREAMING_DONT_SIGN, m_IsStreamingDontSign); 1062 params->Get (I2CP_PARAM_STREAMING_MAX_RESENDS, m_StreamingMaxResends); 1063 1064 if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) 1065 { 1066 // authentication for encrypted LeaseSet 1067 auto authType = GetAuthType (); 1068 if (authType > 0) 1069 { 1070 m_AuthKeys = std::make_shared<std::vector<i2p::data::AuthPublicKey> >(); 1071 if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH) 1072 ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params); 1073 else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK) 1074 ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params); 1075 else 1076 LogPrint (eLogError, "Destination: Unexpected auth type: ", authType); 1077 if (m_AuthKeys->size ()) 1078 LogPrint (eLogInfo, "Destination: ", m_AuthKeys->size (), " auth keys read"); 1079 else 1080 { 1081 LogPrint (eLogCritical, "Destination: No auth keys read for auth type: ", authType); 1082 m_AuthKeys = nullptr; 1083 } 1084 } 1085 } 1086 } 1087 } 1088 catch (std::exception & ex) 1089 { 1090 LogPrint(eLogCritical, "Destination: Unable to parse parameters for destination: ", ex.what()); 1091 } 1092 } 1093 1094 ClientDestination::~ClientDestination () 1095 { 1096 } 1097 1098 void ClientDestination::Start () 1099 { 1100 LeaseSetDestination::Start (); 1101 m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO: 1102 m_StreamingDestination->Start (); 1103 for (auto& it: m_StreamingDestinationsByPorts) 1104 it.second->Start (); 1105 } 1106 1107 void ClientDestination::Stop () 1108 { 1109 LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p"); 1110 m_ReadyChecker.cancel(); 1111 LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination"); 1112 m_StreamingDestination->Stop (); 1113 //m_StreamingDestination->SetOwner (nullptr); 1114 m_StreamingDestination = nullptr; 1115 1116 LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination by ports"); 1117 for (auto& it: m_StreamingDestinationsByPorts) 1118 { 1119 it.second->Stop (); 1120 //it.second->SetOwner (nullptr); 1121 } 1122 m_StreamingDestinationsByPorts.clear (); 1123 m_LastStreamingDestination = nullptr; 1124 1125 if (m_DatagramDestination) 1126 { 1127 LogPrint(eLogDebug, "Destination: -> Stopping Datagram Destination"); 1128 delete m_DatagramDestination; 1129 m_DatagramDestination = nullptr; 1130 } 1131 LeaseSetDestination::Stop (); 1132 LogPrint(eLogDebug, "Destination: -> Stopping done"); 1133 } 1134 1135 void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len, 1136 i2p::garlic::ECIESX25519AEADRatchetSession * from) 1137 { 1138 uint32_t length = bufbe32toh (buf); 1139 if(length > len - 4) 1140 { 1141 LogPrint(eLogError, "Destination: Data message length ", length, " exceeds buffer length ", len); 1142 return; 1143 } 1144 buf += 4; 1145 // we assume I2CP payload 1146 uint16_t fromPort = bufbe16toh (buf + 4), // source 1147 toPort = bufbe16toh (buf + 6); // destination 1148 switch (buf[9]) 1149 { 1150 case PROTOCOL_TYPE_STREAMING: 1151 { 1152 // streaming protocol 1153 if (toPort != m_LastPort || !m_LastStreamingDestination) 1154 { 1155 m_LastStreamingDestination = GetStreamingDestination (toPort); 1156 if (!m_LastStreamingDestination) 1157 m_LastStreamingDestination = m_StreamingDestination; // if no destination on port use default 1158 m_LastPort = toPort; 1159 } 1160 if (m_LastStreamingDestination) 1161 m_LastStreamingDestination->HandleDataMessagePayload (buf, length, from); 1162 else 1163 LogPrint (eLogError, "Destination: Missing streaming destination"); 1164 } 1165 break; 1166 case PROTOCOL_TYPE_DATAGRAM: 1167 case PROTOCOL_TYPE_RAW: 1168 case PROTOCOL_TYPE_DATAGRAM2: 1169 case PROTOCOL_TYPE_DATAGRAM3: 1170 // datagram protocol 1171 if (m_DatagramDestination) 1172 m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, buf[9], from); 1173 else 1174 LogPrint (eLogError, "Destination: Missing datagram destination"); 1175 break; 1176 default: 1177 LogPrint (eLogError, "Destination: Data: Unexpected protocol ", buf[9]); 1178 } 1179 } 1180 1181 void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, uint16_t port) 1182 { 1183 if (!streamRequestComplete) 1184 { 1185 LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream"); 1186 return; 1187 } 1188 auto leaseSet = FindLeaseSet (dest); 1189 if (leaseSet) 1190 { 1191 auto stream = CreateStream (leaseSet, port); 1192 boost::asio::post (GetService (), [streamRequestComplete, stream]() 1193 { 1194 streamRequestComplete(stream); 1195 }); 1196 } 1197 else 1198 { 1199 auto s = GetSharedFromThis (); 1200 RequestDestination (dest, 1201 [s, streamRequestComplete, port](std::shared_ptr<const i2p::data::LeaseSet> ls) 1202 { 1203 if (ls) 1204 streamRequestComplete(s->CreateStream (ls, port)); 1205 else 1206 streamRequestComplete (nullptr); 1207 }); 1208 } 1209 } 1210 1211 void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, uint16_t port) 1212 { 1213 if (!streamRequestComplete) 1214 { 1215 LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream"); 1216 return; 1217 } 1218 auto s = GetSharedFromThis (); 1219 RequestDestinationWithEncryptedLeaseSet (dest, 1220 [s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls) 1221 { 1222 if (ls) 1223 streamRequestComplete(s->CreateStream (ls, port)); 1224 else 1225 streamRequestComplete (nullptr); 1226 }); 1227 } 1228 1229 template<typename Dest> 1230 std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, uint16_t port) 1231 { 1232 volatile bool done = false; 1233 std::shared_ptr<i2p::stream::Stream> stream; 1234 std::condition_variable streamRequestComplete; 1235 std::mutex streamRequestCompleteMutex; 1236 CreateStream ( 1237 [&done, &streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s) 1238 { 1239 stream = s; 1240 std::unique_lock<std::mutex> l(streamRequestCompleteMutex); 1241 streamRequestComplete.notify_all (); 1242 done = true; 1243 }, 1244 dest, port); 1245 while (!done) 1246 { 1247 std::unique_lock<std::mutex> l(streamRequestCompleteMutex); 1248 if (!done) 1249 streamRequestComplete.wait (l); 1250 } 1251 return stream; 1252 } 1253 1254 std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::IdentHash& dest, uint16_t port) 1255 { 1256 return CreateStreamSync (dest, port); 1257 } 1258 1259 std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, uint16_t port) 1260 { 1261 return CreateStreamSync (dest, port); 1262 } 1263 1264 std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, uint16_t port) 1265 { 1266 if (m_StreamingDestination) 1267 return m_StreamingDestination->CreateNewOutgoingStream (remote, port); 1268 else 1269 return nullptr; 1270 } 1271 1272 void ClientDestination::SendPing (const i2p::data::IdentHash& to) 1273 { 1274 if (m_StreamingDestination) 1275 { 1276 auto leaseSet = FindLeaseSet (to); 1277 if (leaseSet) 1278 m_StreamingDestination->SendPing (leaseSet); 1279 else 1280 { 1281 auto s = m_StreamingDestination; 1282 RequestDestination (to, 1283 [s](std::shared_ptr<const i2p::data::LeaseSet> ls) 1284 { 1285 if (ls) s->SendPing (ls); 1286 }); 1287 } 1288 } 1289 } 1290 1291 void ClientDestination::SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to) 1292 { 1293 auto s = m_StreamingDestination; 1294 RequestDestinationWithEncryptedLeaseSet (to, 1295 [s](std::shared_ptr<const i2p::data::LeaseSet> ls) 1296 { 1297 if (ls) s->SendPing (ls); 1298 }); 1299 } 1300 1301 std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (uint16_t port) const 1302 { 1303 if (port) 1304 { 1305 auto it = m_StreamingDestinationsByPorts.find (port); 1306 if (it != m_StreamingDestinationsByPorts.end ()) 1307 return it->second; 1308 } 1309 else // if port is zero, use default destination 1310 return m_StreamingDestination; 1311 return nullptr; 1312 } 1313 1314 void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor) 1315 { 1316 if (m_StreamingDestination) 1317 m_StreamingDestination->SetAcceptor (acceptor); 1318 } 1319 1320 void ClientDestination::StopAcceptingStreams () 1321 { 1322 if (m_StreamingDestination) 1323 m_StreamingDestination->ResetAcceptor (); 1324 } 1325 1326 bool ClientDestination::IsAcceptingStreams () const 1327 { 1328 if (m_StreamingDestination) 1329 return m_StreamingDestination->IsAcceptorSet (); 1330 return false; 1331 } 1332 1333 void ClientDestination::AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor) 1334 { 1335 if (m_StreamingDestination) 1336 m_StreamingDestination->AcceptOnce (acceptor); 1337 } 1338 1339 std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (uint16_t port, bool gzip) 1340 { 1341 auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip); 1342 if (port) 1343 m_StreamingDestinationsByPorts[port] = dest; 1344 else // update default 1345 m_StreamingDestination = dest; 1346 return dest; 1347 } 1348 1349 std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::RemoveStreamingDestination (uint16_t port) 1350 { 1351 if (port) 1352 { 1353 auto it = m_StreamingDestinationsByPorts.find (port); 1354 if (it != m_StreamingDestinationsByPorts.end ()) 1355 { 1356 auto ret = it->second; 1357 m_StreamingDestinationsByPorts.erase (it); 1358 return ret; 1359 } 1360 } 1361 return nullptr; 1362 } 1363 1364 i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip, 1365 i2p::datagram::DatagramVersion version) 1366 { 1367 if (!m_DatagramDestination) 1368 { 1369 if (!GetNumRatchetInboundTags ()) 1370 SetNumRatchetInboundTags (i2p::garlic::ECIESX25519_MAX_NUM_GENERATED_TAGS); // set max tags if not specified 1371 m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip, version); 1372 } 1373 return m_DatagramDestination; 1374 } 1375 1376 std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const 1377 { 1378 std::vector<std::shared_ptr<const i2p::stream::Stream> > ret; 1379 if (m_StreamingDestination) 1380 { 1381 for (auto& it: m_StreamingDestination->GetStreams ()) 1382 ret.push_back (it.second); 1383 } 1384 for (auto& it: m_StreamingDestinationsByPorts) 1385 for (auto& it1: it.second->GetStreams ()) 1386 ret.push_back (it1.second); 1387 return ret; 1388 } 1389 1390 void ClientDestination::PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys) 1391 { 1392 if (!keys) return; 1393 std::string ident = GetIdentHash().ToBase32(); 1394 std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat"); 1395 std::ifstream f(path, std::ifstream::binary); 1396 if (f) 1397 { 1398 size_t len = 0; 1399 if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) 1400 len = 512; 1401 else if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) 1402 { 1403 f.seekg (0, std::ios::end); 1404 len = f.tellg(); 1405 f.seekg (0, std::ios::beg); 1406 } 1407 1408 if (len == 512) 1409 { 1410 char pub[256], priv[256]; 1411 f.read (pub, 256); 1412 memcpy (keys->pub.data(), pub, keys->pub.size()); 1413 f.read (priv, 256); 1414 memcpy (keys->priv.data (), priv, keys->priv.size ()); 1415 } 1416 else 1417 { 1418 f.read ((char *)keys->pub.data(), keys->pub.size()); 1419 f.read ((char *)keys->priv.data(), keys->priv.size()); 1420 } 1421 if (f) 1422 return; 1423 else 1424 LogPrint(eLogWarning, "Destination: Can't read keys from ", path); 1425 } 1426 1427 LogPrint (eLogInfo, "Destination: Creating new temporary keys of type ", keys->keyType, " for address ", ident, ".b32.i2p"); 1428 memset (keys->priv.data (), 0, keys->priv.size ()); 1429 memset (keys->pub.data (), 0, keys->pub.size ()); 1430 keys->GenerateKeys (); 1431 1432 std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); 1433 if (f1) 1434 { 1435 f1.write ((char *)keys->pub.data (), keys->pub.size ()); 1436 f1.write ((char *)keys->priv.data (), keys->priv.size ()); 1437 } 1438 if (!f1) 1439 LogPrint(eLogError, "Destination: Can't save keys to ", path); 1440 } 1441 1442 void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) 1443 { 1444 std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet; 1445 if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) 1446 { 1447 auto it = m_EncryptionKeys.find (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); 1448 if (it != m_EncryptionKeys.end ()) 1449 { 1450 leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), it->second->pub.data (), tunnels); 1451 // sign 1452 Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); 1453 } 1454 else 1455 LogPrint (eLogError, "Destinations: Wrong encryption key type for LeaseSet type 1"); 1456 } 1457 else 1458 { 1459 // standard LS2 (type 3) first 1460 if (m_EncryptionKeys.empty ()) 1461 { 1462 LogPrint (eLogError, "Destinations: No encryption keys"); 1463 return; 1464 } 1465 1466 i2p::data::LocalLeaseSet2::EncryptionKeys keySections; 1467 std::shared_ptr<const i2p::crypto::LocalEncryptionKey> preferredSection; 1468 if (m_EncryptionKeys.size () == 1) 1469 preferredSection = m_EncryptionKeys.begin ()->second; // only key 1470 else 1471 { 1472 for (const auto& it: m_EncryptionKeys) 1473 if (it.first == m_PreferredCryptoType) 1474 preferredSection = it.second; 1475 else 1476 keySections.push_back (it.second); 1477 } 1478 if (preferredSection) 1479 keySections.push_front (preferredSection); // make preferred first 1480 1481 auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch (); 1482 if (publishedTimestamp <= m_LastPublishedTimestamp) 1483 { 1484 LogPrint (eLogDebug, "Destination: LeaseSet update at the same second"); 1485 publishedTimestamp++; // force newer timestamp 1486 } 1487 bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; 1488 auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, 1489 m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted); 1490 if (isPublishedEncrypted) // encrypt if type 5 1491 ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys); 1492 leaseSet = ls2; 1493 m_LastPublishedTimestamp = publishedTimestamp; 1494 } 1495 SetLeaseSet (leaseSet); 1496 } 1497 1498 void ClientDestination::CleanupDestination () 1499 { 1500 if (m_DatagramDestination) m_DatagramDestination->CleanUp (); 1501 } 1502 1503 bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const 1504 { 1505 std::shared_ptr<i2p::crypto::LocalEncryptionKey> encryptionKey; 1506 if (!m_EncryptionKeys.empty ()) 1507 { 1508 if (m_EncryptionKeys.rbegin ()->first == preferredCrypto) 1509 encryptionKey = m_EncryptionKeys.rbegin ()->second; 1510 else 1511 { 1512 auto it = m_EncryptionKeys.find (preferredCrypto); 1513 if (it != m_EncryptionKeys.end ()) 1514 encryptionKey = it->second; 1515 } 1516 if (!encryptionKey) 1517 encryptionKey = m_EncryptionKeys.rbegin ()->second; 1518 } 1519 if (encryptionKey) 1520 return encryptionKey->decryptor->Decrypt (encrypted, data); 1521 else 1522 LogPrint (eLogError, "Destinations: Decryptor is not set"); 1523 return false; 1524 } 1525 1526 bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const 1527 { 1528 #if __cplusplus >= 202002L // C++20 1529 return m_EncryptionKeys.contains (keyType); 1530 #else 1531 return m_EncryptionKeys.count (keyType) > 0; 1532 #endif 1533 } 1534 1535 i2p::data::CryptoKeyType ClientDestination::GetRatchetsHighestCryptoType () const 1536 { 1537 if (m_EncryptionKeys.empty ()) return 0; 1538 auto cryptoType = m_EncryptionKeys.rbegin ()->first; 1539 return cryptoType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? cryptoType : 0; 1540 } 1541 1542 const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const 1543 { 1544 auto it = m_EncryptionKeys.find (keyType); 1545 if (it != m_EncryptionKeys.end ()) 1546 return it->second->pub.data (); 1547 return nullptr; 1548 } 1549 1550 void ClientDestination::ReadAuthKey (const std::string& group, const i2p::util::Mapping * params) 1551 { 1552 for (const auto& it: params->GetOptions ()) 1553 if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group)) 1554 { 1555 auto pos = it.second.find (':'); 1556 if (pos != std::string::npos) 1557 { 1558 i2p::data::AuthPublicKey pubKey; 1559 if (pubKey.FromBase64 (it.second.substr (pos+1))) 1560 m_AuthKeys->push_back (pubKey); 1561 else 1562 LogPrint (eLogCritical, "Destination: Unexpected auth key: ", it.second.substr (pos+1)); 1563 } 1564 } 1565 } 1566 1567 bool ClientDestination::DeleteStream (uint32_t recvStreamID) 1568 { 1569 if (m_StreamingDestination->DeleteStream (recvStreamID)) 1570 return true; 1571 for (auto it: m_StreamingDestinationsByPorts) 1572 if (it.second->DeleteStream (recvStreamID)) 1573 return true; 1574 return false; 1575 } 1576 1577 RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, 1578 bool isPublic, const i2p::util::Mapping * params): 1579 RunnableService ("Destination"), ClientDestination (GetIOService (), keys, isPublic, params) 1580 { 1581 if (!GetNickname ().empty ()) 1582 RunnableService::SetName (GetNickname ()); 1583 } 1584 1585 RunnableClientDestination::~RunnableClientDestination () 1586 { 1587 if (IsRunning ()) 1588 Stop (); 1589 } 1590 1591 void RunnableClientDestination::Start () 1592 { 1593 if (!IsRunning ()) 1594 { 1595 ClientDestination::Start (); 1596 StartIOService (); 1597 } 1598 } 1599 1600 void RunnableClientDestination::Stop () 1601 { 1602 if (IsRunning ()) 1603 { 1604 ClientDestination::Stop (); 1605 StopIOService (); 1606 } 1607 } 1608 1609 } 1610 }