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.
ktorrent/libktorrent/torrent/udptracker.cpp

292 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 <stdlib.h>
#include <kresolver.h>
#include <util/functions.h>
#include <util/log.h>
#include <ksocketaddress.h>
#include "peermanager.h"
#include "udptracker.h"
#include "torrentcontrol.h"
#include "globals.h"
#include "server.h"
#include "udptrackersocket.h"
using namespace kt;
using namespace KNetwork;
namespace bt
{
UDPTrackerSocket* UDPTracker::socket = 0;
Uint32 UDPTracker::num_instances = 0;
UDPTracker::UDPTracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier)
: Tracker(url,tor,id,tier)
{
num_instances++;
if (!socket)
socket = new UDPTrackerSocket();
connection_id = 0;
transaction_id = 0;
interval = 0;
connect(&conn_timer,TQT_SIGNAL(timeout()),this,TQT_SLOT(onConnTimeout()));
connect(socket,TQT_SIGNAL(announceRecieved(Int32, const TQByteArray &)),
this,TQT_SLOT(announceRecieved(Int32, const TQByteArray& )));
connect(socket,TQT_SIGNAL(connectRecieved(Int32, Int64 )),
this,TQT_SLOT(connectRecieved(Int32, Int64 )));
connect(socket,TQT_SIGNAL(error(Int32, const TQString& )),
this,TQT_SLOT(onError(Int32, const TQString& )));
KResolver::resolveAsync(this,TQT_SLOT(onResolverResults(KResolverResults )),
url.host(),TQString::number(url.port()));
}
UDPTracker::~UDPTracker()
{
num_instances--;
if (num_instances == 0)
{
delete socket;
socket = 0;
}
}
void UDPTracker::start()
{
event = STARTED;
conn_timer.stop();
doRequest();
}
void UDPTracker::stop(WaitJob* )
{
if (!started)
return;
event = STOPPED;
conn_timer.stop();
doRequest();
started = false;
}
void UDPTracker::completed()
{
event = COMPLETED;
conn_timer.stop();
doRequest();
}
void UDPTracker::manualUpdate()
{
conn_timer.stop();
if (!started)
event = STARTED;
doRequest();
}
void UDPTracker::connectRecieved(Int32 tid,Int64 cid)
{
if (tid != transaction_id)
return;
connection_id = cid;
n = 0;
sendAnnounce();
}
void UDPTracker::announceRecieved(Int32 tid,const TQByteArray & data)
{
if (tid != transaction_id)
return;
const Uint8* buf = (const Uint8*)data.data();
/*
0 32-bit integer action 1
4 32-bit integer transaction_id
8 32-bit integer interval
12 32-bit integer leechers
16 32-bit integer seeders
20 + 6 * n 32-bit integer IP address
24 + 6 * n 16-bit integer TCP port
20 + 6 * N
*/
interval = ReadInt32(buf,8);
leechers = ReadInt32(buf,12);
seeders = ReadInt32(buf,16);
Uint32 nip = leechers + seeders;
Uint32 j = 0;
for (Uint32 i = 20;i < data.size() && j < nip;i+=6,j++)
{
Uint32 ip = ReadUint32(buf,i);
addPeer(TQString("%1.%2.%3.%4")
.tqarg((ip & (0xFF000000)) >> 24)
.tqarg((ip & (0x00FF0000)) >> 16)
.tqarg((ip & (0x0000FF00)) >> 8)
.tqarg(ip & 0x000000FF),
ReadUint16(buf,i+4));
}
peersReady(this);
connection_id = 0;
conn_timer.stop();
if (event != STOPPED)
{
if (event == STARTED)
started = true;
event = NONE;
requestOK();
}
else
{
stopDone();
requestOK();
}
}
void UDPTracker::onError(Int32 tid,const TQString & error_string)
{
if (tid != transaction_id)
return;
Out(SYS_TRK|LOG_IMPORTANT) << "UDPTracker::error : " << error_string << endl;
requestFailed(error_string);
}
bool UDPTracker::doRequest()
{
Out(SYS_TRK|LOG_NOTICE) << "Doing tracker request to url : " << url << endl;
if (connection_id == 0)
{
n = 0;
sendConnect();
}
else
sendAnnounce();
requestPending();
return true;
}
void UDPTracker::scrape()
{
}
void UDPTracker::sendConnect()
{
transaction_id = socket->newTransactionID();
socket->sendConnect(transaction_id,address);
int tn = 1;
for (int i = 0;i < n;i++)
tn *= 2;
conn_timer.start(60000 * tn,true);
}
void UDPTracker::sendAnnounce()
{
// Out(SYS_TRK|LOG_NOTICE) << "UDPTracker::sendAnnounce()" << endl;
transaction_id = socket->newTransactionID();
/*
0 64-bit integer connection_id
8 32-bit integer action 1
12 32-bit integer transaction_id
16 20-byte string info_hash
36 20-byte string peer_id
56 64-bit integer downloaded
64 64-bit integer left
72 64-bit integer uploaded
80 32-bit integer event
84 32-bit integer IP address 0
88 32-bit integer key
92 32-bit integer num_want -1
96 16-bit integer port
98
*/
Uint32 ev = event;
const TorrentStats & s = tor->getStats();
Uint16 port = Globals::instance().getServer().getPortInUse();
Uint8 buf[98];
WriteInt64(buf,0,connection_id);
WriteInt32(buf,8,ANNOUNCE);
WriteInt32(buf,12,transaction_id);
const SHA1Hash & info_hash = tor->getInfoHash();
memcpy(buf+16,info_hash.getData(),20);
memcpy(buf+36,peer_id.data(),20);
WriteInt64(buf,56,s.trk_bytes_downloaded);
if (ev == COMPLETED)
WriteInt64(buf,64,0);
else
WriteInt64(buf,64,s.bytes_left);
WriteInt64(buf,72,s.trk_bytes_uploaded);
WriteInt32(buf,80,ev);
TQString cip = Tracker::getCustomIP();
if (cip.isNull())
{
WriteUint32(buf,84,0);
}
else
{
KNetwork::KIpAddress addr(cip);
WriteUint32(buf,84,addr.IPv4Addr(true));
}
WriteUint32(buf,88,key);
if (ev != STOPPED)
WriteInt32(buf,92,100);
else
WriteInt32(buf,92,0);
WriteUint16(buf,96,port);
socket->sendAnnounce(transaction_id,buf,address);
}
void UDPTracker::onConnTimeout()
{
if (connection_id)
{
connection_id = 0;
n++;
if (event != STOPPED)
sendConnect();
else
stopDone();
}
else
{
doRequest();
}
}
void UDPTracker::onResolverResults(KResolverResults res)
{
address = res.front().address();
}
}
#include "udptracker.moc"