source: trunk/libtransmission/announcer-http.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

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