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 }