You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
7.0 KiB
288 lines
7.0 KiB
/***************************************************************************
|
|
* Copyright (C) 2005 by Joris Guisson *
|
|
* joris.guisson@gmail.com *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
|
***************************************************************************/
|
|
|
|
#include <util/log.h>
|
|
#include <util/file.h>
|
|
#include <util/fileops.h>
|
|
#include <util/functions.h>
|
|
#include <torrent/globals.h>
|
|
#include "node.h"
|
|
#include "rpcmsg.h"
|
|
#include "key.h"
|
|
#include "rpccall.h"
|
|
#include "rpcserver.h"
|
|
#include "kclosestnodessearch.h"
|
|
#include "dht.h"
|
|
#include "nodelookup.h"
|
|
|
|
using namespace bt;
|
|
using namespace KNetwork;
|
|
|
|
namespace dht
|
|
{
|
|
static void SaveKey(const dht::Key & key,const TQString & key_file)
|
|
{
|
|
bt::File fptr;
|
|
if (!fptr.open(key_file,"wb"))
|
|
{
|
|
Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << key_file << " : " << fptr.errorString() << endl;
|
|
return;
|
|
}
|
|
|
|
fptr.write(key.getData(),20);
|
|
fptr.close();
|
|
}
|
|
|
|
static dht::Key LoadKey(const TQString & key_file,bool & new_key)
|
|
{
|
|
bt::File fptr;
|
|
if (!fptr.open(key_file,"rb"))
|
|
{
|
|
Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << key_file << " : " << fptr.errorString() << endl;
|
|
dht::Key r = dht::Key::random();
|
|
SaveKey(r,key_file);
|
|
new_key = true;
|
|
return r;
|
|
}
|
|
|
|
Uint8 data[20];
|
|
if (fptr.read(data,20) != 20)
|
|
{
|
|
dht::Key r = dht::Key::random();
|
|
SaveKey(r,key_file);
|
|
new_key = true;
|
|
return r;
|
|
}
|
|
|
|
new_key = false;
|
|
return dht::Key(data);
|
|
}
|
|
|
|
Node::Node(RPCServer* srv,const TQString & key_file) : srv(srv)
|
|
{
|
|
num_receives = 0;
|
|
num_entries = 0;
|
|
delete_table = false;
|
|
our_id = LoadKey(key_file,delete_table);
|
|
for (int i = 0;i < 160;i++)
|
|
bucket[i] = 0;
|
|
}
|
|
|
|
|
|
Node::~Node()
|
|
{
|
|
for (int i = 0;i < 160;i++)
|
|
{
|
|
KBucket* b = bucket[i];
|
|
if (b)
|
|
delete b;
|
|
}
|
|
}
|
|
|
|
Uint8 Node::findBucket(const dht::Key & id)
|
|
{
|
|
// XOR our id and the sender's ID
|
|
dht::Key d = dht::Key::distance(id,our_id);
|
|
// now use the first on bit to determin which bucket it should go in
|
|
|
|
Uint8 bit_on = 0xFF;
|
|
for (Uint32 i = 0;i < 20;i++)
|
|
{
|
|
// get the byte
|
|
Uint8 b = *(d.getData() + i);
|
|
// no bit on in this byte so continue
|
|
if (b == 0x00)
|
|
continue;
|
|
|
|
for (Uint8 j = 0;j < 8;j++)
|
|
{
|
|
if (b & (0x80 >> j))
|
|
{
|
|
// we have found the bit
|
|
bit_on = (19 - i)*8 + (7 - j);
|
|
return bit_on;
|
|
}
|
|
}
|
|
}
|
|
return bit_on;
|
|
}
|
|
|
|
void Node::recieved(DHT* dh_table,const MsgBase* msg)
|
|
{
|
|
Uint8 bit_on = findBucket(msg->getID());
|
|
|
|
// return if bit_on is not good
|
|
if (bit_on >= 160)
|
|
return;
|
|
|
|
// make the bucket if it doesn't exist
|
|
if (!bucket[bit_on])
|
|
bucket[bit_on] = new KBucket(bit_on,srv,this);
|
|
|
|
// insert it into the bucket
|
|
KBucket* kb = bucket[bit_on];
|
|
kb->insert(KBucketEntry(msg->getOrigin(),msg->getID()));
|
|
num_receives++;
|
|
if (num_receives == 3)
|
|
{
|
|
// do a node lookup upon our own id
|
|
// when we insert the first entry in the table
|
|
dh_table->findNode(our_id);
|
|
}
|
|
|
|
num_entries = 0;
|
|
for (Uint32 i = 0;i < 160;i++)
|
|
if (bucket[i])
|
|
num_entries += bucket[i]->getNumEntries();
|
|
}
|
|
|
|
void Node::findKClosestNodes(KClosestNodesSearch & kns)
|
|
{
|
|
// go over all buckets until
|
|
for (Uint32 i = 0;i < 160;i++)
|
|
{
|
|
if (bucket[i])
|
|
{
|
|
bucket[i]->findKClosestNodes(kns);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::onTimeout(const MsgBase* msg)
|
|
{
|
|
for (Uint32 i = 0;i < 160;i++)
|
|
{
|
|
if (bucket[i] && bucket[i]->onTimeout(msg->getDestination()))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Generate a random key which lies in a certain bucket
|
|
Key RandomKeyInBucket(Uint32 b,const Key & our_id)
|
|
{
|
|
// first generate a random one
|
|
Key r = dht::Key::random();
|
|
Uint8* data = (Uint8*)r.getData();
|
|
|
|
// before we hit bit b, everything needs to be equal to our_id
|
|
Uint8 nb = b / 8;
|
|
for (Uint8 i = 0;i < nb;i++)
|
|
data[i] = *(our_id.getData() + i);
|
|
|
|
|
|
// copy all bits of ob, until we hit the bit which needs to be different
|
|
Uint8 ob = *(our_id.getData() + nb);
|
|
for (Uint8 j = 0;j < b % 8;j++)
|
|
{
|
|
if ((0x80 >> j) & ob)
|
|
data[nb] |= (0x80 >> j);
|
|
else
|
|
data[nb] &= ~(0x80 >> j);
|
|
}
|
|
|
|
// if the bit b is on turn it off else turn it on
|
|
if ((0x80 >> (b % 8)) & ob)
|
|
data[nb] &= ~(0x80 >> (b % 8));
|
|
else
|
|
data[nb] |= (0x80 >> (b % 8));
|
|
|
|
return Key(data);
|
|
}
|
|
|
|
void Node::refreshBuckets(DHT* dh_table)
|
|
{
|
|
for (Uint32 i = 0;i < 160;i++)
|
|
{
|
|
KBucket* b = bucket[i];
|
|
if (b && b->needsToBeRefreshed())
|
|
{
|
|
// the key needs to be the refreshed
|
|
NodeLookup* nl = dh_table->refreshBucket(RandomKeyInBucket(i,our_id),*b);
|
|
if (nl)
|
|
b->setRefreshTask(nl);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Node::saveTable(const TQString & file)
|
|
{
|
|
bt::File fptr;
|
|
if (!fptr.open(file,"wb"))
|
|
{
|
|
Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl;
|
|
return;
|
|
}
|
|
|
|
for (Uint32 i = 0;i < 160;i++)
|
|
{
|
|
KBucket* b = bucket[i];
|
|
if (b)
|
|
{
|
|
b->save(fptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::loadTable(const TQString & file)
|
|
{
|
|
if (delete_table)
|
|
{
|
|
delete_table = false;
|
|
bt::Delete(file,true);
|
|
Out(SYS_DHT|LOG_IMPORTANT) << "DHT: new key, so removing table" << endl;
|
|
return;
|
|
}
|
|
|
|
bt::File fptr;
|
|
if (!fptr.open(file,"rb"))
|
|
{
|
|
Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl;
|
|
return;
|
|
}
|
|
|
|
num_entries = 0;
|
|
while (!fptr.eof())
|
|
{
|
|
BucketHeader hdr;
|
|
if (fptr.read(&hdr,sizeof(BucketHeader)) != sizeof(BucketHeader))
|
|
return;
|
|
|
|
if (hdr.magic != dht::BUCKET_MAGIC_NUMBER || hdr.num_entries > dht::K || hdr.index > 160)
|
|
return;
|
|
|
|
if (hdr.num_entries == 0)
|
|
continue;
|
|
|
|
Out(SYS_DHT|LOG_NOTICE) << "DHT: Loading bucket " << hdr.index << endl;
|
|
if (bucket[hdr.index])
|
|
delete bucket[hdr.index];
|
|
|
|
bucket[hdr.index] = new KBucket(hdr.index,srv,this);
|
|
bucket[hdr.index]->load(fptr,hdr);
|
|
num_entries += bucket[hdr.index]->getNumEntries();
|
|
}
|
|
}
|
|
}
|
|
|
|
#include "node.moc"
|