1 /*
2 * This file Copyright (C) 2010-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include <limits.h> /* USHRT_MAX */
10 #include <stdio.h> /* fprintf() */
11 #include <string.h> /* strchr(), memcmp(), memcpy() */
12
13 #include <event2/buffer.h>
14 #include <event2/http.h> /* for HTTP_OK */
15
16 #define __LIBTRANSMISSION_ANNOUNCER_MODULE__
17
18 #include "transmission.h"
19 #include "announcer-common.h"
20 #include "log.h"
21 #include "net.h" /* tr_globalIPv6() */
22 #include "peer-mgr.h" /* pex */
23 #include "torrent.h"
24 #include "trevent.h" /* tr_runInEventThread() */
25 #include "utils.h"
26 #include "variant.h"
27 #include "web.h" /* tr_http_escape() */
28
29 #define dbgmsg(name, ...) tr_logAddDeepNamed(name, __VA_ARGS__)
30
31 /****
32 *****
33 ***** ANNOUNCE
34 *****
35 ****/
36
get_event_string(tr_announce_request const * req)37 static char const* get_event_string(tr_announce_request const* req)
38 {
39 if (req->partial_seed)
40 {
41 if (req->event != TR_ANNOUNCE_EVENT_STOPPED)
42 {
43 return "paused";
44 }
45 }
46
47 return tr_announce_event_get_string(req->event);
48 }
49
announce_url_new(tr_session const * session,tr_announce_request const * req)50 static char* announce_url_new(tr_session const* session, tr_announce_request const* req)
51 {
52 char const* str;
53 unsigned char const* ipv6;
54 struct evbuffer* buf = evbuffer_new();
55 char escaped_info_hash[SHA_DIGEST_LENGTH * 3 + 1];
56
57 tr_http_escape_sha1(escaped_info_hash, req->info_hash);
58
59 evbuffer_expand(buf, 1024);
60
61 evbuffer_add_printf(buf,
62 "%s"
63 "%c"
64 "info_hash=%s"
65 "&peer_id=%*.*s"
66 "&port=%d"
67 "&uploaded=%" PRIu64
68 "&downloaded=%" PRIu64
69 "&left=%" PRIu64
70 "&numwant=%d"
71 "&key=%x"
72 "&compact=1"
73 "&supportcrypto=1",
74 req->url,
75 strchr(req->url, '?') != NULL ? '&' : '?',
76 escaped_info_hash,
77 PEER_ID_LEN, PEER_ID_LEN, req->peer_id,
78 req->port,
79 req->up,
80 req->down,
81 req->leftUntilComplete,
82 req->numwant,
83 req->key);
84
85 if (session->encryptionMode == TR_ENCRYPTION_REQUIRED)
86 {
87 evbuffer_add_printf(buf, "&requirecrypto=1");
88 }
89
90 if (req->corrupt != 0)
91 {
92 evbuffer_add_printf(buf, "&corrupt=%" PRIu64, req->corrupt);
93 }
94
95 str = get_event_string(req);
96
97 if (!tr_str_is_empty(str))
98 {
99 evbuffer_add_printf(buf, "&event=%s", str);
100 }
101
102 str = req->tracker_id_str;
103
104 if (!tr_str_is_empty(str))
105 {
106 evbuffer_add_printf(buf, "&trackerid=%s", str);
107 }
108
109 /* There are two incompatible techniques for announcing an IPv6 address.
110 BEP-7 suggests adding an "ipv6=" parameter to the announce URL,
111 while OpenTracker requires that peers announce twice, once over IPv4
112 and once over IPv6.
113
114 To be safe, we should do both: add the "ipv6=" parameter and
115 announce twice. At any rate, we're already computing our IPv6
116 address (for the LTEP handshake), so this comes for free. */
117
118 ipv6 = tr_globalIPv6();
119
120 if (ipv6 != NULL)
121 {
122 char ipv6_readable[INET6_ADDRSTRLEN];
123 evutil_inet_ntop(AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN);
124 evbuffer_add_printf(buf, "&ipv6=");
125 tr_http_escape(buf, ipv6_readable, TR_BAD_SIZE, true);
126 }
127
128 return evbuffer_free_to_str(buf, NULL);
129 }
130
listToPex(tr_variant * peerList,size_t * setme_len)131 static tr_pex* listToPex(tr_variant* peerList, size_t* setme_len)
132 {
133 size_t n = 0;
134 size_t const len = tr_variantListSize(peerList);
135 tr_pex* pex = tr_new0(tr_pex, len);
136
137 for (size_t i = 0; i < len; ++i)
138 {
139 int64_t port;
140 char const* ip;
141 tr_address addr;
142 tr_variant* peer = tr_variantListChild(peerList, i);
143
144 if (peer == NULL)
145 {
146 continue;
147 }
148
149 if (!tr_variantDictFindStr(peer, TR_KEY_ip, &ip, NULL))
150 {
151 continue;
152 }
153
154 if (!tr_address_from_string(&addr, ip))
155 {
156 continue;
157 }
158
159 if (!tr_variantDictFindInt(peer, TR_KEY_port, &port))
160 {
161 continue;
162 }
163
164 if (port < 0 || port > USHRT_MAX)
165 {
166 continue;
167 }
168
169 if (!tr_address_is_valid_for_peers(&addr, port))
170 {
171 continue;
172 }
173
174 pex[n].addr = addr;
175 pex[n].port = htons((uint16_t)port);
176 ++n;
177 }
178
179 *setme_len = n;
180 return pex;
181 }
182
183 struct announce_data
184 {
185 tr_announce_response response;
186 tr_announce_response_func response_func;
187 void* response_func_user_data;
188 char log_name[128];
189 };
190
on_announce_done_eventthread(void * vdata)191 static void on_announce_done_eventthread(void* vdata)
192 {
193 struct announce_data* data = vdata;
194
195 if (data->response_func != NULL)
196 {
197 data->response_func(&data->response, data->response_func_user_data);
198 }
199
200 tr_free(data->response.pex6);
201 tr_free(data->response.pex);
202 tr_free(data->response.tracker_id_str);
203 tr_free(data->response.warning);
204 tr_free(data->response.errmsg);
205 tr_free(data);
206 }
207
on_announce_done(tr_session * session,bool did_connect,bool did_timeout,long response_code,void const * msg,size_t msglen,void * vdata)208 static void on_announce_done(tr_session* session, bool did_connect, bool did_timeout, long response_code, void const* msg,
209 size_t msglen, void* vdata)
210 {
211 tr_announce_response* response;
212 struct announce_data* data = vdata;
213
214 response = &data->response;
215 response->did_connect = did_connect;
216 response->did_timeout = did_timeout;
217 dbgmsg(data->log_name, "Got announce response");
218
219 if (response_code != HTTP_OK)
220 {
221 char const* fmt = _("Tracker gave HTTP response code %1$ld (%2$s)");
222 char const* response_str = tr_webGetResponseStr(response_code);
223 response->errmsg = tr_strdup_printf(fmt, response_code, response_str);
224 }
225 else
226 {
227 tr_variant benc;
228 bool const variant_loaded = tr_variantFromBenc(&benc, msg, msglen) == 0;
229
230 if (tr_env_key_exists("TR_CURL_VERBOSE"))
231 {
232 if (!variant_loaded)
233 {
234 fprintf(stderr, "%s", "Announce response was not in benc format\n");
235 }
236 else
237 {
238 size_t len;
239 char* str = tr_variantToStr(&benc, TR_VARIANT_FMT_JSON, &len);
240 fprintf(stderr, "%s", "Announce response:\n< ");
241
242 for (size_t i = 0; i < len; ++i)
243 {
244 fputc(str[i], stderr);
245 }
246
247 fputc('\n', stderr);
248 tr_free(str);
249 }
250 }
251
252 if (variant_loaded && tr_variantIsDict(&benc))
253 {
254 int64_t i;
255 size_t len;
256 tr_variant* tmp;
257 char const* str;
258 uint8_t const* raw;
259
260 if (tr_variantDictFindStr(&benc, TR_KEY_failure_reason, &str, &len))
261 {
262 response->errmsg = tr_strndup(str, len);
263 }
264
265 if (tr_variantDictFindStr(&benc, TR_KEY_warning_message, &str, &len))
266 {
267 response->warning = tr_strndup(str, len);
268 }
269
270 if (tr_variantDictFindInt(&benc, TR_KEY_interval, &i))
271 {
272 response->interval = i;
273 }
274
275 if (tr_variantDictFindInt(&benc, TR_KEY_min_interval, &i))
276 {
277 response->min_interval = i;
278 }
279
280 if (tr_variantDictFindStr(&benc, TR_KEY_tracker_id, &str, &len))
281 {
282 response->tracker_id_str = tr_strndup(str, len);
283 }
284
285 if (tr_variantDictFindInt(&benc, TR_KEY_complete, &i))
286 {
287 response->seeders = i;
288 }
289
290 if (tr_variantDictFindInt(&benc, TR_KEY_incomplete, &i))
291 {
292 response->leechers = i;
293 }
294
295 if (tr_variantDictFindInt(&benc, TR_KEY_downloaded, &i))
296 {
297 response->downloads = i;
298 }
299
300 if (tr_variantDictFindRaw(&benc, TR_KEY_peers6, &raw, &len))
301 {
302 dbgmsg(data->log_name, "got a peers6 length of %zu", len);
303 response->pex6 = tr_peerMgrCompact6ToPex(raw, len, NULL, 0, &response->pex6_count);
304 }
305
306 if (tr_variantDictFindRaw(&benc, TR_KEY_peers, &raw, &len))
307 {
308 dbgmsg(data->log_name, "got a compact peers length of %zu", len);
309 response->pex = tr_peerMgrCompactToPex(raw, len, NULL, 0, &response->pex_count);
310 }
311 else if (tr_variantDictFindList(&benc, TR_KEY_peers, &tmp))
312 {
313 response->pex = listToPex(tmp, &response->pex_count);
314 dbgmsg(data->log_name, "got a peers list with %zu entries", response->pex_count);
315 }
316 }
317
318 if (variant_loaded)
319 {
320 tr_variantFree(&benc);
321 }
322 }
323
324 tr_runInEventThread(session, on_announce_done_eventthread, data);
325 }
326
tr_tracker_http_announce(tr_session * session,tr_announce_request const * request,tr_announce_response_func response_func,void * response_func_user_data)327 void tr_tracker_http_announce(tr_session* session, tr_announce_request const* request, tr_announce_response_func response_func,
328 void* response_func_user_data)
329 {
330 struct announce_data* d;
331 char* url = announce_url_new(session, request);
332
333 d = tr_new0(struct announce_data, 1);
334 d->response.seeders = -1;
335 d->response.leechers = -1;
336 d->response.downloads = -1;
337 d->response_func = response_func;
338 d->response_func_user_data = response_func_user_data;
339 memcpy(d->response.info_hash, request->info_hash, SHA_DIGEST_LENGTH);
340 tr_strlcpy(d->log_name, request->log_name, sizeof(d->log_name));
341
342 dbgmsg(request->log_name, "Sending announce to libcurl: \"%s\"", url);
343 tr_webRun(session, url, on_announce_done, d);
344
345 tr_free(url);
346 }
347
348 /****
349 *****
350 ***** SCRAPE
351 *****
352 ****/
353
354 struct scrape_data
355 {
356 tr_scrape_response response;
357 tr_scrape_response_func response_func;
358 void* response_func_user_data;
359 char log_name[128];
360 };
361
on_scrape_done_eventthread(void * vdata)362 static void on_scrape_done_eventthread(void* vdata)
363 {
364 struct scrape_data* data = vdata;
365
366 if (data->response_func != NULL)
367 {
368 data->response_func(&data->response, data->response_func_user_data);
369 }
370
371 tr_free(data->response.errmsg);
372 tr_free(data->response.url);
373 tr_free(data);
374 }
375
on_scrape_done(tr_session * session,bool did_connect,bool did_timeout,long response_code,void const * msg,size_t msglen,void * vdata)376 static void on_scrape_done(tr_session* session, bool did_connect, bool did_timeout, long response_code, void const* msg,
377 size_t msglen, void* vdata)
378 {
379 tr_scrape_response* response;
380 struct scrape_data* data = vdata;
381
382 response = &data->response;
383 response->did_connect = did_connect;
384 response->did_timeout = did_timeout;
385 dbgmsg(data->log_name, "Got scrape response for \"%s\"", response->url);
386
387 if (response_code != HTTP_OK)
388 {
389 char const* fmt = _("Tracker gave HTTP response code %1$ld (%2$s)");
390 char const* response_str = tr_webGetResponseStr(response_code);
391 response->errmsg = tr_strdup_printf(fmt, response_code, response_str);
392 }
393 else
394 {
395 tr_variant top;
396 int64_t intVal;
397 tr_variant* files;
398 tr_variant* flags;
399 size_t len;
400 char const* str;
401 bool const variant_loaded = tr_variantFromBenc(&top, msg, msglen) == 0;
402
403 if (tr_env_key_exists("TR_CURL_VERBOSE"))
404 {
405 if (!variant_loaded)
406 {
407 fprintf(stderr, "%s", "Scrape response was not in benc format\n");
408 }
409 else
410 {
411 size_t len;
412 char* str = tr_variantToStr(&top, TR_VARIANT_FMT_JSON, &len);
413 fprintf(stderr, "%s", "Scrape response:\n< ");
414
415 for (size_t i = 0; i < len; ++i)
416 {
417 fputc(str[i], stderr);
418 }
419
420 fputc('\n', stderr);
421 tr_free(str);
422 }
423 }
424
425 if (variant_loaded)
426 {
427 if (tr_variantDictFindStr(&top, TR_KEY_failure_reason, &str, &len))
428 {
429 response->errmsg = tr_strndup(str, len);
430 }
431
432 if (tr_variantDictFindDict(&top, TR_KEY_flags, &flags))
433 {
434 if (tr_variantDictFindInt(flags, TR_KEY_min_request_interval, &intVal))
435 {
436 response->min_request_interval = intVal;
437 }
438 }
439
440 if (tr_variantDictFindDict(&top, TR_KEY_files, &files))
441 {
442 tr_quark key;
443 tr_variant* val;
444
445 for (int i = 0; tr_variantDictChild(files, i, &key, &val); ++i)
446 {
447 /* populate the corresponding row in our response array */
448 for (int j = 0; j < response->row_count; ++j)
449 {
450 struct tr_scrape_response_row* row = &response->rows[j];
451
452 if (memcmp(tr_quark_get_string(key, NULL), row->info_hash, SHA_DIGEST_LENGTH) == 0)
453 {
454 if (tr_variantDictFindInt(val, TR_KEY_complete, &intVal))
455 {
456 row->seeders = intVal;
457 }
458
459 if (tr_variantDictFindInt(val, TR_KEY_incomplete, &intVal))
460 {
461 row->leechers = intVal;
462 }
463
464 if (tr_variantDictFindInt(val, TR_KEY_downloaded, &intVal))
465 {
466 row->downloads = intVal;
467 }
468
469 if (tr_variantDictFindInt(val, TR_KEY_downloaders, &intVal))
470 {
471 row->downloaders = intVal;
472 }
473
474 break;
475 }
476 }
477 }
478 }
479
480 tr_variantFree(&top);
481 }
482 }
483
484 tr_runInEventThread(session, on_scrape_done_eventthread, data);
485 }
486
scrape_url_new(tr_scrape_request const * req)487 static char* scrape_url_new(tr_scrape_request const* req)
488 {
489 char delimiter;
490 struct evbuffer* buf = evbuffer_new();
491
492 evbuffer_add_printf(buf, "%s", req->url);
493 delimiter = strchr(req->url, '?') != NULL ? '&' : '?';
494
495 for (int i = 0; i < req->info_hash_count; ++i)
496 {
497 char str[SHA_DIGEST_LENGTH * 3 + 1];
498 tr_http_escape_sha1(str, req->info_hash[i]);
499 evbuffer_add_printf(buf, "%cinfo_hash=%s", delimiter, str);
500 delimiter = '&';
501 }
502
503 return evbuffer_free_to_str(buf, NULL);
504 }
505
tr_tracker_http_scrape(tr_session * session,tr_scrape_request const * request,tr_scrape_response_func response_func,void * response_func_user_data)506 void tr_tracker_http_scrape(tr_session* session, tr_scrape_request const* request, tr_scrape_response_func response_func,
507 void* response_func_user_data)
508 {
509 struct scrape_data* d;
510 char* url = scrape_url_new(request);
511
512 d = tr_new0(struct scrape_data, 1);
513 d->response.url = tr_strdup(request->url);
514 d->response_func = response_func;
515 d->response_func_user_data = response_func_user_data;
516 d->response.row_count = request->info_hash_count;
517
518 for (int i = 0; i < d->response.row_count; ++i)
519 {
520 memcpy(d->response.rows[i].info_hash, request->info_hash[i], SHA_DIGEST_LENGTH);
521 d->response.rows[i].seeders = -1;
522 d->response.rows[i].leechers = -1;
523 d->response.rows[i].downloads = -1;
524 }
525
526 tr_strlcpy(d->log_name, request->log_name, sizeof(d->log_name));
527
528 dbgmsg(request->log_name, "Sending scrape to libcurl: \"%s\"", url);
529 tr_webRun(session, url, on_scrape_done, d);
530
531 tr_free(url);
532 }
533