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

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

(libT) add '#include errno.h' to announcer-udp.c to ensure errno is declared

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