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

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

(trunk libT) copyediting: '#include "crypto.h"' cleanup

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