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

Last change on this file since 13191 was 13191, checked in by jordan, 10 years ago

(trunk) remove trailing spaces from code lines ;)

  • Property svn:keywords set to Date Rev Author Id
File size: 15.4 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: announcer-http.c 13191 2012-02-03 16:44:07Z jordan $
11 */
12
13#include <limits.h> /* USHRT_MAX */
14#include <stdio.h> /* fprintf() */
15#include <stdlib.h> /* getenv() */
16#include <string.h> /* strchr(), memcmp(), memcpy() */
17
18#include <event2/buffer.h>
19#include <event2/http.h> /* for HTTP_OK */
20
21#define __LIBTRANSMISSION_ANNOUNCER_MODULE___
22
23#include "transmission.h"
24#include "announcer-common.h"
25#include "net.h" /* tr_globalIPv6() */
26#include "peer-mgr.h" /* pex */
27#include "torrent.h"
28#include "trevent.h" /* tr_runInEventThread() */
29#include "utils.h"
30#include "web.h" /* tr_http_escape() */
31
32#define dbgmsg( name, ... ) \
33if( tr_deepLoggingIsActive( ) ) do { \
34  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
35} while( 0 )
36
37/****
38*****
39*****  ANNOUNCE
40*****
41****/
42
43static const char*
44get_event_string( const tr_announce_request * req )
45{
46    if( req->partial_seed )
47        if( req->event != TR_ANNOUNCE_EVENT_STOPPED )
48            return "paused";
49
50    return tr_announce_event_get_string( req->event );
51}
52
53static struct evbuffer *
54announce_url_new( const tr_session * session, const tr_announce_request * req )
55{
56    const char * str;
57    const unsigned char * ipv6;
58    struct evbuffer * buf = evbuffer_new( );
59    char escaped_info_hash[SHA_DIGEST_LENGTH*3 + 1];
60
61    tr_http_escape_sha1( escaped_info_hash, req->info_hash );
62
63    evbuffer_expand( buf, 1024 );
64
65    evbuffer_add_printf( buf, "%s"
66                              "%c"
67                              "info_hash=%s"
68                              "&peer_id=%*.*s"
69                              "&port=%d"
70                              "&uploaded=%" PRIu64
71                              "&downloaded=%" PRIu64
72                              "&left=%" PRIu64
73                              "&numwant=%d"
74                              "&key=%x"
75                              "&compact=1"
76                              "&supportcrypto=1",
77                              req->url,
78                              strchr( req->url, '?' ) ? '&' : '?',
79                              escaped_info_hash,
80                              PEER_ID_LEN, PEER_ID_LEN, req->peer_id,
81                              req->port,
82                              req->up,
83                              req->down,
84                              req->left,
85                              req->numwant,
86                              req->key );
87
88    if( session->encryptionMode == TR_ENCRYPTION_REQUIRED )
89        evbuffer_add_printf( buf, "&requirecrypto=1" );
90
91    if( req->corrupt )
92        evbuffer_add_printf( buf, "&corrupt=%" PRIu64, req->corrupt );
93
94    str = get_event_string( req );
95    if( str && *str )
96        evbuffer_add_printf( buf, "&event=%s", str );
97
98    str = req->tracker_id_str;
99    if( str && *str )
100        evbuffer_add_printf( buf, "&trackerid=%s", str );
101
102    /* There are two incompatible techniques for announcing an IPv6 address.
103       BEP-7 suggests adding an "ipv6=" parameter to the announce URL,
104       while OpenTracker requires that peers announce twice, once over IPv4
105       and once over IPv6.
106
107       To be safe, we should do both: add the "ipv6=" parameter and
108       announce twice. At any rate, we're already computing our IPv6
109       address (for the LTEP handshake), so this comes for free. */
110
111    ipv6 = tr_globalIPv6( );
112    if( ipv6 ) {
113        char ipv6_readable[INET6_ADDRSTRLEN];
114        evutil_inet_ntop( AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN );
115        evbuffer_add_printf( buf, "&ipv6=");
116        tr_http_escape( buf, ipv6_readable, -1, true );
117    }
118
119    return buf;
120}
121
122static tr_pex*
123listToPex( tr_benc * peerList, size_t * setme_len )
124{
125    size_t i;
126    size_t n;
127    const size_t len = tr_bencListSize( peerList );
128    tr_pex * pex = tr_new0( tr_pex, len );
129
130    for( i=n=0; i<len; ++i )
131    {
132        int64_t port;
133        const char * ip;
134        tr_address addr;
135        tr_benc * peer = tr_bencListChild( peerList, i );
136
137        if( peer == NULL )
138            continue;
139        if( !tr_bencDictFindStr( peer, "ip", &ip ) )
140            continue;
141        if( !tr_address_from_string( &addr, ip ) )
142            continue;
143        if( !tr_bencDictFindInt( peer, "port", &port ) )
144            continue;
145        if( ( port < 0 ) || ( port > USHRT_MAX ) )
146            continue;
147        if( !tr_address_is_valid_for_peers( &addr, port ) )
148            continue;
149
150        pex[n].addr = addr;
151        pex[n].port = htons( (uint16_t)port );
152        ++n;
153    }
154
155    *setme_len = n;
156    return pex;
157}
158
159struct announce_data
160{
161    tr_announce_response response;
162    tr_announce_response_func * response_func;
163    void * response_func_user_data;
164    char log_name[128];
165};
166
167static void
168on_announce_done_eventthread( void * vdata )
169{
170    struct announce_data * data = vdata;
171
172    if( data->response_func != NULL )
173        data->response_func( &data->response, data->response_func_user_data );
174
175    tr_free( data->response.pex6 );
176    tr_free( data->response.pex );
177    tr_free( data->response.tracker_id_str );
178    tr_free( data->response.warning );
179    tr_free( data->response.errmsg );
180    tr_free( data );
181}
182
183
184static void
185on_announce_done( tr_session   * session,
186                  bool           did_connect,
187                  bool           did_timeout,
188                  long           response_code,
189                  const void   * msg,
190                  size_t         msglen,
191                  void         * vdata )
192{
193    tr_announce_response * response;
194    struct announce_data * data = vdata;
195
196    response = &data->response;
197    response->did_connect = did_connect;
198    response->did_timeout = did_timeout;
199    dbgmsg( data->log_name, "Got announce response" );
200
201    if( response_code != HTTP_OK )
202    {
203        const char * fmt = _( "Tracker gave HTTP response code %1$ld (%2$s)" );
204        const char * response_str = tr_webGetResponseStr( response_code );
205        response->errmsg = tr_strdup_printf( fmt, response_code, response_str );
206    }
207    else
208    {
209        tr_benc benc;
210        const int benc_loaded = !tr_bencLoad( msg, msglen, &benc, NULL );
211
212        if( getenv( "TR_CURL_VERBOSE" ) != NULL )
213        {
214            if( !benc_loaded )
215                fprintf( stderr, "%s", "Announce response was not in benc format\n" );
216            else {
217                int i, len;
218                char * str = tr_bencToStr( &benc, TR_FMT_JSON, &len );
219                fprintf( stderr, "%s", "Announce response:\n< " );
220                for( i=0; i<len; ++i )
221                    fputc( str[i], stderr );
222                fputc( '\n', stderr );
223                tr_free( str );
224            }
225        }
226
227        if( benc_loaded && tr_bencIsDict( &benc ) )
228        {
229            int64_t i;
230            size_t rawlen;
231            tr_benc * tmp;
232            const char * str;
233            const uint8_t * raw;
234
235            if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
236                response->errmsg = tr_strdup( str );
237
238            if( tr_bencDictFindStr( &benc, "warning message", &str ) )
239                response->warning = tr_strdup( str );
240
241            if( tr_bencDictFindInt( &benc, "interval", &i ) )
242                response->interval = i;
243
244            if( tr_bencDictFindInt( &benc, "min interval", &i ) )
245                response->min_interval = i;
246
247            if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
248                response->tracker_id_str = tr_strdup( str );
249
250            if( tr_bencDictFindInt( &benc, "complete", &i ) )
251                response->seeders = i;
252
253            if( tr_bencDictFindInt( &benc, "incomplete", &i ) )
254                response->leechers = i;
255
256            if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
257                response->downloads = i;
258
259            if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) ) {
260                dbgmsg( data->log_name, "got a peers6 length of %zu", rawlen );
261                response->pex6 = tr_peerMgrCompact6ToPex( raw, rawlen,
262                                              NULL, 0, &response->pex6_count );
263            }
264
265            if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) ) {
266                dbgmsg( data->log_name, "got a compact peers length of %zu", rawlen );
267                response->pex = tr_peerMgrCompactToPex( raw, rawlen,
268                                               NULL, 0, &response->pex_count );
269            } else if( tr_bencDictFindList( &benc, "peers", &tmp ) ) {
270                response->pex = listToPex( tmp, &response->pex_count );
271                dbgmsg( data->log_name, "got a peers list with %zu entries",
272                        response->pex_count );
273            }
274        }
275
276        if( benc_loaded )
277            tr_bencFree( &benc );
278    }
279
280    tr_runInEventThread( session, on_announce_done_eventthread, data );
281}
282
283void
284tr_tracker_http_announce( tr_session                 * session,
285                          const tr_announce_request  * request,
286                          tr_announce_response_func    response_func,
287                          void                       * response_func_user_data )
288{
289    struct announce_data * d;
290    struct evbuffer * buf = announce_url_new( session, request );
291    const char * url = (const char *) evbuffer_pullup( buf, -1 );
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, NULL, NULL, on_announce_done, d );
304
305    evbuffer_free( buf );
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_benc top;
361        int64_t intVal;
362        tr_benc * files;
363        tr_benc * flags;
364        const char * str;
365        const int benc_loaded = !tr_bencLoad( msg, msglen, &top, NULL );
366
367        if( getenv( "TR_CURL_VERBOSE" ) != NULL )
368        {
369            if( !benc_loaded )
370                fprintf( stderr, "%s", "Scrape response was not in benc format\n" );
371            else {
372                int i, len;
373                char * str = tr_bencToStr( &top, TR_FMT_JSON, &len );
374                fprintf( stderr, "%s", "Scrape response:\n< " );
375                for( i=0; i<len; ++i )
376                    fputc( str[i], stderr );
377                fputc( '\n', stderr );
378                tr_free( str );
379            }
380        }
381
382        if( benc_loaded )
383        {
384            if( tr_bencDictFindStr( &top, "failure reason", &str ) )
385                response->errmsg = tr_strdup( str );
386
387            if( tr_bencDictFindDict( &top, "flags", &flags ) )
388                if( tr_bencDictFindInt( flags, "min_request_interval", &intVal ) )
389                    response->min_request_interval = intVal;
390
391            if( tr_bencDictFindDict( &top, "files", &files ) )
392            {
393                int i = 0;
394
395                for( ;; )
396                {
397                    int j;
398                    tr_benc * val;
399                    const char * key;
400
401                    /* get the next "file" */
402                    if( !tr_bencDictChild( files, i++, &key, &val ) )
403                        break;
404
405                    /* populate the corresponding row in our response array */
406                    for( j=0; j<response->row_count; ++j )
407                    {
408                        struct tr_scrape_response_row * row = &response->rows[j];
409                        if( !memcmp( key, row->info_hash, SHA_DIGEST_LENGTH ) )
410                        {
411                            if( tr_bencDictFindInt( val, "complete", &intVal ) )
412                                row->seeders = intVal;
413                            if( tr_bencDictFindInt( val, "incomplete", &intVal ) )
414                                row->leechers = intVal;
415                            if( tr_bencDictFindInt( val, "downloaded", &intVal ) )
416                                row->downloads = intVal;
417                            if( tr_bencDictFindInt( val, "downloaders", &intVal ) )
418                                row->downloaders = intVal;
419                            break;
420                        }
421                    }
422                }
423            }
424
425            tr_bencFree( &top );
426        }
427    }
428
429    tr_runInEventThread( session, on_scrape_done_eventthread, data );
430}
431
432static struct evbuffer *
433scrape_url_new( const tr_scrape_request * req )
434{
435    int i;
436    char delimiter;
437    struct evbuffer * buf = evbuffer_new( );
438
439    evbuffer_add_printf( buf, "%s", req->url );
440    delimiter = strchr( req->url, '?' ) ? '&' : '?';
441    for( i=0; i<req->info_hash_count; ++i )
442    {
443        char str[SHA_DIGEST_LENGTH*3 + 1];
444        tr_http_escape_sha1( str, req->info_hash[i] );
445        evbuffer_add_printf( buf, "%cinfo_hash=%s", delimiter, str );
446        delimiter = '&';
447    }
448
449    return buf;
450}
451
452void
453tr_tracker_http_scrape( tr_session               * session,
454                        const tr_scrape_request  * request,
455                        tr_scrape_response_func    response_func,
456                        void                     * response_func_user_data )
457{
458    int i;
459    struct scrape_data * d;
460    struct evbuffer * buf = scrape_url_new( request );
461    const char * url = (const char *) evbuffer_pullup( buf, -1 );
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, NULL, NULL, on_scrape_done, d );
479
480    evbuffer_free( buf );
481}
Note: See TracBrowser for help on using the repository browser.