qBittorrent/src/tracker/qtracker.cpp

248 lines
8.3 KiB
C++
Raw Normal View History

/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* 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.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include <QHttpRequestHeader>
#include <QTcpSocket>
2011-09-25 18:26:02 +04:00
#include <QUrl>
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include "qtracker.h"
#include "preferences.h"
using namespace libtorrent;
QTracker::QTracker(QObject *parent) :
QTcpServer(parent)
{
Q_ASSERT(Preferences().isTrackerEnabled());
connect(this, SIGNAL(newConnection()), this, SLOT(handlePeerConnection()));
}
QTracker::~QTracker() {
if(isListening()) {
qDebug("Shutting down the embedded tracker...");
close();
}
// TODO: Store the torrent list
}
void QTracker::handlePeerConnection()
{
QTcpSocket *socket;
while((socket = nextPendingConnection()))
{
qDebug("QTracker: New peer connection");
connect(socket, SIGNAL(readyRead()), SLOT(readRequest()));
}
}
bool QTracker::start()
{
const int listen_port = Preferences().getTrackerPort();
//
if(isListening()) {
if(serverPort() == listen_port) {
// Already listening on the right port, just return
return true;
}
// Wrong port, closing the server
close();
}
qDebug("Starting the embedded tracker...");
// Listen on the predefined port
return listen(QHostAddress::Any, listen_port);
}
void QTracker::readRequest()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray input = socket->readAll();
//qDebug("QTracker: Raw request:\n%s", input.data());
2011-09-25 18:26:02 +04:00
QHttpRequestHeader http_request(input);
if(!http_request.isValid()) {
qDebug("QTracker: Invalid HTTP Request:\n %s", qPrintable(http_request.toString()));
respondInvalidRequest(socket, 100, "Invalid request type");
return;
}
//qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString()));
// Request is correct, is it a GET request?
2011-09-25 18:26:02 +04:00
if(http_request.method() != "GET") {
qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(http_request.method()));
respondInvalidRequest(socket, 100, "Invalid request type");
return;
}
2011-09-25 18:26:02 +04:00
if(!http_request.path().startsWith("/announce", Qt::CaseInsensitive)) {
qDebug("QTracker: Unrecognized path: %s", qPrintable(http_request.path()));
respondInvalidRequest(socket, 100, "Invalid request type");
return;
}
2011-09-25 18:26:02 +04:00
// OK, this is a GET request
2011-09-25 18:26:02 +04:00
// Parse GET parameters
QHash<QString, QString> get_parameters;
QUrl url = QUrl::fromEncoded(http_request.path().toAscii());
QListIterator<QPair<QString, QString> > i(url.queryItems());
while (i.hasNext()) {
QPair<QString, QString> pair = i.next();
get_parameters[pair.first] = pair.second;
}
respondToAnnounceRequest(socket, get_parameters);
}
void QTracker::respondInvalidRequest(QTcpSocket *socket, int code, QString msg)
{
QHttpResponseHeader response;
response.setStatusLine(code, msg);
socket->write(response.toString().toLocal8Bit());
socket->disconnectFromHost();
}
2011-09-25 18:26:02 +04:00
void QTracker::respondToAnnounceRequest(QTcpSocket *socket,
const QHash<QString, QString>& get_parameters)
{
TrackerAnnounceRequest annonce_req;
// IP
annonce_req.peer.ip = socket->peerAddress().toString();
// 1. Get info_hash
2011-09-25 18:26:02 +04:00
if(!get_parameters.contains("info_hash")) {
qDebug("QTracker: Missing info_hash");
respondInvalidRequest(socket, 101, "Missing info_hash");
return;
}
2011-09-25 18:26:02 +04:00
annonce_req.info_hash = get_parameters.value("info_hash");
// info_hash cannot be longer than 20 bytes
2010-10-16 19:30:44 +04:00
/*if(annonce_req.info_hash.toAscii().length() > 20) {
qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toAscii().length());
respondInvalidRequest(socket, 150, "Invalid infohash");
return;
2010-10-16 19:30:44 +04:00
}*/
// 2. Get peer ID
2011-09-25 18:26:02 +04:00
if(!get_parameters.contains("peer_id")) {
qDebug("QTracker: Missing peer_id");
respondInvalidRequest(socket, 102, "Missing peer_id");
return;
}
2011-09-25 18:26:02 +04:00
annonce_req.peer.peer_id = get_parameters.value("peer_id");
// peer_id cannot be longer than 20 bytes
2010-10-16 19:30:44 +04:00
/*if(annonce_req.peer.peer_id.length() > 20) {
qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
respondInvalidRequest(socket, 151, "Invalid peerid");
return;
2010-10-16 19:30:44 +04:00
}*/
// 3. Get port
2011-09-25 18:26:02 +04:00
if(!get_parameters.contains("port")) {
qDebug("QTracker: Missing port");
respondInvalidRequest(socket, 103, "Missing port");
return;
}
bool ok = false;
2011-09-25 18:26:02 +04:00
annonce_req.peer.port = get_parameters.value("port").toInt(&ok);
if(!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) {
qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port);
respondInvalidRequest(socket, 103, "Missing port");
return;
}
// 4. Get event
annonce_req.event = "";
2011-09-25 18:26:02 +04:00
if(get_parameters.contains("event")) {
annonce_req.event = get_parameters.value("event");
qDebug("QTracker: event is %s", qPrintable(annonce_req.event));
}
// 5. Get numwant
annonce_req.numwant = 50;
2011-09-25 18:26:02 +04:00
if(get_parameters.contains("numwant")) {
int tmp = get_parameters.value("numwant").toInt();
if(tmp > 0) {
qDebug("QTracker: numwant=%d", tmp);
annonce_req.numwant = tmp;
}
}
// 6. no_peer_id (extension)
annonce_req.no_peer_id = false;
2011-09-25 18:26:02 +04:00
if(get_parameters.contains("no_peer_id")) {
annonce_req.no_peer_id = true;
}
// 7. TODO: support "compact" extension
// Done parsing, now let's reply
if(m_torrents.contains(annonce_req.info_hash)) {
if(annonce_req.event == "stopped") {
qDebug("QTracker: Peer stopped downloading, deleting it from the list");
m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash());
return;
}
} else {
// Unknown torrent
if(m_torrents.size() == MAX_TORRENTS) {
// Reached max size, remove a random torrent
m_torrents.erase(m_torrents.begin());
}
}
// Register the user
PeerList peers = m_torrents.value(annonce_req.info_hash);
if(peers.size() == MAX_PEERS_PER_TORRENT) {
// Too many peers, remove a random one
peers.erase(peers.begin());
}
peers[annonce_req.peer.qhash()] = annonce_req.peer;
m_torrents[annonce_req.info_hash] = peers;
// Reply
ReplyWithPeerList(socket, annonce_req);
}
void QTracker::ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req)
{
// Prepare the entry for bencoding
entry::dictionary_type reply_dict;
reply_dict["interval"] = entry(ANNOUNCE_INTERVAL);
QList<QPeer> peers = m_torrents.value(annonce_req.info_hash).values();
entry::list_type peer_list;
foreach(const QPeer & p, peers) {
//if(p != annonce_req.peer)
peer_list.push_back(p.toEntry(annonce_req.no_peer_id));
}
reply_dict["peers"] = entry(peer_list);
entry reply_entry(reply_dict);
// bencode
std::vector<char> buf;
bencode(std::back_inserter(buf), reply_entry);
2010-11-01 21:05:54 +03:00
QByteArray reply(&buf[0], buf.size());
qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData());
// HTTP reply
QHttpResponseHeader response;
response.setStatusLine(200, "OK");
socket->write(response.toString().toLocal8Bit() + reply);
socket->disconnectFromHost();
}