File indexing completed on 2025-01-05 04:37:11

0001 /*
0002     SPDX-FileCopyrightText: 2012 Joris Guisson <joris.guisson@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kbuckettable.h"
0008 #include "dht.h"
0009 #include "nodelookup.h"
0010 #include <QFile>
0011 #include <bcodec/bdecoder.h>
0012 #include <bcodec/bencoder.h>
0013 #include <bcodec/bnode.h>
0014 #include <util/error.h>
0015 #include <util/file.h>
0016 #include <util/log.h>
0017 
0018 using namespace bt;
0019 
0020 namespace dht
0021 {
0022 KBucketTable::KBucketTable(const Key &our_id)
0023     : our_id(our_id)
0024 {
0025 }
0026 
0027 KBucketTable::~KBucketTable()
0028 {
0029 }
0030 
0031 void KBucketTable::insert(const dht::KBucketEntry &entry, dht::RPCServerInterface *srv)
0032 {
0033     if (buckets.empty()) {
0034         KBucket::Ptr initial(new KBucket(srv, our_id));
0035         buckets.push_back(initial);
0036     }
0037 
0038     KBucketList::iterator kb = findBucket(entry.getID());
0039 
0040     // return if we can't find a bucket, should never happen'
0041     if (kb == buckets.end()) {
0042         Out(SYS_DHT | LOG_IMPORTANT) << "Unable to find bucket !" << endl;
0043         return;
0044     }
0045 
0046     // insert it into the bucket
0047     try {
0048         if ((*kb)->insert(entry)) {
0049             // Bucket needs to be split
0050             std::pair<KBucket::Ptr, KBucket::Ptr> result = (*kb)->split();
0051             /*
0052                             Out(SYS_DHT | LOG_DEBUG) << "Splitting bucket " << (*kb)->minKey().toString() << "-" << (*kb)->maxKey().toString() << endl;
0053                             Out(SYS_DHT | LOG_DEBUG) << "L: " << result.first->minKey().toString() << "-" << result.first->maxKey().toString() << endl;
0054                             Out(SYS_DHT | LOG_DEBUG) << "L range: " << (result.first->maxKey() - result.first->minKey()).toString() << endl;
0055                             Out(SYS_DHT | LOG_DEBUG) << "R: " << result.second->minKey().toString() << "-" << result.second->maxKey().toString() << endl;
0056                             Out(SYS_DHT | LOG_DEBUG) << "R range: " << (result.second->maxKey() - result.second->minKey()).toString() << endl;
0057             */
0058             buckets.insert(kb, result.first);
0059             buckets.insert(kb, result.second);
0060             buckets.erase(kb);
0061             if (result.first->keyInRange(entry.getID()))
0062                 result.first->insert(entry);
0063             else
0064                 result.second->insert(entry);
0065         }
0066     } catch (const KBucket::UnableToSplit &) {
0067         // Can't split, so stop this
0068         Out(SYS_DHT | LOG_IMPORTANT) << "Unable to split buckets further !" << endl;
0069         return;
0070     }
0071 }
0072 
0073 int KBucketTable::numEntries() const
0074 {
0075     int count = 0;
0076     for (const KBucket::Ptr &b : std::as_const(buckets)) {
0077         count += b->getNumEntries();
0078     }
0079 
0080     return count;
0081 }
0082 
0083 KBucketTable::KBucketList::iterator KBucketTable::findBucket(const dht::Key &id)
0084 {
0085     for (KBucketList::iterator i = buckets.begin(); i != buckets.end(); ++i) {
0086         if ((*i)->keyInRange(id))
0087             return i;
0088     }
0089 
0090     return buckets.end();
0091 }
0092 
0093 void KBucketTable::refreshBuckets(DHT *dh_table)
0094 {
0095     for (const KBucket::Ptr &b : std::as_const(buckets)) {
0096         if (b->needsToBeRefreshed()) {
0097             // the key needs to be the refreshed
0098             dht::Key m = dht::Key::mid(b->minKey(), b->maxKey());
0099             NodeLookup *nl = dh_table->refreshBucket(m, *b);
0100             if (nl)
0101                 b->setRefreshTask(nl);
0102         }
0103     }
0104 }
0105 
0106 void KBucketTable::onTimeout(const net::Address &addr)
0107 {
0108     for (const KBucket::Ptr &b : std::as_const(buckets)) {
0109         if (b->onTimeout(addr))
0110             return;
0111     }
0112 }
0113 
0114 void KBucketTable::loadTable(const QString &file, RPCServerInterface *srv)
0115 {
0116     QFile fptr(file);
0117     if (!fptr.open(QIODevice::ReadOnly)) {
0118         Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl;
0119         return;
0120     }
0121 
0122     try {
0123         QByteArray data = fptr.readAll();
0124         bt::BDecoder dec(data, false, 0);
0125 
0126         QScopedPointer<BListNode> bucket_list(dec.decodeList());
0127         if (!bucket_list)
0128             return;
0129 
0130         for (bt::Uint32 i = 0; i < bucket_list->getNumChildren(); i++) {
0131             BDictNode *dict = bucket_list->getDict(i);
0132             if (!dict)
0133                 continue;
0134 
0135             KBucket::Ptr bucket(new KBucket(srv, our_id));
0136             bucket->load(dict);
0137             buckets.push_back(bucket);
0138         }
0139     } catch (bt::Error &e) {
0140         Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Failed to load bucket table: " << e.toString() << endl;
0141     }
0142 }
0143 
0144 void KBucketTable::saveTable(const QString &file)
0145 {
0146     bt::File fptr;
0147     if (!fptr.open(file, "wb")) {
0148         Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl;
0149         return;
0150     }
0151 
0152     BEncoder enc(&fptr);
0153 
0154     try {
0155         enc.beginList();
0156         for (const KBucket::Ptr &b : std::as_const(buckets)) {
0157             b->save(enc);
0158         }
0159         enc.end();
0160     } catch (bt::Error &err) {
0161         Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Failed to save table to " << file << " : " << err.toString() << endl;
0162     }
0163 }
0164 
0165 void KBucketTable::findKClosestNodes(KClosestNodesSearch &kns) const
0166 {
0167     for (const KBucket::Ptr &b : std::as_const(buckets)) {
0168         b->findKClosestNodes(kns);
0169     }
0170 }
0171 
0172 }