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