source: trunk/libtransmission/announcer-udp.c @ 12982

Last change on this file since 12982 was 12982, checked in by jordan, 9 years ago

(trunk libT) #4526 "do not skip scrape unless all three scrape fields came with announce" -- add field initializers.

  • Property svn:keywords set to Date Rev Author Id
File size: 28.8 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-udp.c 12982 2011-10-14 00:27:14Z jordan $
11 */
12
13#define __LIBTRANSMISSION_ANNOUNCER_MODULE___
14
15#include <string.h> /* memcpy(), memset() */
16
17#include <event2/buffer.h>
18#include <event2/dns.h>
19#include <event2/util.h>
20
21#include "transmission.h"
22#include "announcer.h"
23#include "announcer-common.h"
24#include "crypto.h" /* tr_cryptoRandBuf() */
25#include "peer-io.h"
26#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
27#include "ptrarray.h"
28#include "tr-udp.h"
29#include "utils.h"
30
31#define dbgmsg( name, ... ) \
32if( tr_deepLoggingIsActive( ) ) do { \
33  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
34} while( 0 )
35
36/****
37*****
38****/
39
40static void
41tau_sockaddr_setport( struct sockaddr * sa, tr_port port )
42{
43    if( sa->sa_family == AF_INET )
44        ((struct sockaddr_in *)sa)->sin_port = htons(port);
45    else if (sa->sa_family == AF_INET6)
46        ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
47}
48
49static int
50tau_sendto( tr_session * session,
51            struct evutil_addrinfo * ai, tr_port port,
52            const void * buf, size_t buflen )
53{
54    int sockfd;
55
56    if( ai->ai_addr->sa_family == AF_INET )
57        sockfd = session->udp_socket;
58    else if( ai->ai_addr->sa_family == AF_INET6 )
59        sockfd = session->udp6_socket;
60    else
61        sockfd = -1;
62
63    if( sockfd < 0 ) {
64        errno = EAFNOSUPPORT;
65        return -1;
66    }
67
68    tau_sockaddr_setport( ai->ai_addr, port );
69    return sendto( sockfd, buf, buflen, 0, ai->ai_addr, ai->ai_addrlen );
70}
71
72/****
73*****
74****/
75
76static uint32_t
77evbuffer_read_ntoh_32( struct evbuffer * buf )
78{
79    uint32_t val;
80    evbuffer_remove( buf, &val, sizeof( uint32_t ) );
81    return ntohl( val );
82}
83
84static uint64_t
85evbuffer_read_ntoh_64( struct evbuffer * buf )
86{
87    uint64_t val;
88    evbuffer_remove( buf, &val, sizeof( uint64_t ) );
89    return tr_ntohll( val );
90}
91
92/****
93*****
94****/
95
96typedef uint64_t tau_connection_t;
97
98enum
99{
100    TAU_CONNECTION_TTL_SECS = 60
101};
102
103typedef uint32_t tau_transaction_t;
104
105static tau_transaction_t
106tau_transaction_new( void )
107{
108    tau_transaction_t tmp;
109    tr_cryptoRandBuf( &tmp, sizeof( tau_transaction_t ) );
110    return tmp;
111}
112
113/* used in the "action" field of a request */
114typedef enum
115{
116    TAU_ACTION_CONNECT  = 0,
117    TAU_ACTION_ANNOUNCE = 1,
118    TAU_ACTION_SCRAPE   = 2,
119    TAU_ACTION_ERROR    = 3
120}
121tau_action_t;
122
123static bool
124is_tau_response_message( int action, int msglen )
125{
126    if( action == TAU_ACTION_CONNECT  ) return msglen == 16;
127    if( action == TAU_ACTION_ANNOUNCE ) return msglen >= 20;
128    if( action == TAU_ACTION_SCRAPE   ) return msglen >= 20;
129    if( action == TAU_ACTION_ERROR    ) return msglen >= 8;
130    return false;
131}
132
133enum
134{
135    TAU_REQUEST_TTL = 60
136};
137
138/****
139*****
140*****  SCRAPE
141*****
142****/
143
144struct tau_scrape_request
145{
146    void * payload;
147    size_t payload_len;
148
149    time_t sent_at;
150    time_t created_at;
151    tau_transaction_t transaction_id;
152
153    tr_scrape_response response;
154    tr_scrape_response_func * callback;
155    void * user_data;
156};
157
158static struct tau_scrape_request *
159tau_scrape_request_new( const tr_scrape_request  * in,
160                        tr_scrape_response_func    callback,
161                        void                     * user_data )
162{
163    int i;
164    struct evbuffer * buf;
165    struct tau_scrape_request * req;
166    const tau_transaction_t transaction_id = tau_transaction_new( );
167
168    /* build the payload */
169    buf = evbuffer_new( );
170    evbuffer_add_hton_32( buf, TAU_ACTION_SCRAPE );
171    evbuffer_add_hton_32( buf, transaction_id );
172    for( i=0; i<in->info_hash_count; ++i )
173        evbuffer_add( buf, in->info_hash[i], SHA_DIGEST_LENGTH );
174
175    /* build the tau_scrape_request */
176    req = tr_new0( struct tau_scrape_request, 1 );
177    req->created_at = tr_time( );
178    req->transaction_id = transaction_id;
179    req->callback = callback;
180    req->user_data = user_data;
181    req->response.url = tr_strdup( in->url );
182    req->response.row_count = in->info_hash_count;
183    req->payload_len = evbuffer_get_length( buf );
184    req->payload = tr_memdup( evbuffer_pullup( buf, -1 ), req->payload_len );
185    for( i=0; i<req->response.row_count; ++i )
186    {
187        req->response.rows[i].seeders = -1;
188        req->response.rows[i].leechers = -1;
189        req->response.rows[i].downloads = -1;
190        memcpy( req->response.rows[i].info_hash,
191                in->info_hash[i], SHA_DIGEST_LENGTH );
192    }
193
194    /* cleanup */
195    evbuffer_free( buf );
196    return req;
197}
198
199static void
200tau_scrape_request_free( struct tau_scrape_request * req )
201{
202    tr_free( req->response.errmsg );
203    tr_free( req->response.url );
204    tr_free( req->payload );
205    tr_free( req );
206}
207
208static void
209tau_scrape_request_finished( const struct tau_scrape_request  * request )
210{
211    if( request->callback != NULL )
212        request->callback( &request->response, request->user_data );
213}
214
215static void
216tau_scrape_request_fail( struct tau_scrape_request  * request,
217                         bool                         did_connect,
218                         bool                         did_timeout,
219                         const char                 * errmsg )
220{
221    request->response.did_connect = did_connect;
222    request->response.did_timeout = did_timeout;
223    request->response.errmsg = tr_strdup( errmsg );
224    tau_scrape_request_finished( request );
225}
226
227static void
228on_scrape_response( struct tau_scrape_request  * request,
229                    tau_action_t                 action,
230                    struct evbuffer            * buf )
231{
232    request->response.did_connect = true;
233    request->response.did_timeout = false;
234
235    if( action == TAU_ACTION_SCRAPE )
236    {
237        int i;
238        for( i=0; i<request->response.row_count; ++i )
239        {
240            struct tr_scrape_response_row * row;
241
242            if( evbuffer_get_length( buf ) < ( sizeof( uint32_t ) * 3 ) )
243                break;
244
245            row = &request->response.rows[i];
246            row->seeders   = evbuffer_read_ntoh_32( buf );
247            row->downloads = evbuffer_read_ntoh_32( buf );
248            row->leechers  = evbuffer_read_ntoh_32( buf );
249        }
250
251        tau_scrape_request_finished( request );
252    }
253    else
254    {
255        char * errmsg;
256        const size_t buflen = evbuffer_get_length( buf );
257
258        if( ( action == TAU_ACTION_ERROR ) && ( buflen > 0 ) )
259            errmsg = tr_strndup( evbuffer_pullup( buf, -1 ), buflen );
260        else
261            errmsg = tr_strdup( _( "Unknown error" ) );
262
263        tau_scrape_request_fail( request, true, false, errmsg );
264        tr_free( errmsg );
265    }
266}
267
268/****
269*****
270*****  ANNOUNCE
271*****
272****/
273
274struct tau_announce_request
275{
276    void * payload;
277    size_t payload_len;
278
279    time_t created_at;
280    time_t sent_at;
281    tau_transaction_t transaction_id;
282
283    tr_announce_response response;
284    tr_announce_response_func * callback;
285    void * user_data;
286};
287
288typedef enum
289{
290    /* used in the "event" field of an announce request */
291    TAU_ANNOUNCE_EVENT_NONE      = 0,
292    TAU_ANNOUNCE_EVENT_COMPLETED = 1,
293    TAU_ANNOUNCE_EVENT_STARTED   = 2,
294    TAU_ANNOUNCE_EVENT_STOPPED   = 3
295}
296tau_announce_event;
297
298static tau_announce_event
299get_tau_announce_event( tr_announce_event e )
300{
301    switch( e )
302    {
303        case TR_ANNOUNCE_EVENT_COMPLETED: return TAU_ANNOUNCE_EVENT_COMPLETED;
304        case TR_ANNOUNCE_EVENT_STARTED:   return TAU_ANNOUNCE_EVENT_STARTED;
305        case TR_ANNOUNCE_EVENT_STOPPED:   return TAU_ANNOUNCE_EVENT_STOPPED;
306        default:                          return TAU_ANNOUNCE_EVENT_NONE;
307    }
308}
309
310static struct tau_announce_request *
311tau_announce_request_new( const tr_announce_request  * in,
312                          tr_announce_response_func    callback,
313                          void                       * user_data )
314{
315    struct evbuffer * buf;
316    struct tau_announce_request * req;
317    const tau_transaction_t transaction_id = tau_transaction_new( );
318
319    /* build the payload */
320    buf = evbuffer_new( );
321    evbuffer_add_hton_32( buf, TAU_ACTION_ANNOUNCE );
322    evbuffer_add_hton_32( buf, transaction_id );
323    evbuffer_add        ( buf, in->info_hash, SHA_DIGEST_LENGTH );
324    evbuffer_add        ( buf, in->peer_id, PEER_ID_LEN );
325    evbuffer_add_hton_64( buf, in->down );
326    evbuffer_add_hton_64( buf, in->left );
327    evbuffer_add_hton_64( buf, in->up );
328    evbuffer_add_hton_32( buf, get_tau_announce_event( in->event ) );
329    evbuffer_add_hton_32( buf, 0 );
330    evbuffer_add_hton_32( buf, in->key );
331    evbuffer_add_hton_32( buf, in->numwant );
332    evbuffer_add_hton_16( buf, in->port );
333
334    /* build the tau_announce_request */
335    req = tr_new0( struct tau_announce_request, 1 );
336    req->created_at = tr_time( );
337    req->transaction_id = transaction_id;
338    req->callback = callback;
339    req->user_data = user_data;
340    req->payload_len = evbuffer_get_length( buf );
341    req->payload = tr_memdup( evbuffer_pullup( buf, -1 ), req->payload_len );
342    req->response.seeders = -1;
343    req->response.leechers = -1;
344    req->response.downloads = -1;
345    memcpy( req->response.info_hash, in->info_hash, SHA_DIGEST_LENGTH );
346
347    evbuffer_free( buf );
348    return req;
349}
350
351static void
352tau_announce_request_free( struct tau_announce_request * req )
353{
354    tr_free( req->response.tracker_id_str );
355    tr_free( req->response.warning );
356    tr_free( req->response.errmsg );
357    tr_free( req->response.pex6 );
358    tr_free( req->response.pex );
359    tr_free( req->payload );
360    tr_free( req );
361}
362
363static void
364tau_announce_request_finished( const struct tau_announce_request * request )
365{
366    if( request->callback != NULL )
367        request->callback( &request->response, request->user_data );
368}
369
370static void
371tau_announce_request_fail( struct tau_announce_request  * request,
372                           bool                           did_connect,
373                           bool                           did_timeout,
374                           const char                   * errmsg )
375{
376    request->response.did_connect = did_connect;
377    request->response.did_timeout = did_timeout;
378    request->response.errmsg = tr_strdup( errmsg );
379    tau_announce_request_finished( request );
380}
381
382static void
383on_announce_response( struct tau_announce_request  * request,
384                      tau_action_t                   action,
385                      struct evbuffer              * buf )
386{
387    const size_t buflen = evbuffer_get_length( buf );
388
389    request->response.did_connect = true;
390    request->response.did_timeout = false;
391
392    if( ( action == TAU_ACTION_ANNOUNCE ) && ( buflen >= 3*sizeof(uint32_t) ) )
393    {
394        tr_announce_response * resp = &request->response;
395        resp->interval = evbuffer_read_ntoh_32( buf );
396        resp->leechers = evbuffer_read_ntoh_32( buf );
397        resp->seeders  = evbuffer_read_ntoh_32( buf );
398        resp->pex = tr_peerMgrCompactToPex( evbuffer_pullup( buf, -1 ),
399                                            evbuffer_get_length( buf ),
400                                            NULL, 0,
401                                            &request->response.pex_count );
402        tau_announce_request_finished( request );
403    }
404    else
405    {
406        char * errmsg;
407
408        if( ( action == TAU_ACTION_ERROR ) && ( buflen > 0 ) )
409            errmsg = tr_strndup( evbuffer_pullup( buf, -1 ), buflen );
410        else
411            errmsg = tr_strdup( _( "Unknown error" ) );
412
413        tau_announce_request_fail( request, true, false, errmsg );
414        tr_free( errmsg );
415    }
416}
417
418/****
419*****
420*****  TRACKERS
421*****
422****/
423
424struct tau_tracker
425{
426    tr_session * session;
427
428    char * key;
429    char * host;
430    int port;
431
432    bool is_asking_dns;
433    struct evutil_addrinfo * addr;
434    time_t addr_expiration_time;
435
436    time_t connecting_at;
437    time_t connection_expiration_time;
438    tau_connection_t connection_id;
439    tau_transaction_t connection_transaction_id;
440
441    time_t close_at;
442
443    tr_ptrArray announces;
444    tr_ptrArray scrapes;
445};
446
447static void tau_tracker_upkeep( struct tau_tracker * );
448
449static void
450tau_tracker_free( struct tau_tracker * t )
451{
452    if( t->addr )
453        evutil_freeaddrinfo( t->addr );
454    tr_ptrArrayDestruct( &t->announces, (PtrArrayForeachFunc)tau_announce_request_free );
455    tr_ptrArrayDestruct( &t->scrapes, (PtrArrayForeachFunc)tau_scrape_request_free );
456    tr_free( t->host );
457    tr_free( t->key );
458    tr_free( t );
459}
460
461static void
462tau_tracker_fail_all( struct tau_tracker  * tracker,
463                      bool                  did_connect,
464                      bool                  did_timeout,
465                      const char          * errmsg )
466{
467    int i;
468    int n;
469    tr_ptrArray * reqs;
470
471    /* fail all the scrapes */
472    reqs = &tracker->scrapes;
473    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i )
474        tau_scrape_request_fail( tr_ptrArrayNth( reqs, i ),
475                                 did_connect, did_timeout, errmsg );
476    tr_ptrArrayDestruct( reqs, (PtrArrayForeachFunc)tau_scrape_request_free );
477    *reqs = TR_PTR_ARRAY_INIT;
478
479    /* fail all the announces */
480    reqs = &tracker->announces;
481    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i )
482        tau_announce_request_fail( tr_ptrArrayNth( reqs, i ),
483                                   did_connect, did_timeout, errmsg );
484    tr_ptrArrayDestruct( reqs, (PtrArrayForeachFunc)tau_announce_request_free );
485    *reqs = TR_PTR_ARRAY_INIT;
486
487}
488
489static void
490tau_tracker_on_dns( int errcode, struct evutil_addrinfo *addr, void * vtracker )
491{
492    struct tau_tracker * tracker = vtracker;
493
494    tracker->is_asking_dns = false;
495
496    if ( errcode )
497    {
498        char * errmsg = tr_strdup_printf( _( "DNS Lookup failed: %s" ),
499                                          evdns_err_to_string( errcode ) );
500        dbgmsg( tracker->key, "%s", errmsg );
501        tau_tracker_fail_all( tracker, false, false, errmsg );
502        tr_free( errmsg );
503    }
504    else
505    {
506        dbgmsg( tracker->key, "DNS lookup succeeded" );
507        tracker->addr = addr;
508        tracker->addr_expiration_time = tr_time() + (60*60); /* one hour */
509        tau_tracker_upkeep( tracker );
510    }
511}
512
513static void
514tau_tracker_send_request( struct tau_tracker  * tracker,
515                          const void          * payload,
516                          size_t                payload_len )
517{
518    struct evbuffer * buf = evbuffer_new( );
519    dbgmsg( tracker->key, "sending request w/connection id %"PRIu64"\n",
520                          tracker->connection_id );
521    evbuffer_add_hton_64( buf, tracker->connection_id );
522    evbuffer_add_reference( buf, payload, payload_len, NULL, NULL );
523    tau_sendto( tracker->session, tracker->addr, tracker->port,
524                evbuffer_pullup( buf, -1 ),
525                evbuffer_get_length( buf ) );
526    evbuffer_free( buf );
527}
528
529static void
530tau_tracker_send_reqs( struct tau_tracker * tracker )
531{
532    int i, n;
533    tr_ptrArray * reqs;
534    const time_t now = tr_time( );
535
536    assert( tracker->is_asking_dns == false );
537    assert( tracker->connecting_at == 0 );
538    assert( tracker->addr != NULL );
539    assert( tracker->connection_expiration_time > now );
540
541    reqs = &tracker->announces;
542    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i ) {
543        struct tau_announce_request * req = tr_ptrArrayNth( reqs, i );
544        if( !req->sent_at ) {
545            dbgmsg( tracker->key, "sending announce req %p", req );
546            req->sent_at = now;
547            tau_tracker_send_request( tracker, req->payload, req->payload_len );
548            if( req->callback == NULL ) {
549                tau_announce_request_free( req );
550                tr_ptrArrayRemove( reqs, i );
551                --i;
552                --n;
553            }
554        }
555    }
556
557    reqs = &tracker->scrapes;
558    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i ) {
559        struct tau_scrape_request * req = tr_ptrArrayNth( reqs, i );
560        if( !req->sent_at ) {
561            dbgmsg( tracker->key, "sending scrape req %p", req );
562            req->sent_at = now;
563            tau_tracker_send_request( tracker, req->payload, req->payload_len );
564            if( req->callback == NULL ) {
565                tau_scrape_request_free( req );
566                tr_ptrArrayRemove( reqs, i );
567                --i;
568                --n;
569            }
570        }
571    }
572}
573
574static void
575on_tracker_connection_response( struct tau_tracker  * tracker,
576                                tau_action_t          action,
577                                struct evbuffer     * buf )
578{
579    const time_t now = tr_time( );
580
581    tracker->connecting_at = 0;
582    tracker->connection_transaction_id = 0;
583
584    if( action == TAU_ACTION_CONNECT )
585    {
586        tracker->connection_id = evbuffer_read_ntoh_64( buf );
587        tracker->connection_expiration_time = now + TAU_CONNECTION_TTL_SECS;
588        dbgmsg( tracker->key, "Got a new connection ID from tracker: %"PRIu64,
589                tracker->connection_id );
590    }
591    else
592    {
593        char * errmsg;
594        const size_t buflen = buf ? evbuffer_get_length( buf ) : 0;
595
596        if( ( action == TAU_ACTION_ERROR ) && ( buflen > 0 ) )
597            errmsg = tr_strndup( evbuffer_pullup( buf, -1 ), buflen );
598        else
599            errmsg = tr_strdup( _( "Connection failed" ) );
600
601        dbgmsg( tracker->key, "%s", errmsg );
602        tau_tracker_fail_all( tracker, true, false, errmsg );
603        tr_free( errmsg );
604    }
605
606    tau_tracker_upkeep( tracker );
607}
608
609static void
610tau_tracker_timeout_reqs( struct tau_tracker * tracker )
611{
612    int i, n;
613    tr_ptrArray * reqs;
614    const time_t now = time( NULL );
615    const bool cancel_all = tracker->close_at && ( tracker->close_at <= now );
616
617
618    if( tracker->connecting_at && ( tracker->connecting_at + TAU_REQUEST_TTL < now ) ) {
619        on_tracker_connection_response( tracker, TAU_ACTION_ERROR, NULL );
620    }
621
622    reqs = &tracker->announces;
623    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i ) {
624        struct tau_announce_request * req = tr_ptrArrayNth( reqs, i );
625        if( cancel_all || ( req->created_at + TAU_REQUEST_TTL < now ) ) {
626            dbgmsg( tracker->key, "timeout announce req %p", req );
627            tau_announce_request_fail( req, false, true, NULL );
628            tau_announce_request_free( req );
629            tr_ptrArrayRemove( reqs, i );
630            --i;
631            --n;
632        }
633    }
634
635    reqs = &tracker->scrapes;
636    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i ) {
637        struct tau_scrape_request * req = tr_ptrArrayNth( reqs, i );
638        if( cancel_all || ( req->created_at + TAU_REQUEST_TTL < now ) ) {
639            dbgmsg( tracker->key, "timeout scrape req %p", req );
640            tau_scrape_request_fail( req, false, true, NULL );
641            tau_scrape_request_free( req );
642            tr_ptrArrayRemove( reqs, i );
643            --i;
644            --n;
645        }
646    }
647}
648
649static bool
650tau_tracker_is_idle( const struct tau_tracker * tracker )
651{
652    return tr_ptrArrayEmpty( &tracker->announces )
653        && tr_ptrArrayEmpty( &tracker->scrapes );
654}
655
656static void
657tau_tracker_upkeep( struct tau_tracker * tracker )
658{
659    const time_t now = tr_time( );
660
661    /* if the address info is too old, expire it */
662    if( tracker->addr && ( tracker->addr_expiration_time <= now ) ) {
663        dbgmsg( tracker->host, "Expiring old DNS result" );
664        evutil_freeaddrinfo( tracker->addr );
665        tracker->addr = NULL;
666    }
667
668    /* are there any requests pending? */
669    if( tau_tracker_is_idle( tracker ) )
670        return;
671
672    /* if we don't have an address yet, try & get one now. */
673    if( !tracker->addr && !tracker->is_asking_dns )
674    {
675        struct evutil_addrinfo hints;
676        memset( &hints, 0, sizeof( hints ) );
677        hints.ai_family = AF_UNSPEC;
678        hints.ai_flags = EVUTIL_AI_CANONNAME;
679        hints.ai_socktype = SOCK_DGRAM;
680        hints.ai_protocol = IPPROTO_UDP;
681        tracker->is_asking_dns = true;
682        dbgmsg( tracker->host, "Trying a new DNS lookup" );
683        evdns_getaddrinfo( tracker->session->evdns_base,
684                           tracker->host, NULL, &hints,
685                           tau_tracker_on_dns, tracker );
686        return;
687    }
688
689    dbgmsg( tracker->key, "addr %p -- connected %d (%zu %zu) -- connecting_at %zu",
690            tracker->addr,
691            (int)(tracker->connection_expiration_time > now), (size_t)tracker->connection_expiration_time, (size_t)now,
692            (size_t)tracker->connecting_at );
693
694    /* also need a valid connection ID... */
695    if( tracker->addr
696        && ( tracker->connection_expiration_time <= now )
697        && ( !tracker->connecting_at ) )
698    {
699        struct evbuffer * buf = evbuffer_new( );
700        tracker->connecting_at = now;
701        tracker->connection_transaction_id = tau_transaction_new( );
702        dbgmsg( tracker->key, "Trying to connect. Transaction ID is %u",
703                tracker->connection_transaction_id );
704        evbuffer_add_hton_64( buf, 0x41727101980LL );
705        evbuffer_add_hton_32( buf, TAU_ACTION_CONNECT );
706        evbuffer_add_hton_32( buf, tracker->connection_transaction_id );
707        tau_sendto( tracker->session, tracker->addr, tracker->port,
708                    evbuffer_pullup( buf, -1 ),
709                    evbuffer_get_length( buf ) );
710        evbuffer_free( buf );
711        return;
712    }
713
714    tau_tracker_timeout_reqs( tracker );
715
716    if( ( tracker->addr != NULL ) && ( tracker->connection_expiration_time > now ) )
717        tau_tracker_send_reqs( tracker );
718}
719
720/****
721*****
722*****  SESSION
723*****
724****/
725
726struct tr_announcer_udp
727{
728    /* tau_tracker */
729    tr_ptrArray trackers;
730
731    tr_session * session;
732};
733
734static struct tr_announcer_udp*
735announcer_udp_get( tr_session * session )
736{
737    struct tr_announcer_udp * tau;
738
739    if( session->announcer_udp != NULL )
740        return session->announcer_udp;
741
742    tau = tr_new0( struct tr_announcer_udp, 1 );
743    tau->trackers = TR_PTR_ARRAY_INIT;
744    tau->session = session;
745    session->announcer_udp = tau;
746    return tau;
747}
748
749/* Finds the tau_tracker struct that corresponds to this url.
750   If it doesn't exist yet, create one. */
751static struct tau_tracker *
752tau_session_get_tracker( struct tr_announcer_udp * tau, const char * url )
753{
754    int i;
755    int n;
756    int port;
757    char * host;
758    char * key;
759    struct tau_tracker * tracker = NULL;
760
761    /* see if we've already got a tracker that matches this host + port */
762    tr_urlParse( url, -1, NULL, &host, &port, NULL );
763    key = tr_strdup_printf( "%s:%d", host, port );
764    for( i=0, n=tr_ptrArraySize( &tau->trackers ); !tracker && i<n; ++i ) {
765        struct tau_tracker * tmp = tr_ptrArrayNth( &tau->trackers, i );
766        if( !tr_strcmp0( tmp->key, key ) )
767            tracker = tmp;
768    }
769
770    /* if we don't have a match, build a new tracker */
771    if( tracker == NULL )
772    {
773        tracker = tr_new0( struct tau_tracker, 1 );
774        tracker->session = tau->session;
775        tracker->key = key;
776        tracker->host = host;
777        tracker->port = port;
778        tracker->scrapes = TR_PTR_ARRAY_INIT;
779        tracker->announces = TR_PTR_ARRAY_INIT;
780        tr_ptrArrayAppend( &tau->trackers, tracker );
781        dbgmsg( tracker->key, "New tau_tracker created" );
782    }
783    else
784    {
785        tr_free( key );
786        tr_free( host );
787    }
788
789    return tracker;
790}
791
792/****
793*****
794*****  PUBLIC API
795*****
796****/
797
798void
799tr_tracker_udp_upkeep( tr_session * session )
800{
801    struct tr_announcer_udp * tau = session->announcer_udp;
802
803    if( tau != NULL )
804        tr_ptrArrayForeach( &tau->trackers,
805                            (PtrArrayForeachFunc)tau_tracker_upkeep );
806}
807
808bool
809tr_tracker_udp_is_idle( const tr_session * session )
810{
811    int i;
812    int n;
813    struct tr_announcer_udp * tau = session->announcer_udp;
814
815    if( tau != NULL )
816        for( i=0, n=tr_ptrArraySize(&tau->trackers); i<n; ++i )
817            if( !tau_tracker_is_idle( tr_ptrArrayNth( &tau->trackers, i ) ) )
818                return false;
819
820    return true;
821}
822
823/* drop dead now. */
824void
825tr_tracker_udp_close( tr_session * session )
826{
827    struct tr_announcer_udp * tau = session->announcer_udp;
828
829    if( tau != NULL )
830    {
831        session->announcer_udp = NULL;
832        tr_ptrArrayDestruct( &tau->trackers, (PtrArrayForeachFunc)tau_tracker_free );
833        tr_free( tau );
834    }
835}
836
837/* start shutting down.
838   This doesn't destroy everything if there are requests,
839   but sets a deadline on how much longer to wait for the remaining ones */
840void
841tr_tracker_udp_start_shutdown( tr_session * session )
842{
843    const time_t now = time( NULL );
844    struct tr_announcer_udp * tau = session->announcer_udp;
845
846    if( tau != NULL )
847    {
848        int i, n;
849        for( i=0, n=tr_ptrArraySize(&tau->trackers); i<n; ++i )
850        {
851            struct tau_tracker * tracker = tr_ptrArrayNth( &tau->trackers, i );
852            tracker->close_at = now + 3;
853            tau_tracker_upkeep( tracker );
854        }
855    }
856}
857
858/* @brief process an incoming udp message if it's a tracker response.
859 * @return true if msg was a tracker response; false otherwise */
860bool
861tau_handle_message( tr_session * session, const uint8_t * msg, size_t msglen )
862{
863    int i;
864    int n;
865    struct tr_announcer_udp * tau;
866    tau_action_t action_id;
867    tau_transaction_t transaction_id;
868    struct evbuffer * buf;
869
870    /*fprintf( stderr, "got an incoming udp message w/len %zu\n", msglen );*/
871
872    if( !session || !session->announcer_udp )
873        return false;
874    if( msglen < (sizeof(uint32_t)*2) )
875        return false;
876
877    /* extract the action_id and see if it makes sense */
878    buf = evbuffer_new( );
879    evbuffer_add_reference( buf, msg, msglen, NULL, NULL );
880    action_id = evbuffer_read_ntoh_32( buf );
881    if( !is_tau_response_message( action_id, msglen ) ) {
882        evbuffer_free( buf );
883        return false;
884    }
885
886    /* extract the transaction_id and look for a match */
887    tau = session->announcer_udp;
888    transaction_id = evbuffer_read_ntoh_32( buf );
889    /*fprintf( stderr, "UDP got a transaction_id %u...\n", transaction_id );*/
890    for( i=0, n=tr_ptrArraySize( &tau->trackers ); i<n; ++i )
891    {
892        int j, jn;
893        tr_ptrArray * reqs;
894        struct tau_tracker * tracker = tr_ptrArrayNth( &tau->trackers, i );
895
896        /* is it a connection response? */
897        if( tracker->connecting_at
898            && ( transaction_id == tracker->connection_transaction_id ) )
899        {
900            dbgmsg( tracker->key, "%"PRIu32" is my connection request!", transaction_id );
901            on_tracker_connection_response( tracker, action_id, buf );
902            evbuffer_free( buf );
903            return true;
904        }
905
906        /* is it a response to one of this tracker's announces? */
907        reqs = &tracker->announces;
908        for( j=0, jn=tr_ptrArraySize(reqs); j<jn; ++j ) {
909            struct tau_announce_request * req = tr_ptrArrayNth( reqs, j );
910            if( req->sent_at && ( transaction_id == req->transaction_id ) ) {
911                dbgmsg( tracker->key, "%"PRIu32" is an announce request!", transaction_id );
912                tr_ptrArrayRemove( reqs, j );
913                on_announce_response( req, action_id, buf );
914                tau_announce_request_free( req );
915                evbuffer_free( buf );
916                return true;
917            }
918        }
919
920        /* is it a response to one of this tracker's scrapes? */
921        reqs = &tracker->scrapes;
922        for( j=0, jn=tr_ptrArraySize(reqs); j<jn; ++j ) {
923            struct tau_scrape_request * req = tr_ptrArrayNth( reqs, j );
924            if( req->sent_at && ( transaction_id == req->transaction_id ) ) {
925                dbgmsg( tracker->key, "%"PRIu32" is a scrape request!", transaction_id );
926                tr_ptrArrayRemove( reqs, j );
927                on_scrape_response( req, action_id, buf );
928                tau_scrape_request_free( req );
929                evbuffer_free( buf );
930                return true;
931            }
932        }
933    }
934
935    /* no match... */
936    evbuffer_free( buf );
937    return false;
938}
939
940void
941tr_tracker_udp_announce( tr_session                 * session,
942                         const tr_announce_request  * request,
943                         tr_announce_response_func    response_func,
944                         void                       * user_data )
945{
946    struct tr_announcer_udp * tau = announcer_udp_get( session );
947    struct tau_tracker * tracker = tau_session_get_tracker( tau, request->url );
948    struct tau_announce_request * r = tau_announce_request_new( request,
949                                                                response_func,
950                                                                user_data );
951    tr_ptrArrayAppend( &tracker->announces, r );
952    tau_tracker_upkeep( tracker );
953}
954
955void
956tr_tracker_udp_scrape( tr_session               * session,
957                       const tr_scrape_request  * request,
958                       tr_scrape_response_func    response_func,
959                       void                     * user_data )
960{
961    struct tr_announcer_udp * tau = announcer_udp_get( session );
962    struct tau_tracker * tracker = tau_session_get_tracker( tau, request->url );
963    struct tau_scrape_request * r = tau_scrape_request_new( request,
964                                                            response_func,
965                                                            user_data );
966    tr_ptrArrayAppend( &tracker->scrapes, r );
967    tau_tracker_upkeep( tracker );
968}
Note: See TracBrowser for help on using the repository browser.