summary |
shortlog |
changelog |
graph |
tags |
branches |
files |
changeset |
file |
revisions |
annotate |
diff |
raw
lib/trackertalker.cc
| author | Rink Springer <rink@rink.nu> |
| Sun Jul 31 12:34:24 2011 +0200 (22 months ago ago) | |
| changeset 328 | 89367a44e2dc |
| parent 319 | 99568dedb218 |
| permissions | -rw-r--r-- |
When parsing trackers, revert to the old-style 'announce' URL if 'announce-list' is corrupt
I've seen one torrent which has an empty 'announce-list', causing it to be rejected completely. This may be overly strict, even though the presence of of an empty 'announce-list' should be considered a bug.
I've seen one torrent which has an empty 'announce-list', causing it to be rejected completely. This may be overly strict, even though the presence of of an empty 'announce-list' should be considered a bug.
| rink@304 | 1 |
#include <boost/thread/locks.hpp> |
| rink@270 | 2 |
#include <algorithm> |
| rink@270 | 3 |
#include "trackertalker.h" |
| rink@270 | 4 |
#include "macros.h" |
| rink@270 | 5 |
#include "metafield.h" |
| rink@270 | 6 |
#include "httprequest.h" |
| rink@270 | 7 |
#include "exceptions.h" |
| rink@270 | 8 |
#include "tracer.h" |
| rink@270 | 9 |
|
| rink@270 | 10 |
using namespace std; |
| rink@304 | 11 |
using namespace boost; |
| rink@319 | 12 |
using namespace Tortilla; |
| rink@270 | 13 |
|
| rink@270 | 14 |
#define TRACER (getTracer()) |
| rink@270 | 15 |
|
| rink@307 | 16 |
AnnounceTier::AnnounceTier(TrackerTalker* tt, const MetaList* ml) |
| rink@270 | 17 |
{
|
| rink@270 | 18 |
talker = tt; |
| rink@307 | 19 |
for (list<MetaField*>::const_iterator it = ml->getList().begin(); |
| rink@270 | 20 |
it != ml->getList().end(); it++) {
|
| rink@270 | 21 |
MetaString* ms = dynamic_cast<MetaString*>(*it); |
| rink@270 | 22 |
if (ms == NULL) |
| rink@270 | 23 |
throw TrackerException("announce tear list doesn't contain a string");
|
| rink@270 | 24 |
string trackerURL = ms->getString(); |
| rink@270 | 25 |
trackers.push_back(trackerURL); |
| rink@270 | 26 |
TRACE(TRACKER, "added tracker: %s", trackerURL.c_str()); |
| rink@270 | 27 |
} |
| rink@270 | 28 |
random_shuffle(trackers.begin(), trackers.end()); |
| rink@270 | 29 |
} |
| rink@270 | 30 |
|
| rink@270 | 31 |
AnnounceTier::AnnounceTier(TrackerTalker* tt, std::string url) |
| rink@270 | 32 |
{
|
| rink@270 | 33 |
talker = tt; |
| rink@270 | 34 |
trackers.push_back(url); |
| rink@270 | 35 |
} |
| rink@270 | 36 |
|
| rink@270 | 37 |
void |
| rink@270 | 38 |
AnnounceTier::resetCurrentTracker() |
| rink@270 | 39 |
{
|
| rink@270 | 40 |
currentTracker = 0; |
| rink@270 | 41 |
} |
| rink@270 | 42 |
|
| rink@270 | 43 |
string |
| rink@270 | 44 |
AnnounceTier::getNextTracker() |
| rink@270 | 45 |
{
|
| rink@270 | 46 |
if (currentTracker >= trackers.size()) |
| rink@270 | 47 |
return ""; |
| rink@270 | 48 |
return trackers[currentTracker++]; |
| rink@270 | 49 |
} |
| rink@270 | 50 |
|
| rink@270 | 51 |
void |
| rink@270 | 52 |
AnnounceTier::demoteCurrentTracker() |
| rink@270 | 53 |
{
|
| rink@270 | 54 |
TRACE(TRACKER, "TODO: demote"); |
| rink@270 | 55 |
} |
| rink@270 | 56 |
|
| rink@270 | 57 |
void |
| rink@270 | 58 |
AnnounceTier::promoteCurrentTracker() |
| rink@270 | 59 |
{
|
| rink@270 | 60 |
TRACE(TRACKER, "TODO: promote"); |
| rink@270 | 61 |
} |
| rink@270 | 62 |
|
| rink@270 | 63 |
Tracer* |
| rink@270 | 64 |
AnnounceTier::getTracer() {
|
| rink@270 | 65 |
return talker->getTorrent()->getTracer(); |
| rink@270 | 66 |
} |
| rink@270 | 67 |
|
| rink@270 | 68 |
TrackerTalker::TrackerTalker(Torrent* t, MetaDictionary* dictionary) |
| rink@270 | 69 |
{
|
| rink@270 | 70 |
torrent = t; httpRequest = NULL; |
| rink@270 | 71 |
|
| rink@307 | 72 |
const MetaList* mlList = dynamic_cast<const MetaList*>((*dictionary)["announce-list"]); |
| rink@328 | 73 |
if (mlList != NULL) {
|
| rink@328 | 74 |
/* |
| rink@328 | 75 |
* An announce-list is made up of lists of sublists. Each sublist is an |
| rink@328 | 76 |
* announcement tier, and contains a list of strings which must be shuffeled |
| rink@328 | 77 |
* and stored. Each of the tiers must be tried in-order. |
| rink@328 | 78 |
*/ |
| rink@328 | 79 |
try {
|
| rink@328 | 80 |
for (list<MetaField*>::const_iterator it = mlList->getList().begin(); |
| rink@328 | 81 |
it != mlList->getList().end(); it++) {
|
| rink@328 | 82 |
MetaList* tierList = dynamic_cast<MetaList*>(*it); |
| rink@328 | 83 |
if (tierList == NULL) |
| rink@328 | 84 |
throw TorrentException("announce list doesn't contain a list element");
|
| rink@270 | 85 |
|
| rink@328 | 86 |
tiers.push_back(new AnnounceTier(this, tierList)); |
| rink@328 | 87 |
} |
| rink@328 | 88 |
} catch (TortillaException) {
|
| rink@328 | 89 |
/* |
| rink@328 | 90 |
* Something went wrong while adding tiers; throw them all away and |
| rink@328 | 91 |
* attempt to add the old-style single URL. |
| rink@328 | 92 |
*/ |
| rink@328 | 93 |
for (vector<AnnounceTier*>::iterator it = tiers.begin(); |
| rink@328 | 94 |
it != tiers.end(); it++) {
|
| rink@328 | 95 |
delete (*it); |
| rink@328 | 96 |
} |
| rink@328 | 97 |
tiers.clear(); |
| rink@328 | 98 |
} |
| rink@270 | 99 |
} |
| rink@270 | 100 |
|
| rink@328 | 101 |
/* If we have an announce list, we're good to go */ |
| rink@328 | 102 |
if (!tiers.empty()) |
| rink@328 | 103 |
return; |
| rink@270 | 104 |
|
| rink@328 | 105 |
/* New-style announce list unavailable or corrupt; revert to old single URL */ |
| rink@328 | 106 |
const MetaString* msAnnounce = dynamic_cast<const MetaString*>((*dictionary)["announce"]); |
| rink@328 | 107 |
if (msAnnounce == NULL) |
| rink@328 | 108 |
throw TrackerException("metadata doesn't contain an announce URL or list");
|
| rink@328 | 109 |
|
| rink@328 | 110 |
string url = msAnnounce->getString(); |
| rink@328 | 111 |
TRACE(TRACKER, "added old-style tracker: %s", url.c_str()); |
| rink@328 | 112 |
tiers.push_back(new AnnounceTier(this, url)); |
| rink@270 | 113 |
} |
| rink@270 | 114 |
|
| rink@270 | 115 |
TrackerTalker::~TrackerTalker() |
| rink@270 | 116 |
{
|
| rink@270 | 117 |
for (vector<AnnounceTier*>::iterator it = tiers.begin(); |
| rink@270 | 118 |
it != tiers.end(); it++) {
|
| rink@270 | 119 |
delete (*it); |
| rink@270 | 120 |
} |
| rink@270 | 121 |
} |
| rink@270 | 122 |
|
| rink@270 | 123 |
void |
| rink@270 | 124 |
TrackerTalker::request(map<string, string> req) |
| rink@270 | 125 |
{
|
| rink@270 | 126 |
/* Always start at the first tier with this request */ |
| rink@270 | 127 |
currentTier = 0; currentRequest = req; |
| rink@270 | 128 |
for (vector<AnnounceTier*>::iterator it = tiers.begin(); |
| rink@270 | 129 |
it != tiers.end(); it++) {
|
| rink@270 | 130 |
(*it)->resetCurrentTracker(); |
| rink@270 | 131 |
} |
| rink@270 | 132 |
|
| rink@270 | 133 |
/* Give it a spin */ |
| rink@270 | 134 |
if (!tryRequest()) {
|
| rink@270 | 135 |
/* Wow, this failed extremely quickly - talk to the torrent */ |
| rink@270 | 136 |
torrent->callbackTrackerReply("", true);
|
| rink@270 | 137 |
} |
| rink@270 | 138 |
} |
| rink@270 | 139 |
|
| rink@270 | 140 |
bool |
| rink@270 | 141 |
TrackerTalker::tryRequest() |
| rink@270 | 142 |
{
|
| rink@304 | 143 |
unique_lock<mutex> lock(mtx_data); |
| rink@270 | 144 |
while (true) {
|
| rink@270 | 145 |
/* Find the next announcer to use */ |
| rink@270 | 146 |
if (currentTier >= tiers.size()) |
| rink@270 | 147 |
break; |
| rink@270 | 148 |
AnnounceTier* at = tiers[currentTier]; |
| rink@270 | 149 |
string url = at->getNextTracker(); |
| rink@270 | 150 |
if (url == "") {
|
| rink@270 | 151 |
currentTier++; |
| rink@270 | 152 |
continue; |
| rink@270 | 153 |
} |
| rink@270 | 154 |
|
| rink@270 | 155 |
/* OK, we got an URL - try to chat with it */ |
| rink@304 | 156 |
lock.unlock(); /* drop data lock while talking to tracker */ |
| rink@270 | 157 |
try {
|
| rink@270 | 158 |
TRACE(TRACKER, "attempting to contact tracker '%s'", url.c_str()); |
| rink@270 | 159 |
httpRequest = new HTTPRequest(this, url, currentRequest); |
| rink@270 | 160 |
torrent->addRequest(httpRequest); |
| rink@270 | 161 |
return true; |
| rink@270 | 162 |
} catch (HTTPException e) {
|
| rink@270 | 163 |
TRACE(TRACKER, "unable to communicate with tracker '%s': %s", url.c_str(), e.what()); |
| rink@270 | 164 |
} |
| rink@270 | 165 |
|
| rink@304 | 166 |
/* Fallthrough to the next tracker - reacquire lock */ |
| rink@304 | 167 |
lock.lock(); |
| rink@270 | 168 |
} |
| rink@270 | 169 |
return false; |
| rink@270 | 170 |
} |
| rink@270 | 171 |
|
| rink@270 | 172 |
void |
| rink@270 | 173 |
TrackerTalker::callbackTrackerRequest(std::string result, bool error) |
| rink@270 | 174 |
{
|
| rink@270 | 175 |
TRACE(TRACKER, "tracker callback: error=%u", error ? 1 : 0); |
| rink@270 | 176 |
|
| rink@270 | 177 |
/* Get rid of the request ASAP - the receiver will clean it up */ |
| rink@304 | 178 |
{
|
| rink@304 | 179 |
unique_lock<mutex> lock(mtx_data); |
| rink@304 | 180 |
httpRequest = NULL; |
| rink@304 | 181 |
} |
| rink@270 | 182 |
|
| rink@270 | 183 |
AnnounceTier* at = tiers[currentTier]; |
| rink@270 | 184 |
|
| rink@270 | 185 |
if (error) {
|
| rink@270 | 186 |
at->demoteCurrentTracker(); |
| rink@270 | 187 |
if (!tryRequest()) {
|
| rink@270 | 188 |
/* Give up... */ |
| rink@270 | 189 |
torrent->callbackTrackerReply(result, true); |
| rink@270 | 190 |
} |
| rink@270 | 191 |
return; |
| rink@270 | 192 |
} |
| rink@270 | 193 |
|
| rink@270 | 194 |
at->promoteCurrentTracker(); |
| rink@270 | 195 |
torrent->callbackTrackerReply(result, false); |
| rink@270 | 196 |
} |
| rink@270 | 197 |
|
| rink@270 | 198 |
/* vim:set ts=2 sw=2: */ |