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

Last change on this file since 13868 was 13868, checked in by jordan, 8 years ago

make all the log functions/structs/enums use a single 'tr_log' namespace, such as tr_logGetQueue, tr_logAddInfo, tr_logIsLevelActive

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