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