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

Last change on this file since 12150 was 12150, checked in by jordan, 11 years ago

(trunk libT) tweak #includes in announcer-http.c

File size: 14.2 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:$
11 */
12
13#include <limits.h> /* USHRT_MAX */
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 "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 "web.h" /* tr_http_escape() */
28
29#define dbgmsg( name, ... ) \
30if( tr_deepLoggingIsActive( ) ) do { \
31  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
32} while( 0 )
33
34/****
35*****
36*****  ANNOUNCE
37*****
38****/
39
40static const char*
41get_event_string( const tr_announce_request * req )
42{
43    if( req->partial_seed )
44        if( req->event != TR_ANNOUNCE_EVENT_STOPPED )
45            return "paused";
46
47    return tr_announce_event_get_string( req->event );
48}
49
50static char*
51announce_url_new( const tr_session * session, const tr_announce_request * req )
52{
53    const char * str;
54    const unsigned char * ipv6;
55    struct evbuffer * buf = evbuffer_new( );
56    char escaped_info_hash[SHA_DIGEST_LENGTH*3 + 1];
57
58    tr_http_escape_sha1( escaped_info_hash, req->info_hash );
59
60    evbuffer_expand( buf, 1024 );
61
62    evbuffer_add_printf( buf, "%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, '?' ) ? '&' : '?',
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->left,
82                              req->numwant,
83                              req->key );
84
85    if( session->encryptionMode == TR_ENCRYPTION_REQUIRED )
86        evbuffer_add_printf( buf, "&requirecrypto=1" );
87
88    if( req->corrupt )
89        evbuffer_add_printf( buf, "&corrupt=%" PRIu64, req->corrupt );
90
91    str = get_event_string( req );
92    if( str && *str )
93        evbuffer_add_printf( buf, "&event=%s", str );
94
95    str = req->tracker_id_str;
96    if( str && *str )
97        evbuffer_add_printf( buf, "&trackerid=%s", str );
98
99    /* There are two incompatible techniques for announcing an IPv6 address.
100       BEP-7 suggests adding an "ipv6=" parameter to the announce URL,
101       while OpenTracker requires that peers announce twice, once over IPv4
102       and once over IPv6.
103
104       To be safe, we should do both: add the "ipv6=" parameter and
105       announce twice. At any rate, we're already computing our IPv6
106       address (for the LTEP handshake), so this comes for free. */
107
108    ipv6 = tr_globalIPv6( );
109    if( ipv6 ) {
110        char ipv6_readable[INET6_ADDRSTRLEN];
111        evutil_inet_ntop( AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN );
112        evbuffer_add_printf( buf, "&ipv6=");
113        tr_http_escape( buf, ipv6_readable, -1, TRUE );
114    }
115
116    return evbuffer_free_to_str( buf );
117}
118
119static tr_pex*
120listToPex( tr_benc * peerList, size_t * setme_len )
121{
122    size_t i;
123    size_t n;
124    const size_t len = tr_bencListSize( peerList );
125    tr_pex * pex = tr_new0( tr_pex, len );
126
127    for( i=n=0; i<len; ++i )
128    {
129        int64_t port;
130        const char * ip;
131        tr_address addr;
132        tr_benc * peer = tr_bencListChild( peerList, i );
133
134        if( peer == NULL )
135            continue;
136        if( !tr_bencDictFindStr( peer, "ip", &ip ) )
137            continue;
138        if( tr_pton( ip, &addr ) == NULL )
139            continue;
140        if( !tr_bencDictFindInt( peer, "port", &port ) )
141            continue;
142        if( ( port < 0 ) || ( port > USHRT_MAX ) )
143            continue;
144        if( !tr_isValidPeerAddress( &addr, port ) )
145            continue;
146
147        pex[n].addr = addr;
148        pex[n].port = htons( (uint16_t)port );
149        ++n;
150    }
151
152    *setme_len = n;
153    return pex;
154}
155
156struct announce_data
157{
158    tr_session * session;
159    tr_announce_response response;
160    tr_announce_response_func * response_func;
161    void * response_func_user_data;
162    char log_name[128];
163};
164
165static void
166on_announce_done_eventthread( void * vdata )
167{
168    struct announce_data * data = vdata;
169
170    if( data->response_func != NULL )
171        data->response_func( data->session,
172                             &data->response,
173                             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                  tr_bool        did_connect,
187                  tr_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            char * str = tr_bencToStr( &benc, TR_FMT_JSON, NULL );
215            fprintf( stderr, "Announce response:\n< %s\n", str );
216            tr_free( str );
217        }
218
219        if( benc_loaded && tr_bencIsDict( &benc ) )
220        {
221            int64_t i;
222            size_t rawlen;
223            tr_benc * tmp;
224            const char * str;
225            const uint8_t * raw;
226
227            if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
228                response->errmsg = tr_strdup( str );
229
230            if( tr_bencDictFindStr( &benc, "warning message", &str ) )
231                response->warning = tr_strdup( str );
232
233            if( tr_bencDictFindInt( &benc, "interval", &i ) )
234                response->interval = i;
235
236            if( tr_bencDictFindInt( &benc, "min interval", &i ) )
237                response->min_interval = i;
238
239            if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
240                response->tracker_id_str = tr_strdup( str );
241
242            if( tr_bencDictFindInt( &benc, "complete", &i ) )
243                response->seeders = i;
244
245            if( tr_bencDictFindInt( &benc, "incomplete", &i ) )
246                response->leechers = i;
247
248            if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
249                response->downloads = i;
250
251            if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) ) {
252                dbgmsg( data->log_name, "got a peers6 length of %zu", rawlen );
253                response->pex6 = tr_peerMgrCompact6ToPex( raw, rawlen,
254                                              NULL, 0, &response->pex6_count );
255            }
256
257            if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) ) {
258                dbgmsg( data->log_name, "got a compact peers length of %zu", rawlen );
259                response->pex = tr_peerMgrCompactToPex( raw, rawlen,
260                                               NULL, 0, &response->pex_count );
261            } else if( tr_bencDictFindList( &benc, "peers", &tmp ) ) {
262                response->pex = listToPex( tmp, &response->pex_count );
263                dbgmsg( data->log_name, "got a peers list with %zu entries",
264                        response->pex_count );
265            }
266        }
267
268        if( benc_loaded )
269            tr_bencFree( &benc );
270    }
271
272    tr_runInEventThread( session, on_announce_done_eventthread, data );
273}
274
275void
276tr_tracker_http_announce( tr_session                 * session,
277                          const tr_announce_request  * request,
278                          tr_announce_response_func    response_func,
279                          void                       * response_func_user_data )
280{
281    struct announce_data * d;
282    char * url = announce_url_new( session, request );
283
284    d = tr_new0( struct announce_data, 1 );
285    d->session = session;
286    d->response_func = response_func;
287    d->response_func_user_data = response_func_user_data;
288    memcpy( d->response.info_hash, request->info_hash, SHA_DIGEST_LENGTH );
289    tr_strlcpy( d->log_name, request->log_name, sizeof( d->log_name ) );
290
291    dbgmsg( request->log_name, "Sending announce to libcurl: \"%s\"", url );
292    tr_webRun( session, url, NULL, on_announce_done, d );
293    tr_free( url );
294}
295
296/****
297*****
298*****  SCRAPE
299*****
300****/
301
302struct scrape_data
303{
304    tr_session * session;
305    tr_scrape_response response;
306    tr_scrape_response_func * response_func;
307    void * response_func_user_data;
308    char log_name[128];
309};
310
311static void
312on_scrape_done_eventthread( void * vdata )
313{
314    struct scrape_data * data = vdata;
315
316    if( data->response_func != NULL )
317        data->response_func( data->session,
318                             &data->response,
319                             data->response_func_user_data );
320
321    tr_free( data->response.errmsg );
322    tr_free( data->response.url );
323    tr_free( data );
324}
325
326static void
327on_scrape_done( tr_session   * session,
328                tr_bool        did_connect,
329                tr_bool        did_timeout,
330                long           response_code,
331                const void   * msg,
332                size_t         msglen,
333                void         * vdata )
334{
335    tr_scrape_response * response;
336    struct scrape_data * data = vdata;
337
338    response = &data->response;
339    response->did_connect = did_connect;
340    response->did_timeout = did_timeout;
341    dbgmsg( data->log_name, "Got scrape response for \"%s\"", response->url );
342
343    if( response_code != HTTP_OK )
344    {
345        const char * fmt = _( "Tracker gave HTTP response code %1$ld (%2$s)" );
346        const char * response_str = tr_webGetResponseStr( response_code );
347        response->errmsg = tr_strdup_printf( fmt, response_code, response_str );
348    }
349    else
350    {
351        tr_benc top;
352        int64_t intVal;
353        tr_benc * files;
354        const char * str;
355        const int benc_loaded = !tr_bencLoad( msg, msglen, &top, NULL );
356        if( benc_loaded )
357        {
358            if( tr_bencDictFindStr( &top, "failure reason", &str ) )
359                response->errmsg = tr_strdup( str );
360
361            if( tr_bencDictFindInt( &top, "min_request_interval", &intVal ) )
362                response->min_request_interval = intVal;
363
364            if( tr_bencDictFindDict( &top, "files", &files ) )
365            {
366                int i = 0;
367
368                for( ;; )
369                {
370                    int j;
371                    tr_benc * val;
372                    const char * key;
373
374                    /* get the next "file" */
375                    if( !tr_bencDictChild( files, i++, &key, &val ) )
376                        break;
377
378                    /* populate the corresponding row in our response array */
379                    for( j=0; j<response->row_count; ++j )
380                    {
381                        struct tr_scrape_response_row * row = &response->rows[j];
382                        if( !memcmp( key, row->info_hash, SHA_DIGEST_LENGTH ) )
383                        {
384                            if( tr_bencDictFindInt( val, "complete", &intVal ) )
385                                row->seeders = intVal;
386                            if( tr_bencDictFindInt( val, "incomplete", &intVal ) )
387                                row->leechers = intVal;
388                            if( tr_bencDictFindInt( val, "downloaded", &intVal ) )
389                                row->downloads = intVal;
390                            if( tr_bencDictFindInt( val, "downloaders", &intVal ) )
391                                row->downloaders = intVal;
392                            break;
393                        }
394                    }
395                }
396            }
397
398            tr_bencFree( &top );
399        }
400    }
401
402    tr_runInEventThread( session, on_scrape_done_eventthread, data );
403}
404
405static char*
406scrape_url_new( const tr_scrape_request * req )
407{
408    int i;
409    char delimiter;
410    struct evbuffer * buf = evbuffer_new( );
411
412    evbuffer_add_printf( buf, "%s", req->url );
413    delimiter = strchr( req->url, '?' ) ? '&' : '?';
414    for( i=0; i<req->info_hash_count; ++i )
415    {
416        char str[SHA_DIGEST_LENGTH*3 + 1];
417        tr_http_escape_sha1( str, req->info_hash[i] );
418        evbuffer_add_printf( buf, "%cinfo_hash=%s", delimiter, str );
419        delimiter = '&';
420    }
421
422    return evbuffer_free_to_str( buf );
423}
424
425void
426tr_tracker_http_scrape( tr_session               * session,
427                        const tr_scrape_request  * request,
428                        tr_scrape_response_func    response_func,
429                        void                     * response_func_user_data )
430{
431    int i;
432    struct scrape_data * d;
433    char * url = scrape_url_new( request );
434
435    d = tr_new0( struct scrape_data, 1 );
436    d->session = session;
437    d->response.url = tr_strdup( request->url );
438    d->response_func = response_func;
439    d->response_func_user_data = response_func_user_data;
440    d->response.row_count = request->info_hash_count;
441    for( i=0; i<d->response.row_count; ++i )
442        memcpy( d->response.rows[i].info_hash, request->info_hash[i], SHA_DIGEST_LENGTH );
443    tr_strlcpy( d->log_name, request->log_name, sizeof( d->log_name ) );
444
445    dbgmsg( request->log_name, "Sending scrape to libcurl: \"%s\"", url );
446    tr_webRun( session, url, NULL, on_scrape_done, d );
447    tr_free( url );
448}
Note: See TracBrowser for help on using the repository browser.