NetDb.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 <fstream> 11 #include <vector> 12 #include <map> 13 #include <boost/asio.hpp> 14 #include <stdexcept> 15 16 #include "I2PEndian.h" 17 #include "Base.h" 18 #include "Crypto.h" 19 #include "Log.h" 20 #include "Timestamp.h" 21 #include "I2NPProtocol.h" 22 #include "Tunnel.h" 23 #include "Transports.h" 24 #include "NTCP2.h" 25 #include "RouterContext.h" 26 #include "Garlic.h" 27 #include "ECIESX25519AEADRatchetSession.h" 28 #include "Config.h" 29 #include "NetDb.hpp" 30 #include "util.h" 31 32 using namespace i2p::transport; 33 34 namespace i2p 35 { 36 namespace data 37 { 38 NetDb netdb; 39 40 NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), 41 m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true), 42 m_LastExploratorySelectionUpdateTime (0), m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL) 43 { 44 } 45 46 NetDb::~NetDb () 47 { 48 Stop (); 49 delete m_Reseeder; 50 } 51 52 void NetDb::Start () 53 { 54 m_Storage.SetPlace(i2p::fs::GetDataDir()); 55 m_Storage.Init(i2p::data::GetBase64SubstitutionTable(), 64); 56 InitProfilesStorage (); 57 m_Families.LoadCertificates (); 58 Load (); 59 60 if (!m_Requests) 61 { 62 m_Requests = std::make_shared<NetDbRequests>(); 63 m_Requests->Start (); 64 } 65 66 uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold); 67 if (m_RouterInfos.size () < threshold || m_Floodfills.GetSize () < NETDB_MIN_FLOODFILLS) // reseed if # of router less than threshold or too few floodfiils 68 { 69 Reseed (); 70 } 71 else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false, false)) 72 Reseed (); // we don't have a router we can connect to. Trying to reseed 73 74 auto it = m_RouterInfos.find (i2p::context.GetIdentHash ()); 75 if (it != m_RouterInfos.end ()) 76 { 77 // remove own router 78 m_Floodfills.Remove (it->second->GetIdentHash ()); 79 m_RouterInfos.erase (it); 80 } 81 // insert own router 82 m_RouterInfos.emplace (i2p::context.GetIdentHash (), i2p::context.GetSharedRouterInfo ()); 83 if (i2p::context.IsFloodfill ()) 84 m_Floodfills.Insert (i2p::context.GetSharedRouterInfo ()); 85 86 i2p::config::GetOption("persist.profiles", m_PersistProfiles); 87 88 m_IsRunning = true; 89 m_Thread = new std::thread (std::bind (&NetDb::Run, this)); 90 } 91 92 void NetDb::Stop () 93 { 94 if (m_Requests) 95 m_Requests->Stop (); 96 if (m_IsRunning) 97 { 98 if (m_PersistProfiles) 99 SaveProfiles (); 100 DeleteObsoleteProfiles (); 101 m_RouterInfos.clear (); 102 m_Floodfills.Clear (); 103 if (m_Thread) 104 { 105 m_IsRunning = false; 106 m_Queue.WakeUp (); 107 m_Thread->join (); 108 delete m_Thread; 109 m_Thread = 0; 110 } 111 m_LeaseSets.clear(); 112 } 113 m_Requests = nullptr; 114 } 115 116 void NetDb::Run () 117 { 118 i2p::util::SetThreadName("NetDB"); 119 120 uint64_t lastManage = 0; 121 uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), 122 lastObsoleteProfilesCleanup = lastProfilesCleanup, lastApplyingProfileUpdates = lastProfilesCleanup; 123 int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0, applyingProfileUpdatesVariance = 0; 124 125 std::list<std::shared_ptr<const I2NPMessage> > msgs; 126 while (m_IsRunning) 127 { 128 try 129 { 130 if (m_Queue.Wait (1,0)) // 1 sec 131 { 132 m_Queue.GetWholeQueue (msgs); 133 while (!msgs.empty ()) 134 { 135 auto msg = msgs.front (); msgs.pop_front (); 136 if (!msg) continue; 137 LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ()); 138 switch (msg->GetTypeID ()) 139 { 140 case eI2NPDatabaseStore: 141 HandleDatabaseStoreMsg (msg); 142 break; 143 case eI2NPDatabaseLookup: 144 HandleDatabaseLookupMsg (msg); 145 break; 146 default: // WTF? 147 LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ()); 148 //i2p::HandleI2NPMessage (msg); 149 } 150 } 151 } 152 if (!m_IsRunning) break; 153 if (!i2p::transport::transports.IsOnline () || !i2p::transport::transports.IsRunning ()) 154 continue; // don't manage netdb when offline or transports are not running 155 156 uint64_t mts = i2p::util::GetMonotonicMilliseconds (); 157 if (mts >= lastManage + 60000) // manage routers and leasesets every minute 158 { 159 if (lastManage) 160 { 161 ManageRouterInfos (); 162 ManageLeaseSets (); 163 } 164 lastManage = mts; 165 } 166 167 if (mts >= lastProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance)*1000) 168 { 169 m_RouterProfilesPool.CleanUpMt (); 170 if (m_PersistProfiles) 171 { 172 bool isSaving = m_SavingProfiles.valid (); 173 if (isSaving && m_SavingProfiles.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active? 174 { 175 m_SavingProfiles.get (); 176 isSaving = false; 177 } 178 if (!isSaving) 179 m_SavingProfiles = PersistProfiles (); 180 else 181 LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk"); 182 } 183 lastProfilesCleanup = mts; 184 profilesCleanupVariance = m_Rng () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE; 185 } 186 187 if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000) 188 { 189 bool isDeleting = m_DeletingProfiles.valid (); 190 if (isDeleting && m_DeletingProfiles.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active? 191 { 192 m_DeletingProfiles.get (); 193 isDeleting = false; 194 } 195 if (!isDeleting) 196 m_DeletingProfiles = DeleteObsoleteProfiles (); 197 else 198 LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk"); 199 lastObsoleteProfilesCleanup = mts; 200 obsoleteProfilesCleanVariance = m_Rng () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE; 201 } 202 if (mts >= lastApplyingProfileUpdates + i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT + applyingProfileUpdatesVariance) 203 { 204 bool isApplying = m_ApplyingProfileUpdates.valid (); 205 if (isApplying && m_ApplyingProfileUpdates.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active? 206 { 207 m_ApplyingProfileUpdates.get (); 208 isApplying = false; 209 } 210 if (!isApplying) 211 m_ApplyingProfileUpdates = i2p::data::FlushPostponedRouterProfileUpdates (); 212 lastApplyingProfileUpdates = mts; 213 applyingProfileUpdatesVariance = m_Rng () % i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE; 214 } 215 } 216 catch (std::exception& ex) 217 { 218 LogPrint (eLogError, "NetDb: Runtime exception: ", ex.what ()); 219 } 220 } 221 } 222 223 std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len) 224 { 225 bool updated; 226 return AddRouterInfo (buf, len, updated); 227 } 228 229 std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated) 230 { 231 IdentityEx identity; 232 if (identity.FromBuffer (buf, len)) 233 return AddRouterInfo (identity.GetIdentHash (), buf, len, updated); 234 updated = false; 235 return nullptr; 236 } 237 238 bool NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len) 239 { 240 bool updated; 241 if (!AddRouterInfo (ident, buf, len, updated)) 242 updated = false; 243 return updated; 244 } 245 246 std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated) 247 { 248 updated = true; 249 auto r = FindRouter (ident); 250 if (r) 251 { 252 if (r->IsNewer (buf, len)) 253 { 254 bool wasFloodfill = r->IsFloodfill (); 255 { 256 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 257 if (!r->Update (buf, len)) 258 { 259 updated = false; 260 m_Requests->RequestComplete (ident, r); 261 return r; 262 } 263 if (r->IsUnreachable () || 264 i2p::util::GetMillisecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ()) 265 { 266 // delete router as invalid or from future after update 267 m_RouterInfos.erase (ident); 268 if (wasFloodfill) 269 { 270 std::lock_guard<std::mutex> l(m_FloodfillsMutex); 271 m_Floodfills.Remove (r->GetIdentHash ()); 272 } 273 m_Requests->RequestComplete (ident, nullptr); 274 return nullptr; 275 } 276 } 277 if (CheckLogLevel (eLogInfo)) 278 LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64()); 279 if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated 280 { 281 if (CheckLogLevel (eLogDebug)) 282 LogPrint (eLogDebug, "NetDb: RouterInfo floodfill status updated: ", ident.ToBase64()); 283 std::lock_guard<std::mutex> l(m_FloodfillsMutex); 284 if (wasFloodfill) 285 m_Floodfills.Remove (r->GetIdentHash ()); 286 else if (r->IsEligibleFloodfill ()) 287 { 288 if (m_Floodfills.GetSize () < NETDB_NUM_FLOODFILLS_THRESHOLD || r->GetProfile ()->IsReal ()) 289 m_Floodfills.Insert (r); 290 else 291 r->ResetFloodfill (); 292 } 293 } 294 } 295 else 296 { 297 r->CancelBufferToDelete (); // since an update received 298 if (CheckLogLevel (eLogDebug)) 299 LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); 300 updated = false; 301 } 302 } 303 else 304 { 305 r = std::make_shared<RouterInfo> (buf, len); 306 bool isValid = !r->IsUnreachable () && r->HasValidAddresses () && (!r->IsFloodfill () || !r->GetProfile ()->IsUnreachable ()); 307 if (isValid) 308 { 309 auto mts = i2p::util::GetMillisecondsSinceEpoch (); 310 isValid = mts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp () && // from future 311 (mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL || // too old 312 context.GetUptime () < NETDB_CHECK_FOR_EXPIRATION_UPTIME/10); // enough uptime 313 } 314 if (isValid) 315 { 316 bool inserted = false; 317 { 318 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 319 inserted = m_RouterInfos.insert ({r->GetIdentHash (), r}).second; 320 } 321 if (inserted) 322 { 323 if (CheckLogLevel (eLogInfo)) 324 LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); 325 if (r->IsFloodfill () && r->IsEligibleFloodfill ()) 326 { 327 if (m_Floodfills.GetSize () < NETDB_NUM_FLOODFILLS_THRESHOLD || 328 r->GetProfile ()->IsReal ()) // don't insert floodfill until it's known real if we have enough 329 { 330 std::lock_guard<std::mutex> l(m_FloodfillsMutex); 331 m_Floodfills.Insert (r); 332 } 333 else 334 r->ResetFloodfill (); 335 } 336 } 337 else 338 { 339 LogPrint (eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64()); 340 updated = false; 341 } 342 } 343 else 344 updated = false; 345 } 346 // take care about requested destination 347 m_Requests->RequestComplete (ident, r); 348 return r; 349 } 350 351 bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len) 352 { 353 std::lock_guard<std::mutex> lock(m_LeaseSetsMutex); 354 bool updated = false; 355 auto it = m_LeaseSets.find(ident); 356 if (it != m_LeaseSets.end () && it->second->GetStoreType () == i2p::data::NETDB_STORE_TYPE_LEASESET) 357 { 358 // we update only is existing LeaseSet is not LeaseSet2 359 uint64_t expires; 360 if(LeaseSetBufferValidate(buf, len, expires)) 361 { 362 if(it->second->GetExpirationTime() < expires) 363 { 364 it->second->Update (buf, len, nullptr, false); // signature is verified already 365 if (CheckLogLevel (eLogInfo)) 366 LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32()); 367 updated = true; 368 } 369 else if (CheckLogLevel (eLogDebug)) 370 LogPrint(eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32()); 371 } 372 else 373 LogPrint(eLogError, "NetDb: LeaseSet is invalid: ", ident.ToBase32()); 374 } 375 else 376 { 377 auto leaseSet = std::make_shared<LeaseSet> (buf, len, false); // we don't need leases in netdb 378 if (leaseSet->IsValid ()) 379 { 380 if (CheckLogLevel (eLogInfo)) 381 LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32()); 382 m_LeaseSets[ident] = leaseSet; 383 updated = true; 384 } 385 else 386 LogPrint (eLogError, "NetDb: New LeaseSet validation failed: ", ident.ToBase32()); 387 } 388 return updated; 389 } 390 391 bool NetDb::AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType) 392 { 393 auto leaseSet = std::make_shared<LeaseSet2> (storeType, buf, len, false); // we don't need leases in netdb 394 if (leaseSet->IsValid ()) 395 { 396 std::lock_guard<std::mutex> lock(m_LeaseSetsMutex); 397 auto it = m_LeaseSets.find(ident); 398 if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType || 399 leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ()) 400 { 401 if (leaseSet->IsPublic () && !leaseSet->IsExpired ()) 402 { 403 // TODO: implement actual update 404 if (CheckLogLevel (eLogInfo)) 405 LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32()); 406 m_LeaseSets[ident] = leaseSet; 407 return true; 408 } 409 else 410 { 411 LogPrint (eLogWarning, "NetDb: Unpublished or expired or future LeaseSet2 received: ", ident.ToBase32()); 412 m_LeaseSets.erase (ident); 413 } 414 } 415 } 416 else 417 LogPrint (eLogError, "NetDb: New LeaseSet2 validation failed: ", ident.ToBase32()); 418 return false; 419 } 420 421 std::shared_ptr<RouterInfo> NetDb::FindRouter (const IdentHash& ident) const 422 { 423 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 424 auto it = m_RouterInfos.find (ident); 425 if (it != m_RouterInfos.end ()) 426 return it->second; 427 else 428 return nullptr; 429 } 430 431 std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const 432 { 433 std::lock_guard<std::mutex> lock(m_LeaseSetsMutex); 434 auto it = m_LeaseSets.find (destination); 435 if (it != m_LeaseSets.end ()) 436 return it->second; 437 else 438 return nullptr; 439 } 440 441 std::shared_ptr<RouterProfile> NetDb::FindRouterProfile (const IdentHash& ident) const 442 { 443 if (!m_PersistProfiles) 444 return nullptr; 445 446 auto router = FindRouter (ident); 447 return router ? router->GetProfile () : nullptr; 448 } 449 450 void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable) 451 { 452 auto r = FindRouter (ident); 453 if (r) 454 { 455 r->SetUnreachable (unreachable); 456 auto profile = r->GetProfile (); 457 if (profile) 458 { 459 profile->Unreachable (unreachable); 460 if (!unreachable && r->IsDeclaredFloodfill () && !r->IsFloodfill () && 461 r->IsEligibleFloodfill () && profile->IsReal ()) 462 { 463 // enable previously disabled floodfill 464 r->SetFloodfill (); 465 std::lock_guard<std::mutex> l(m_FloodfillsMutex); 466 m_Floodfills.Insert (r); 467 } 468 } 469 } 470 } 471 472 void NetDb::ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports) 473 { 474 auto r = FindRouter (ident); 475 if (r) 476 { 477 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 478 r->ExcludeReachableTransports (transports); 479 } 480 } 481 482 void NetDb::Reseed () 483 { 484 if (!m_Reseeder) 485 { 486 m_Reseeder = new Reseeder (); 487 m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification 488 } 489 490 m_Reseeder->Bootstrap (); 491 } 492 493 void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) 494 { 495 LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64()); 496 std::list<std::shared_ptr<i2p::I2NPMessage> > requests; 497 498 i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash(); 499 i2p::data::IdentHash ih = ri.GetIdentHash(); 500 i2p::data::IdentHash randomIdent; 501 502 // make floodfill lookups 503 while(numFloodfills > 0) { 504 randomIdent.Randomize(); 505 auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, false); 506 requests.push_back(msg); 507 numFloodfills --; 508 } 509 510 // make regular router lookups 511 while(numRouters > 0) { 512 randomIdent.Randomize(); 513 auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, true); 514 requests.push_back(msg); 515 numRouters --; 516 } 517 518 // send them off 519 i2p::transport::transports.SendMessages(ih, std::move (requests)); 520 } 521 522 bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts) 523 { 524 auto r = std::make_shared<RouterInfo>(path); 525 if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses () && 526 ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL && // too old 527 (r->GetVersion () >= NETDB_MIN_ALLOWED_VERSION || r->IsHighBandwidth ())) // old version 528 { 529 r->DeleteBuffer (); 530 if (m_RouterInfos.emplace (r->GetIdentHash (), r).second) 531 { 532 if (r->IsFloodfill () && r->IsEligibleFloodfill ()) 533 m_Floodfills.Insert (r); 534 } 535 } 536 else 537 { 538 LogPrint(eLogWarning, "NetDb: RI from ", path, " is invalid or too old. Delete"); 539 i2p::fs::Remove(path); 540 } 541 return true; 542 } 543 544 void NetDb::VisitLeaseSets(LeaseSetVisitor v) 545 { 546 std::lock_guard<std::mutex> lock(m_LeaseSetsMutex); 547 for ( auto & entry : m_LeaseSets) 548 v(entry.first, entry.second); 549 } 550 551 void NetDb::VisitStoredRouterInfos(RouterInfoVisitor v) 552 { 553 m_Storage.Iterate([v] (const std::string & filename) 554 { 555 auto ri = std::make_shared<i2p::data::RouterInfo>(filename); 556 v(ri); 557 }); 558 } 559 560 void NetDb::VisitRouterInfos(RouterInfoVisitor v) 561 { 562 std::lock_guard<std::mutex> lock(m_RouterInfosMutex); 563 for ( const auto & item : m_RouterInfos ) 564 v(item.second); 565 } 566 567 size_t NetDb::VisitRandomRouterInfos(RouterInfoFilter filter, RouterInfoVisitor v, size_t n) 568 { 569 std::vector<std::shared_ptr<const RouterInfo> > found; 570 const size_t max_iters_per_cyle = 3; 571 size_t iters = max_iters_per_cyle; 572 while(n > 0) 573 { 574 std::lock_guard<std::mutex> lock(m_RouterInfosMutex); 575 uint32_t idx = m_Rng () % m_RouterInfos.size (); 576 uint32_t i = 0; 577 for (const auto & it : m_RouterInfos) { 578 if(i >= idx) // are we at the random start point? 579 { 580 // yes, check if we want this one 581 if(filter(it.second)) 582 { 583 // we have a match 584 --n; 585 found.push_back(it.second); 586 // reset max iterations per cycle 587 iters = max_iters_per_cyle; 588 break; 589 } 590 } 591 else // not there yet 592 ++i; 593 } 594 // we have enough 595 if(n == 0) break; 596 --iters; 597 // have we tried enough this cycle ? 598 if(!iters) { 599 // yes let's try the next cycle 600 --n; 601 iters = max_iters_per_cyle; 602 } 603 } 604 // visit the ones we found 605 size_t visited = 0; 606 for(const auto & ri : found ) { 607 v(ri); 608 ++visited; 609 } 610 return visited; 611 } 612 613 void NetDb::Load () 614 { 615 // make sure we cleanup netDb from previous attempts 616 m_RouterInfos.clear (); 617 m_Floodfills.Clear (); 618 619 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); 620 std::vector<std::string> files; 621 m_Storage.Traverse(files); 622 for (const auto& path : files) 623 LoadRouterInfo (path, ts); 624 625 LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.GetSize (), " floodfils)"); 626 } 627 628 void NetDb::SaveUpdated () 629 { 630 if (m_PersistingRouters.valid ()) 631 { 632 if (m_PersistingRouters.wait_for(std::chrono::seconds(0)) == std::future_status::ready) 633 m_PersistingRouters.get (); 634 else 635 { 636 LogPrint (eLogWarning, "NetDb: Can't save updated routers. Routers are being saved to disk"); 637 return; 638 } 639 } 640 641 int updatedCount = 0, deletedCount = 0, deletedFloodfillsCount = 0; 642 auto total = m_RouterInfos.size (); 643 auto totalFloodfills = m_Floodfills.GetSize (); 644 uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; 645 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); 646 auto uptime = i2p::context.GetUptime (); 647 double minTunnelCreationSuccessRate; 648 i2p::config::GetOption("limits.zombies", minTunnelCreationSuccessRate); 649 bool isLowRate = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < minTunnelCreationSuccessRate; 650 // routers don't expire if less than 90 or uptime is less than 1 hour 651 bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // 10 minutes 652 if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour 653 expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : 654 NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total; 655 bool isOffline = checkForExpiration && i2p::transport::transports.GetNumPeers () < NETDB_MIN_TRANSPORTS; // enough routers and uptime, but no transports 656 657 std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk; 658 std::list<std::string> removeFromDisk; 659 660 auto own = i2p::context.GetSharedRouterInfo (); 661 for (auto [ident, r]: m_RouterInfos) 662 { 663 if (!r || r == own) continue; // skip own 664 if (r->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete 665 { 666 std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update 667 r->DeleteBuffer (); 668 } 669 if (r->IsUpdated ()) 670 { 671 if (r->GetBuffer () && !r->IsUnreachable ()) 672 { 673 // we have something to save 674 std::shared_ptr<RouterInfo::Buffer> buffer; 675 { 676 std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update 677 buffer = r->CopyBuffer (); 678 } 679 if (!i2p::transport::transports.IsConnected (ident)) 680 r->ScheduleBufferToDelete (); 681 if (buffer) 682 saveToDisk.emplace_back(ident.ToBase64 (), buffer); 683 } 684 r->SetUpdated (false); 685 updatedCount++; 686 continue; 687 } 688 else if (r->GetBuffer () && ts > r->GetTimestamp () + NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) 689 // since update was long time ago we assume that router is not connected anymore 690 r->ScheduleBufferToDelete (); 691 692 if (r->HasProfile () && r->GetProfile ()->IsUnreachable ()) 693 r->SetUnreachable (true); 694 // make router reachable back if too few routers or floodfills 695 if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline || 696 (r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) 697 r->SetUnreachable (false); 698 if (!r->IsUnreachable ()) 699 { 700 // find & mark expired routers 701 if (!r->GetCompatibleTransports (true)) // non reachable by any transport 702 r->SetUnreachable (true); 703 else if (r->GetVersion () < NETDB_MIN_ALLOWED_VERSION && !r->IsHighBandwidth ()) // too old 704 r->SetUnreachable (true); 705 else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ()) 706 { 707 LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (r->GetTimestamp () - ts)/1000LL, " seconds"); 708 r->SetUnreachable (true); 709 } 710 else if (checkForExpiration) 711 { 712 if (ts > r->GetTimestamp () + expirationTimeout) 713 r->SetUnreachable (true); 714 else if ((ts > r->GetTimestamp () + expirationTimeout/2) && // more than half of expiration 715 total > NETDB_NUM_ROUTERS_THRESHOLD && !r->IsHighBandwidth() && // low bandwidth 716 !r->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill 717 (CreateRoutingKey (ident) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits 718 r->SetUnreachable (true); 719 } 720 } 721 // make router reachable back if connected now or trusted router 722 if (r->IsUnreachable () && (i2p::transport::transports.IsConnected (ident) || 723 i2p::transport::transports.IsTrustedRouter (ident))) 724 r->SetUnreachable (false); 725 726 if (r->IsUnreachable ()) 727 { 728 if (r->IsFloodfill ()) deletedFloodfillsCount++; 729 // delete RI file 730 removeFromDisk.emplace_back (ident.ToBase64()); 731 deletedCount++; 732 if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; 733 } 734 } // m_RouterInfos iteration 735 736 if (!saveToDisk.empty () || !removeFromDisk.empty ()) 737 { 738 m_PersistingRouters = std::async (std::launch::async, &NetDb::PersistRouters, 739 this, std::move (saveToDisk), std::move (removeFromDisk)); 740 } 741 742 m_RouterInfoBuffersPool.CleanUpMt (); 743 m_RouterInfoAddressesPool.CleanUpMt (); 744 m_RouterInfoAddressVectorsPool.CleanUpMt (); 745 m_IdentitiesPool.CleanUpMt (); 746 747 if (updatedCount > 0) 748 LogPrint (eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers"); 749 if (deletedCount > 0) 750 { 751 LogPrint (eLogInfo, "NetDb: Deleting ", deletedCount, " unreachable routers"); 752 // clean up RouterInfos table 753 { 754 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 755 for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();) 756 { 757 if (!it->second || it->second->IsUnreachable ()) 758 it = m_RouterInfos.erase (it); 759 else 760 { 761 it->second->DropProfile (); 762 it++; 763 } 764 } 765 } 766 // clean up expired floodfills or not floodfills anymore 767 { 768 std::lock_guard<std::mutex> l(m_FloodfillsMutex); 769 m_Floodfills.Cleanup ([](const std::shared_ptr<RouterInfo>& r)->bool 770 { 771 return r && r->IsFloodfill () && !r->IsUnreachable (); 772 }); 773 } 774 } 775 } 776 777 void NetDb::PersistRouters (std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > >&& update, 778 std::list<std::string>&& remove) 779 { 780 for (auto it: update) 781 RouterInfo::SaveToFile (m_Storage.Path(it.first), it.second); 782 for (auto it: remove) 783 m_Storage.Remove (it); 784 } 785 786 void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct) 787 { 788 if (direct && (i2p::transport::transports.RoutesRestricted () || i2p::context.IsLimitedConnectivity ())) 789 direct = false; // always use tunnels for restricted routes or limited connectivity 790 if (m_Requests) 791 m_Requests->PostRequestDestination (destination, requestComplete, direct); 792 else 793 LogPrint (eLogError, "NetDb: Requests is null"); 794 } 795 796 void NetDb::HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m) 797 { 798 uint8_t flood = m->GetPayload ()[0] & NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD; 799 bool updated; 800 auto ri = AddRouterInfo (m->GetPayload () + 1, m->GetPayloadLength () - 1, updated); // without flags 801 if (flood && updated && context.IsFloodfill () && ri) 802 { 803 auto floodMsg = CreateDatabaseStoreMsg (ri, 0); // replyToken = 0 804 Flood (ri->GetIdentHash (), floodMsg); 805 } 806 } 807 808 void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m) 809 { 810 const uint8_t * buf = m->GetPayload (); 811 size_t len = m->GetSize (); 812 if (len < DATABASE_STORE_HEADER_SIZE) 813 { 814 LogPrint (eLogError, "NetDb: Database store msg is too short ", len, ". Dropped"); 815 return; 816 } 817 IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET); 818 if (ident.IsZero ()) 819 { 820 LogPrint (eLogDebug, "NetDb: Database store with zero ident, dropped"); 821 return; 822 } 823 uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); 824 size_t offset = DATABASE_STORE_HEADER_SIZE; 825 if (replyToken) 826 { 827 if (len < offset + 36) // 32 + 4 828 { 829 LogPrint (eLogError, "NetDb: Database store msg with reply token is too short ", len, ". Dropped"); 830 return; 831 } 832 uint32_t tunnelID = bufbe32toh (buf + offset); 833 offset += 4; 834 if (replyToken != 0xFFFFFFFFU) // if not caught on OBEP or IBGW 835 { 836 IdentHash replyIdent(buf + offset); 837 auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); 838 if (!tunnelID) // send response directly 839 transports.SendMessage (replyIdent, deliveryStatus); 840 else 841 { 842 bool direct = true; 843 if (!i2p::transport::transports.IsConnected (replyIdent)) 844 { 845 auto r = FindRouter (replyIdent); 846 if (r && !r->IsReachableFrom (i2p::context.GetRouterInfo ())) 847 direct = false; 848 } 849 if (direct) // send response directly to IBGW 850 transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (tunnelID, deliveryStatus)); 851 else 852 { 853 // send response through exploratory tunnel 854 auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); 855 auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; 856 if (outbound) 857 outbound->SendTunnelDataMsgTo (replyIdent, tunnelID, deliveryStatus); 858 else 859 LogPrint (eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); 860 } 861 } 862 } 863 offset += 32; 864 } 865 // we must send reply back before this check 866 if (ident == i2p::context.GetIdentHash ()) 867 { 868 LogPrint (eLogDebug, "NetDb: Database store with own RouterInfo received, dropped"); 869 return; 870 } 871 size_t payloadOffset = offset; 872 873 bool updated = false; 874 uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET]; 875 if (storeType) // LeaseSet or LeaseSet2 876 { 877 if (len > MAX_LS_BUFFER_SIZE + offset) 878 { 879 LogPrint (eLogError, "NetDb: Database store message is too long ", len); 880 return; 881 } 882 if (!context.IsFloodfill ()) 883 { 884 LogPrint (eLogInfo, "NetDb: Not Floodfill, LeaseSet store request ignored for ", ident.ToBase32()); 885 return; 886 } 887 else if (!m->from) // unsolicited LS must be received directly 888 { 889 if (storeType == NETDB_STORE_TYPE_LEASESET) // 1 890 { 891 if (CheckLogLevel (eLogDebug)) 892 LogPrint (eLogDebug, "NetDb: Store request: LeaseSet for ", ident.ToBase32()); 893 updated = AddLeaseSet (ident, buf + offset, len - offset); 894 } 895 else // all others are considered as LeaseSet2 896 { 897 if (CheckLogLevel (eLogDebug)) 898 LogPrint (eLogDebug, "NetDb: Store request: LeaseSet2 of type ", int(storeType), " for ", ident.ToBase32()); 899 updated = AddLeaseSet2 (ident, buf + offset, len - offset, storeType); 900 } 901 } 902 } 903 else // RouterInfo 904 { 905 if (CheckLogLevel (eLogDebug)) 906 LogPrint (eLogDebug, "NetDb: Store request: RouterInfo ", ident.ToBase64()); 907 size_t size = bufbe16toh (buf + offset); 908 offset += 2; 909 if (size > MAX_RI_BUFFER_SIZE || size > len - offset) 910 { 911 LogPrint (eLogError, "NetDb: Invalid RouterInfo length ", (int)size); 912 return; 913 } 914 uint8_t uncompressed[MAX_RI_BUFFER_SIZE]; 915 size_t uncompressedSize = m_Inflator.Inflate (buf + offset, size, uncompressed, MAX_RI_BUFFER_SIZE); 916 if (uncompressedSize && uncompressedSize < MAX_RI_BUFFER_SIZE) 917 updated = AddRouterInfo (ident, uncompressed, uncompressedSize); 918 else 919 { 920 LogPrint (eLogInfo, "NetDb: Decompression failed ", uncompressedSize); 921 return; 922 } 923 } 924 925 if (replyToken && context.IsFloodfill () && updated) 926 { 927 // flood updated 928 auto floodMsg = NewI2NPShortMessage (); 929 uint8_t * payload = floodMsg->GetPayload (); 930 memcpy (payload, buf, 33); // key + type 931 htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token 932 size_t msgLen = len - payloadOffset; 933 floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen; 934 if (floodMsg->len < floodMsg->maxLen) 935 { 936 memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen); 937 floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore); 938 int minutesBeforeMidnight = 24*60 - i2p::util::GetMinutesSinceEpoch () % (24*60); 939 bool andNextDay = storeType ? minutesBeforeMidnight < NETDB_NEXT_DAY_LEASESET_THRESHOLD: 940 minutesBeforeMidnight < NETDB_NEXT_DAY_ROUTER_INFO_THRESHOLD; 941 Flood (ident, floodMsg, andNextDay); 942 } 943 else 944 LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len); 945 } 946 } 947 948 void NetDb::HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg) 949 { 950 const uint8_t * buf = msg->GetPayload (); 951 IdentHash ident (buf); 952 if (ident.IsZero ()) 953 { 954 LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored"); 955 return; 956 } 957 std::string key; 958 if (CheckLogLevel (eLogInfo)) 959 key = i2p::data::ByteStreamToBase64 (buf, 32); 960 961 IdentHash replyIdent(buf + 32); 962 uint8_t flag = buf[64]; 963 964 LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag); 965 uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK; 966 const uint8_t * excluded = buf + 65; 967 uint32_t replyTunnelID = 0; 968 if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel 969 { 970 replyTunnelID = bufbe32toh (excluded); 971 excluded += 4; 972 } 973 uint16_t numExcluded = bufbe16toh (excluded); 974 excluded += 2; 975 if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ()) 976 { 977 LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much"); 978 return; 979 } 980 981 std::shared_ptr<I2NPMessage> replyMsg; 982 if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) 983 { 984 if (!context.IsFloodfill ()) 985 { 986 LogPrint (eLogWarning, "NetDb: Exploratory lookup to non-floodfill dropped"); 987 return; 988 } 989 LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded"); 990 std::unordered_set<IdentHash> excludedRouters; 991 const uint8_t * excluded_ident = excluded; 992 for (int i = 0; i < numExcluded; i++) 993 { 994 excludedRouters.insert (excluded_ident); 995 excluded_ident += 32; 996 } 997 replyMsg = CreateDatabaseSearchReply (ident, GetExploratoryNonFloodfill (ident, 998 NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES, excludedRouters)); 999 } 1000 else 1001 { 1002 if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || 1003 lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) 1004 { 1005 // try to find router 1006 auto router = FindRouter (ident); 1007 if (router && !router->IsUnreachable ()) 1008 { 1009 LogPrint (eLogDebug, "NetDb: Requested RouterInfo ", key, " found"); 1010 if (PopulateRouterInfoBuffer (router)) 1011 replyMsg = CreateDatabaseStoreMsg (router); 1012 } 1013 } 1014 1015 if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || 1016 lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)) 1017 { 1018 // try to find leaseset 1019 if (context.IsFloodfill ()) 1020 { 1021 auto leaseSet = FindLeaseSet (ident); 1022 if (!leaseSet) 1023 { 1024 // no leaseset found 1025 LogPrint(eLogDebug, "NetDb: Requested LeaseSet not found for ", ident.ToBase32()); 1026 } 1027 else if (!leaseSet->IsExpired ()) // we don't send back expired leasesets 1028 { 1029 LogPrint (eLogDebug, "NetDb: Requested LeaseSet ", key, " found"); 1030 replyMsg = CreateDatabaseStoreMsg (ident, leaseSet); 1031 } 1032 } 1033 else if (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP) 1034 { 1035 LogPrint (eLogWarning, "NetDb: Explicit LeaseSet lookup to non-floodfill dropped"); 1036 return; 1037 } 1038 } 1039 1040 if (!replyMsg) 1041 { 1042 std::unordered_set<IdentHash> excludedRouters; 1043 const uint8_t * exclude_ident = excluded; 1044 for (int i = 0; i < numExcluded; i++) 1045 { 1046 excludedRouters.insert (exclude_ident); 1047 exclude_ident += 32; 1048 } 1049 auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, false); 1050 if (closestFloodfills.empty ()) 1051 LogPrint (eLogWarning, "NetDb: No more floodfills for ", key, " found. ", numExcluded, " peers excluded"); 1052 replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills); 1053 } 1054 } 1055 excluded += numExcluded * 32; 1056 if (replyMsg) 1057 { 1058 if (replyTunnelID) 1059 { 1060 // encryption might be used though tunnel only 1061 if (flag & (DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_ECIES_FLAG)) // encrypted reply requested 1062 { 1063 const uint8_t * sessionKey = excluded; 1064 const uint8_t numTags = excluded[32]; 1065 if (numTags) 1066 { 1067 if (flag & DATABASE_LOOKUP_ECIES_FLAG) 1068 { 1069 uint64_t tag; 1070 memcpy (&tag, excluded + 33, 8); 1071 replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag); 1072 } 1073 else 1074 { 1075 const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag 1076 i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag); 1077 replyMsg = garlic.WrapSingleMessage (replyMsg); 1078 } 1079 if (!replyMsg) 1080 LogPrint (eLogError, "NetDb: Failed to wrap message"); 1081 } 1082 else 1083 LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided"); 1084 } 1085 bool direct = true; 1086 if (!i2p::transport::transports.IsConnected (replyIdent)) 1087 { 1088 auto r = FindRouter (replyIdent); 1089 if (r && !r->IsReachableFrom (i2p::context.GetRouterInfo ())) 1090 direct = false; 1091 } 1092 if (direct) 1093 transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg)); 1094 else 1095 { 1096 auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); 1097 auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; 1098 if (outbound) 1099 outbound->SendTunnelDataMsgTo (replyIdent, replyTunnelID, replyMsg); 1100 else 1101 LogPrint (eLogWarning, "NetDb: Can't send lookup reply to ", replyIdent.ToBase64 (), ". Non reachable and no outbound tunnels"); 1102 } 1103 } 1104 else 1105 transports.SendMessage (replyIdent, replyMsg); 1106 } 1107 } 1108 1109 void NetDb::Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg, bool andNextDay) 1110 { 1111 std::unordered_set<IdentHash> excluded; 1112 excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself 1113 excluded.insert (ident); // don't flood back 1114 for (int i = 0; i < 3; i++) 1115 { 1116 auto floodfill = GetClosestFloodfill (ident, excluded, false); // current day 1117 if (floodfill) 1118 { 1119 const auto& h = floodfill->GetIdentHash(); 1120 transports.SendMessage (h, CopyI2NPMessage(floodMsg)); 1121 excluded.insert (h); 1122 } 1123 else 1124 return; // no more floodfills 1125 } 1126 if (andNextDay) 1127 { 1128 // flood to two more closest flodfills for next day 1129 std::unordered_set<IdentHash> excluded1; 1130 excluded1.insert (i2p::context.GetIdentHash ()); // don't flood to itself 1131 excluded1.insert (ident); // don't flood back 1132 for (int i = 0; i < 2; i++) 1133 { 1134 auto floodfill = GetClosestFloodfill (ident, excluded1, true); // next day 1135 if (floodfill) 1136 { 1137 const auto& h = floodfill->GetIdentHash(); 1138 if (!excluded.count (h)) // we didn't send for current day, otherwise skip 1139 transports.SendMessage (h, CopyI2NPMessage(floodMsg)); 1140 excluded1.insert (h); 1141 } 1142 else 1143 return; 1144 } 1145 } 1146 } 1147 1148 std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter () const 1149 { 1150 return GetRandomRouter ( 1151 [](std::shared_ptr<const RouterInfo> router)->bool 1152 { 1153 return !router->IsHidden (); 1154 }); 1155 } 1156 1157 std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, 1158 bool reverse, bool endpoint, bool clientTunnel) const 1159 { 1160 bool checkIsReal = clientTunnel && i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD && // too low rate 1161 context.GetUptime () > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // after 10 minutes uptime 1162 return GetRandomRouter ( 1163 [compatibleWith, reverse, endpoint, clientTunnel, checkIsReal](std::shared_ptr<const RouterInfo> router)->bool 1164 { 1165 return !router->IsHidden () && router != compatibleWith && 1166 (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)): 1167 router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) && 1168 router->GetVersion () >= NETDB_MIN_ALLOWED_VERSION && 1169 router->IsECIES () && !router->IsHighCongestion (clientTunnel) && 1170 (!i2p::transport::transports.IsCheckReserved () || !router->IsSameSubnet (*compatibleWith)) && 1171 (!checkIsReal || router->GetProfile ()->IsReal ()) && 1172 (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse) 1173 }); 1174 } 1175 1176 std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const 1177 { 1178 return GetRandomRouter ( 1179 [v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool 1180 { 1181 return !router->IsHidden () && router->IsECIES () && 1182 router->IsSSU2PeerTesting (v4) && !excluded.count (router->GetIdentHash ()); 1183 }); 1184 } 1185 1186 std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const 1187 { 1188 return GetRandomRouter ( 1189 [v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool 1190 { 1191 return !router->IsHidden () && router->IsSSU2Introducer (v4) && 1192 !excluded.count (router->GetIdentHash ()); 1193 }); 1194 } 1195 1196 std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, 1197 bool reverse, bool endpoint) const 1198 { 1199 bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD && // too low rate 1200 context.GetUptime () > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // after 10 minutes uptime 1201 return GetRandomRouter ( 1202 [compatibleWith, reverse, endpoint, checkIsReal](std::shared_ptr<const RouterInfo> router)->bool 1203 { 1204 return !router->IsHidden () && router != compatibleWith && 1205 (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)) : 1206 router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) && 1207 (router->GetCaps () & RouterInfo::eHighBandwidth) && 1208 router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION && 1209 router->IsECIES () && !router->IsHighCongestion (true) && 1210 (!i2p::transport::transports.IsCheckReserved () || !router->IsSameSubnet (*compatibleWith)) && 1211 (!checkIsReal || router->GetProfile ()->IsReal ()) && 1212 (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse) 1213 1214 }); 1215 } 1216 1217 template<typename Filter> 1218 std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const 1219 { 1220 if (m_RouterInfos.empty()) 1221 return nullptr; 1222 uint16_t inds[3]; 1223 RAND_bytes ((uint8_t *)inds, sizeof (inds)); 1224 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 1225 auto count = m_RouterInfos.size (); 1226 if(count == 0) return nullptr; 1227 inds[0] %= count; 1228 auto it = m_RouterInfos.begin (); 1229 std::advance (it, inds[0]); 1230 // try random router 1231 if (it != m_RouterInfos.end () && !it->second->IsUnreachable () && filter (it->second)) 1232 return it->second; 1233 // try some routers around 1234 auto it1 = m_RouterInfos.begin (); 1235 if (inds[0]) 1236 { 1237 // before 1238 inds[1] %= inds[0]; 1239 std::advance (it1, (inds[1] + inds[0])/2); 1240 } 1241 else 1242 it1 = it; 1243 auto it2 = it; 1244 if (inds[0] < m_RouterInfos.size () - 1) 1245 { 1246 // after 1247 inds[2] %= (m_RouterInfos.size () - 1 - inds[0]); inds[2] /= 2; 1248 std::advance (it2, inds[2]); 1249 } 1250 // it1 - from, it2 - to 1251 it = it1; 1252 while (it != it2 && it != m_RouterInfos.end ()) 1253 { 1254 if (!it->second->IsUnreachable () && filter (it->second)) 1255 return it->second; 1256 it++; 1257 } 1258 // still not found, try from the beginning 1259 it = m_RouterInfos.begin (); 1260 while (it != it1 && it != m_RouterInfos.end ()) 1261 { 1262 if (!it->second->IsUnreachable () && filter (it->second)) 1263 return it->second; 1264 it++; 1265 } 1266 // still not found, try to the beginning 1267 it = it2; 1268 while (it != m_RouterInfos.end ()) 1269 { 1270 if (!it->second->IsUnreachable () && filter (it->second)) 1271 return it->second; 1272 it++; 1273 } 1274 return nullptr; // seems we have too few routers 1275 } 1276 1277 void NetDb::PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg) 1278 { 1279 if (msg) m_Queue.Put (msg); 1280 } 1281 1282 void NetDb::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg) 1283 { 1284 if (msg && m_Requests) 1285 m_Requests->PostDatabaseSearchReplyMsg (msg); 1286 } 1287 1288 std::shared_ptr<const RouterInfo> NetDb::GetClosestFloodfill (const IdentHash& destination, 1289 const std::unordered_set<IdentHash>& excluded, bool nextDay) const 1290 { 1291 IdentHash destKey = CreateRoutingKey (destination, nextDay); 1292 std::lock_guard<std::mutex> l(m_FloodfillsMutex); 1293 return m_Floodfills.FindClosest (destKey, [&excluded](const std::shared_ptr<RouterInfo>& r)->bool 1294 { 1295 return r && !r->IsUnreachable () && !r->GetProfile ()->IsUnreachable () && 1296 !excluded.count (r->GetIdentHash ()); 1297 }); 1298 } 1299 1300 std::vector<IdentHash> NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num, 1301 std::unordered_set<IdentHash>& excluded, bool closeThanUsOnly) const 1302 { 1303 std::vector<IdentHash> res; 1304 IdentHash destKey = CreateRoutingKey (destination); 1305 std::vector<std::shared_ptr<RouterInfo> > v; 1306 { 1307 std::lock_guard<std::mutex> l(m_FloodfillsMutex); 1308 v = m_Floodfills.FindClosest (destKey, num, [&excluded](const std::shared_ptr<RouterInfo>& r)->bool 1309 { 1310 return r && !r->IsUnreachable () && !r->GetProfile ()->IsUnreachable () && 1311 !excluded.count (r->GetIdentHash ()); 1312 }); 1313 } 1314 if (v.empty ()) return res; 1315 1316 XORMetric ourMetric; 1317 if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash (); 1318 for (auto& it: v) 1319 { 1320 if (closeThanUsOnly && ourMetric < (destKey ^ it->GetIdentHash ())) break; 1321 res.push_back (it->GetIdentHash ()); 1322 } 1323 return res; 1324 } 1325 1326 std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily (FamilyID fam) const 1327 { 1328 return GetRandomRouter( 1329 [fam](std::shared_ptr<const RouterInfo> router)->bool 1330 { 1331 return router->IsFamily(fam); 1332 }); 1333 } 1334 1335 std::vector<IdentHash> NetDb::GetExploratoryNonFloodfill (const IdentHash& destination, 1336 size_t num, const std::unordered_set<IdentHash>& excluded) 1337 { 1338 std::vector<IdentHash> ret; 1339 if (!num || m_RouterInfos.empty ()) return ret; // empty list 1340 auto ts = i2p::util::GetMonotonicSeconds (); 1341 if (ts > m_LastExploratorySelectionUpdateTime + NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL) 1342 { 1343 // update selection 1344 m_ExploratorySelection.clear (); 1345 std::vector<std::shared_ptr<const RouterInfo> > eligible; 1346 eligible.reserve (m_RouterInfos.size ()); 1347 { 1348 // collect eligible from current netdb 1349 bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate 1350 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 1351 for (const auto& it: m_RouterInfos) 1352 if (!it.second->IsDeclaredFloodfill () && 1353 (!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ()))) 1354 eligible.push_back (it.second); 1355 } 1356 if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE) 1357 { 1358 std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection), 1359 NETDB_MAX_EXPLORATORY_SELECTION_SIZE, m_Rng); 1360 } 1361 else 1362 std::swap (m_ExploratorySelection, eligible); 1363 m_LastExploratorySelectionUpdateTime = ts; 1364 } 1365 1366 // sort by distance 1367 IdentHash destKey = CreateRoutingKey (destination); 1368 std::map<XORMetric, std::shared_ptr<const RouterInfo> > sorted; 1369 for (const auto& it: m_ExploratorySelection) 1370 if (!excluded.count (it->GetIdentHash ())) 1371 sorted.emplace (destKey ^ it->GetIdentHash (), it); 1372 // return first num closest routers 1373 for (const auto& it: sorted) 1374 { 1375 ret.push_back (it.second->GetIdentHash ()); 1376 if (ret.size () >= num) break; 1377 } 1378 return ret; 1379 } 1380 1381 void NetDb::ManageRouterInfos () 1382 { 1383 auto ts = i2p::util::GetSecondsSinceEpoch (); 1384 { 1385 std::lock_guard<std::mutex> l(m_RouterInfosMutex); 1386 for (auto& it: m_RouterInfos) 1387 it.second->UpdateIntroducers (ts); 1388 } 1389 SaveUpdated (); 1390 } 1391 1392 void NetDb::ManageLeaseSets () 1393 { 1394 auto ts = i2p::util::GetMillisecondsSinceEpoch (); 1395 for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();) 1396 { 1397 if (!it->second->IsValid () || ts > it->second->GetExpirationTime () - LEASE_ENDDATE_THRESHOLD) 1398 { 1399 LogPrint (eLogInfo, "NetDb: LeaseSet ", it->first.ToBase64 (), " expired or invalid"); 1400 it = m_LeaseSets.erase (it); 1401 } 1402 else 1403 ++it; 1404 } 1405 m_LeasesPool.CleanUpMt (); 1406 } 1407 1408 bool NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r) 1409 { 1410 if (!r) return false; 1411 if (r->GetBuffer ()) return true; 1412 return r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ())); 1413 } 1414 } 1415 }