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 (9 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.
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: */