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

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

(trunk libT) better shutdown management of libutp and UDP trackers in tr_sessionClose().

This is a little overlapping since the utp code can be closed more-or-less immediately, but the udp manager needs to stay open in order to process the udp tracker connection requests before sending out event=stopped. Moreover DNS resolver can be shut down after the UDP tracker is shutdown.

  • Property svn:keywords set to Date Rev Author Id
File size: 27.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 12184 2011-03-17 18:51: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"
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 tr_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                         tr_bool                      did_connect,
215                         tr_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                           tr_bool                        did_connect,
370                           tr_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    tr_bool is_asking_dns;
431    struct evutil_addrinfo * addr;
432    time_t addr_expiration_time;
433
434    tr_bool is_connecting;
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                      tr_bool               did_connect,
462                      tr_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 tr_bool
528tau_tracker_is_empty( const struct tau_tracker * tracker )
529{
530    return tr_ptrArrayEmpty( &tracker->announces )
531        && tr_ptrArrayEmpty( &tracker->scrapes );
532}
533
534static void
535tau_tracker_upkeep( struct tau_tracker * tracker )
536{
537    int i;
538    int n;
539    tr_ptrArray * reqs;
540    const time_t now = tr_time( );
541    const tr_bool is_connected = tracker->connection_expiration_time > now;
542
543    /* if the address info is too old, expire it */
544    if( tracker->addr && ( tracker->addr_expiration_time <= now ) ) {
545        dbgmsg( tracker->host, "Expiring old DNS result" );     
546        evutil_freeaddrinfo( tracker->addr );
547        tracker->addr = NULL;
548    }
549
550    /* are there any requests pending? */
551    if( tau_tracker_is_empty( tracker ) )
552        return;
553
554    /* if we don't have an address yet, try & get one now. */
555    if( !tracker->addr && !tracker->is_asking_dns )
556    {
557        struct evutil_addrinfo hints;
558        memset( &hints, 0, sizeof( hints ) );
559        hints.ai_family = AF_UNSPEC;
560        hints.ai_flags = EVUTIL_AI_CANONNAME;
561        hints.ai_socktype = SOCK_DGRAM;
562        hints.ai_protocol = IPPROTO_UDP;
563        tracker->is_asking_dns = TRUE;
564        dbgmsg( tracker->host, "Trying a new DNS lookup" );     
565        evdns_getaddrinfo( tracker->session->evdns_base,
566                           tracker->host, NULL, &hints,
567                           tau_tracker_on_dns, tracker );
568        return;
569    }
570
571    /* also need a valid connection ID... */
572    if( !is_connected && !tracker->is_connecting && tracker->addr )
573    {
574        struct evbuffer * buf = evbuffer_new( );
575        tracker->is_connecting = TRUE;
576        tracker->connection_transaction_id = tau_transaction_new( );
577        dbgmsg( tracker->key, "Trying to connect. Transaction ID is %u",
578                tracker->connection_transaction_id );
579        evbuffer_add_hton_64( buf, 0x41727101980LL );
580        evbuffer_add_hton_32( buf, TAU_ACTION_CONNECT );
581        evbuffer_add_hton_32( buf, tracker->connection_transaction_id );
582        tau_sendto( tracker->session, tracker->addr, tracker->port,
583                    evbuffer_pullup( buf, -1 ),
584                    evbuffer_get_length( buf ) );
585        evbuffer_free( buf );
586        return;
587    }
588
589    /* send the announce requests */
590    reqs = &tracker->announces;
591    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i )
592    {
593        tr_bool remove_request = FALSE;
594        struct tau_announce_request * req = tr_ptrArrayNth( reqs, i );
595        if( is_connected && !req->sent_at ) {
596            dbgmsg( tracker->key, "Sending an announce request" );
597            req->sent_at = now;
598            tau_tracker_send_request( tracker, req->payload, req->payload_len );
599            remove_request = req->callback == NULL;
600        }
601        else if( req->created_at + TAU_REQUEST_TTL < now ) {
602            tau_announce_request_fail( tracker->session, req, FALSE, TRUE, NULL );
603            remove_request = TRUE;
604        }
605        if( tracker->close_at && ( tracker->close_at <= time(NULL) ) )
606            remove_request = TRUE;
607        if( remove_request ) {
608            tau_announce_request_free( req );
609            tr_ptrArrayRemove( reqs, i );
610            --i;
611            --n;
612        }
613    }
614
615    /* send the scrape requests */
616    reqs = &tracker->scrapes;
617    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i )
618    {
619        tr_bool remove_request = FALSE;
620        struct tau_scrape_request * req = tr_ptrArrayNth( reqs, i );
621        if( is_connected && !req->sent_at ) {
622            dbgmsg( tracker->key, "Sending a scrape request" );
623            req->sent_at = now;
624            tau_tracker_send_request( tracker, req->payload, req->payload_len );
625            remove_request = req->callback == NULL;
626        }
627        else if( req->created_at + TAU_REQUEST_TTL < now ) {
628            tau_scrape_request_fail( tracker->session, req, FALSE, TRUE, NULL );
629            remove_request = TRUE;
630        }
631        if( tracker->close_at && ( tracker->close_at <= time(NULL) ) )
632            remove_request = TRUE;
633        if( remove_request ) {
634            tau_scrape_request_free( req );
635            tr_ptrArrayRemove( reqs, i );
636            --i;
637            --n;
638        }
639    }
640}
641
642static void
643on_tracker_connection_response( struct tau_tracker  * tracker,
644                                tau_action_t          action,
645                                struct evbuffer     * buf )
646{
647    const time_t now = tr_time( );
648
649    tracker->is_connecting = FALSE;
650    tracker->connection_transaction_id = 0;
651
652    if( action == TAU_ACTION_CONNECT )
653    {
654        tracker->connection_id = evbuffer_read_ntoh_64( buf );
655        tracker->connection_expiration_time = now + TAU_CONNECTION_TTL_SECS;
656        dbgmsg( tracker->key, "Got a new connection ID from tracker: %"PRIu64,
657                tracker->connection_id );
658    }
659    else
660    {
661        char * errmsg;
662        const size_t buflen = evbuffer_get_length( buf );
663
664        if( ( action == TAU_ACTION_ERROR ) && ( buflen > 0 ) )
665            errmsg = tr_strndup( evbuffer_pullup( buf, -1 ), buflen );
666        else
667            errmsg = tr_strdup( _( "Connection refused" ) );
668
669        dbgmsg( tracker->key, "%s", errmsg );
670        tau_tracker_fail_all( tracker, TRUE, FALSE, errmsg );
671        tr_free( errmsg );
672    }
673
674    tau_tracker_upkeep( tracker );
675}
676
677/****
678*****
679*****  SESSION
680*****
681****/
682
683struct tr_announcer_udp
684{
685    /* tau_tracker */
686    tr_ptrArray trackers;
687
688    tr_session * session;
689};
690
691static struct tr_announcer_udp*
692announcer_udp_get( tr_session * session )
693{
694    struct tr_announcer_udp * tau;
695
696    if( session->announcer_udp != NULL )
697        return session->announcer_udp;
698
699    tau = tr_new0( struct tr_announcer_udp, 1 );
700    tau->trackers = TR_PTR_ARRAY_INIT;
701    tau->session = session;
702    session->announcer_udp = tau;
703    return tau;
704}
705
706/* Finds the tau_tracker struct that corresponds to this url.
707   If it doesn't exist yet, create one. */
708static struct tau_tracker *
709tau_session_get_tracker( struct tr_announcer_udp * tau, const char * url )
710{
711    int i;
712    int n;
713    int port;
714    char * host;
715    char * key;
716    struct tau_tracker * tracker = NULL;
717
718    /* see if we've already got a tracker that matches this host + port */
719    tr_urlParse( url, -1, NULL, &host, &port, NULL );
720    key = tr_strdup_printf( "%s:%d", host, port );
721    for( i=0, n=tr_ptrArraySize( &tau->trackers ); !tracker && i<n; ++i ) {
722        struct tau_tracker * tmp = tr_ptrArrayNth( &tau->trackers, i );
723        if( !tr_strcmp0( tmp->key, key ) )
724            tracker = tmp;
725    }
726
727    /* if we don't have a match, build a new tracker */
728    if( tracker == NULL )
729    {
730        tracker = tr_new0( struct tau_tracker, 1 );
731        tracker->session = tau->session;
732        tracker->key = key;
733        tracker->host = host;
734        tracker->port = port;
735        tracker->scrapes = TR_PTR_ARRAY_INIT;
736        tracker->announces = TR_PTR_ARRAY_INIT;
737        tr_ptrArrayAppend( &tau->trackers, tracker );
738        dbgmsg( tracker->key, "New tau_tracker created" );
739    }
740    else
741    {
742        tr_free( key );
743        tr_free( host );
744    }
745
746    return tracker;
747}
748
749/****
750*****
751*****  PUBLIC API
752*****
753****/
754
755void
756tr_tracker_udp_upkeep( tr_session * session )
757{
758    struct tr_announcer_udp * tau = session->announcer_udp;
759
760    if( tau != NULL )
761        tr_ptrArrayForeach( &tau->trackers,
762                            (PtrArrayForeachFunc)tau_tracker_upkeep );
763}
764
765tr_bool
766tr_tracker_udp_is_empty( const tr_session * session )
767{
768    int i;
769    int n;
770    struct tr_announcer_udp * tau = session->announcer_udp;
771
772    if( tau != NULL )
773        for( i=0, n=tr_ptrArraySize(&tau->trackers); i<n; ++i )
774            if( !tau_tracker_is_empty( tr_ptrArrayNth( &tau->trackers, i ) ) )
775                return FALSE;
776
777    return TRUE;
778}
779
780/* drop dead now. */
781void
782tr_tracker_udp_close( tr_session * session )
783{
784    struct tr_announcer_udp * tau = session->announcer_udp;
785
786    if( tau != NULL )
787    {
788        session->announcer_udp = NULL;
789        tr_ptrArrayDestruct( &tau->trackers, (PtrArrayForeachFunc)tau_tracker_free );
790        tr_free( tau );
791    }
792       
793}
794
795/* start shutting down.
796   This doesn't destroy everything if there are requests,
797   but sets a deadline on how much longer to wait for the remaining ones */
798void
799tr_tracker_udp_start_shutdown( tr_session * session )
800{
801    const time_t now = time( NULL );
802    struct tr_announcer_udp * tau = session->announcer_udp;
803
804    if( tau != NULL )
805    {
806        int i, n;
807        for( i=0, n=tr_ptrArraySize(&tau->trackers); i<n; ++i )
808        {
809            struct tau_tracker * tracker = tr_ptrArrayNth( &tau->trackers, i );
810            tracker->close_at = now + 3;
811            tau_tracker_upkeep( tracker );
812        }
813    }
814}
815
816/* @brief process an incoming udp message if it's a tracker response.
817 * @return true if msg was a tracker response; false otherwise */
818tr_bool
819tau_handle_message( tr_session * session, const uint8_t * msg, size_t msglen )
820{
821    int i;
822    int n;
823    struct tr_announcer_udp * tau;
824    tau_action_t action_id;
825    tau_transaction_t transaction_id;
826    struct evbuffer * buf;
827
828    /*fprintf( stderr, "got an incoming udp message w/len %zu\n", msglen );*/
829
830    if( !session || !session->announcer_udp )
831        return FALSE;
832    if( msglen < (sizeof(uint32_t)*2) )
833        return FALSE;
834
835    /* extract the action_id and see if it makes sense */
836    buf = evbuffer_new( );
837    evbuffer_add_reference( buf, msg, msglen, NULL, NULL );
838    action_id = evbuffer_read_ntoh_32( buf );
839    if( !is_tau_response_message( action_id, msglen ) ) {
840        evbuffer_free( buf );
841        return FALSE;
842    }
843
844    /* extract the transaction_id and look for a match */
845    tau = session->announcer_udp;
846    transaction_id = evbuffer_read_ntoh_32( buf );
847    /*fprintf( stderr, "UDP got a transaction_id %u...\n", transaction_id );*/
848    for( i=0, n=tr_ptrArraySize( &tau->trackers ); i<n; ++i )
849    {
850        int j, jn;
851        tr_ptrArray * reqs;
852        struct tau_tracker * tracker = tr_ptrArrayNth( &tau->trackers, i );
853
854        /* is it a connection response? */
855        if( tracker->is_connecting
856            && ( transaction_id == tracker->connection_transaction_id ) )
857        {
858            dbgmsg( tracker->key, "%"PRIu32" is my connection request!", transaction_id );
859            on_tracker_connection_response( tracker, action_id, buf );
860            evbuffer_free( buf );
861            return TRUE;
862        }
863
864        /* is it a response to one of this tracker's announces? */
865        reqs = &tracker->announces;
866        for( j=0, jn=tr_ptrArraySize(reqs); j<jn; ++j ) {
867            struct tau_announce_request * req = tr_ptrArrayNth( reqs, j );
868            if( req->sent_at && ( transaction_id == req->transaction_id ) ) {
869                dbgmsg( tracker->key, "%"PRIu32" is an announce request!", transaction_id );
870                tr_ptrArrayRemove( reqs, j );
871                on_announce_response( session, req, action_id, buf );
872                tau_announce_request_free( req );
873                evbuffer_free( buf );
874                return TRUE;
875            }
876        }
877
878        /* is it a response to one of this tracker's scrapes? */
879        reqs = &tracker->scrapes;
880        for( j=0, jn=tr_ptrArraySize(reqs); j<jn; ++j ) {
881            struct tau_scrape_request * req = tr_ptrArrayNth( reqs, j );
882            if( req->sent_at && ( transaction_id == req->transaction_id ) ) {
883                dbgmsg( tracker->key, "%"PRIu32" is a scrape request!", transaction_id );
884                tr_ptrArrayRemove( reqs, j );
885                on_scrape_response( session, req, action_id, buf );
886                tau_scrape_request_free( req );
887                evbuffer_free( buf );
888                return TRUE;
889            }
890        }
891    }
892
893    /* no match... */
894    evbuffer_free( buf );
895    return FALSE;
896}
897
898void
899tr_tracker_udp_announce( tr_session                 * session,
900                         const tr_announce_request  * request,
901                         tr_announce_response_func    response_func,
902                         void                       * user_data )
903{
904    struct tr_announcer_udp * tau = announcer_udp_get( session );
905    struct tau_tracker * tracker = tau_session_get_tracker( tau, request->url );
906    struct tau_announce_request * r = tau_announce_request_new( request,
907                                                                response_func,
908                                                                user_data );
909    tr_ptrArrayAppend( &tracker->announces, r );
910    tau_tracker_upkeep( tracker );
911}
912
913void
914tr_tracker_udp_scrape( tr_session               * session,
915                       const tr_scrape_request  * request,
916                       tr_scrape_response_func    response_func,
917                       void                     * user_data )
918{
919    struct tr_announcer_udp * tau = announcer_udp_get( session );
920    struct tau_tracker * tracker = tau_session_get_tracker( tau, request->url );
921    struct tau_scrape_request * r = tau_scrape_request_new( request,
922                                                            response_func,
923                                                            user_data );
924    tr_ptrArrayAppend( &tracker->scrapes, r );
925    tau_tracker_upkeep( tracker );
926}
Note: See TracBrowser for help on using the repository browser.