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

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

(trunk libT) #117 "UDP tracker protocol suppoort" -- in case the tracker gives an error message in response to a connection response, store the error message in the scrape/announce response structs' errmsg fields.

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