source: trunk/libtransmission/announcer-http.c @ 14241

Last change on this file since 14241 was 14241, checked in by jordan, 8 years ago

Copyedit the license's revised text: (1) remove unnecessary repitition use of the word 'license' from the top of the header and source files (2) add the standard 'we hope it's useful, but no warranty' clause to COPYING (3) make explicit that linking OpenSSL is allowed (see https://people.gnome.org/~markmc/openssl-and-the-gpl.html for background) (4) sync the Qt and GTK+ clients' license popups with COPYING's revised text

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