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

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

(trunk libT) strip trailing whitespace

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