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

0001 /*
0002     SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "database.h"
0007 #include <arpa/inet.h>
0008 #include <torrent/globals.h>
0009 #include <util/functions.h>
0010 #include <util/log.h>
0011 
0012 using namespace bt;
0013 
0014 namespace dht
0015 {
0016 DBItem::DBItem()
0017 {
0018     time_stamp = bt::CurrentTime();
0019 }
0020 
0021 DBItem::DBItem(const net::Address &addr)
0022     : addr(addr)
0023 {
0024     time_stamp = bt::CurrentTime();
0025 }
0026 
0027 DBItem::DBItem(const DBItem &it)
0028 {
0029     addr = it.addr;
0030     time_stamp = it.time_stamp;
0031 }
0032 
0033 DBItem::~DBItem()
0034 {
0035 }
0036 
0037 bool DBItem::expired(bt::TimeStamp now) const
0038 {
0039     return (now - time_stamp >= MAX_ITEM_AGE);
0040 }
0041 
0042 DBItem &DBItem::operator=(const DBItem &it)
0043 {
0044     addr = it.addr;
0045     time_stamp = it.time_stamp;
0046     return *this;
0047 }
0048 
0049 Uint32 DBItem::pack(Uint8 *buf) const
0050 {
0051     if (addr.ipVersion() == 4) {
0052         WriteUint32(buf, 0, addr.toIPv4Address());
0053         WriteUint16(buf, 4, addr.port());
0054         return 6;
0055     } else {
0056         memcpy(buf, addr.toIPv6Address().c, 16);
0057         WriteUint16(buf, 16, addr.port());
0058         return 18;
0059     }
0060 }
0061 
0062 ///////////////////////////////////////////////
0063 
0064 Database::Database()
0065 {
0066     items.setAutoDelete(true);
0067 }
0068 
0069 Database::~Database()
0070 {
0071 }
0072 
0073 void Database::store(const dht::Key &key, const DBItem &dbi)
0074 {
0075     DBItemList *dbl = items.find(key);
0076     if (!dbl) {
0077         dbl = new DBItemList();
0078         items.insert(key, dbl);
0079     }
0080     dbl->append(dbi);
0081 }
0082 
0083 void Database::sample(const dht::Key &key, DBItemList &tdbl, bt::Uint32 max_entries, bt::Uint32 ip_version)
0084 {
0085     DBItemList *dbl = items.find(key);
0086     if (!dbl)
0087         return;
0088 
0089     DBItemList::iterator i = dbl->begin();
0090     while (i != dbl->end() && tdbl.count() < (int)max_entries) {
0091         if (ip_version == (bt::Uint32)i->getAddress().ipVersion())
0092             tdbl.append(*i);
0093         ++i;
0094     }
0095 }
0096 
0097 void Database::expire(bt::TimeStamp now)
0098 {
0099     bt::PtrMap<dht::Key, DBItemList>::iterator itr = items.begin();
0100     while (itr != items.end()) {
0101         DBItemList *dbl = itr->second;
0102         // newer keys are inserted at the back
0103         // so we can stop when we hit the first key which is not expired
0104         while (dbl->count() > 0 && dbl->first().expired(now)) {
0105             dbl->pop_front();
0106         }
0107         if (dbl->count() == 0) {
0108             // PtrMap::erase(PtrMap::iterator) does not auto-delete
0109             delete dbl;
0110             itr = items.erase(itr);
0111         } else {
0112             ++itr;
0113         }
0114     }
0115 
0116     QMap<QByteArray, bt::TimeStamp>::iterator token_itr = tokens.begin();
0117     while (token_itr != tokens.end()) {
0118         if (now - token_itr.value() >= MAX_ITEM_AGE) {
0119             token_itr = tokens.erase(token_itr);
0120         } else {
0121             ++token_itr;
0122         }
0123     }
0124 }
0125 
0126 QByteArray Database::genToken(const net::Address &addr)
0127 {
0128     if (addr.ipVersion() == 4) {
0129         Uint8 tdata[14];
0130         TimeStamp now = bt::CurrentTime();
0131         // generate a hash of the ip port and the current time
0132         // should prevent anybody from crapping things up
0133         bt::WriteUint32(tdata, 0, addr.toIPv4Address());
0134         bt::WriteUint16(tdata, 4, addr.port());
0135         bt::WriteUint64(tdata, 6, now);
0136 
0137         QByteArray token = SHA1Hash::generate(tdata, 14).toByteArray();
0138         // keep track of the token, tokens will expire after a while
0139         tokens.insert(token, now);
0140         return token;
0141     } else {
0142         Uint8 tdata[26];
0143         TimeStamp now = bt::CurrentTime();
0144         // generate a hash of the ip port and the current time
0145         // should prevent anybody from crapping things up
0146         memcpy(tdata, addr.toIPv6Address().c, 16);
0147         bt::WriteUint16(tdata, 16, addr.port());
0148         bt::WriteUint64(tdata, 18, now);
0149 
0150         QByteArray token = SHA1Hash::generate(tdata, 26).toByteArray();
0151         // keep track of the token, tokens will expire after a while
0152         tokens.insert(token, now);
0153         return token;
0154     }
0155 }
0156 
0157 bool Database::checkToken(const QByteArray &token, const net::Address &addr)
0158 {
0159     // the token must be in the map
0160     if (!tokens.contains(token))
0161         return false;
0162 
0163     // in the map so now get the timestamp and regenerate the token
0164     // using the IP and port of the sender
0165     TimeStamp ts = tokens[token];
0166 
0167     if (addr.ipVersion() == 4) {
0168         Uint8 tdata[14];
0169         bt::WriteUint32(tdata, 0, addr.toIPv4Address());
0170         bt::WriteUint16(tdata, 4, addr.port());
0171         bt::WriteUint64(tdata, 6, ts);
0172         QByteArray ct = SHA1Hash::generate(tdata, 14).toByteArray();
0173 
0174         // compare the generated token to the one received
0175         if (token != ct) { // not good, this peer didn't went through the proper channels
0176             Out(SYS_DHT | LOG_DEBUG) << "Invalid token" << endl;
0177             return false;
0178         }
0179     } else {
0180         Uint8 tdata[26];
0181 
0182         memcpy(tdata, addr.toIPv6Address().c, 16);
0183         bt::WriteUint16(tdata, 16, addr.port());
0184         bt::WriteUint64(tdata, 18, ts);
0185 
0186         QByteArray ct = SHA1Hash::generate(tdata, 26).toByteArray();
0187         // compare the generated token to the one received
0188         if (token != ct) { // not good, this peer didn't went through the proper channels
0189             Out(SYS_DHT | LOG_DEBUG) << "Invalid token" << endl;
0190             return false;
0191         }
0192     }
0193 
0194     // expire the token
0195     tokens.remove(token);
0196     return true;
0197 }
0198 
0199 bool Database::contains(const dht::Key &key) const
0200 {
0201     return items.find(key) != nullptr;
0202 }
0203 
0204 void Database::insert(const dht::Key &key)
0205 {
0206     DBItemList *dbl = items.find(key);
0207     if (!dbl) {
0208         dbl = new DBItemList();
0209         items.insert(key, dbl);
0210     }
0211 }
0212 }