source: trunk/libtransmission/announcer-udp.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

  • Property svn:keywords set to Date Rev Author Id
File size: 28.7 KB
Line 
1/*
2 * This file Copyright (C) 2010-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: announcer-udp.c 14718 2016-03-13 22:11:01Z mikedld $
8 */
9
10#define __LIBTRANSMISSION_ANNOUNCER_MODULE__
11
12#include <errno.h> /* errno, EAFNOSUPPORT */
13#include <string.h> /* memcpy (), memset () */
14
15#include <event2/buffer.h>
16#include <event2/dns.h>
17#include <event2/util.h>
18
19#include "transmission.h"
20#include "announcer.h"
21#include "announcer-common.h"
22#include "crypto-utils.h" /* tr_rand_buffer () */
23#include "log.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, ...) \
31  do \
32    { \
33      if (tr_logGetDeepEnabled ()) \
34        tr_logAddDeep (__FILE__, __LINE__, name, __VA_ARGS__); \
35    } \
36  while (0)
37
38/****
39*****
40****/
41
42static void
43tau_sockaddr_setport (struct sockaddr * sa, tr_port port)
44{
45    if (sa->sa_family == AF_INET)
46      ((struct sockaddr_in *)sa)->sin_port = htons (port);
47    else if (sa->sa_family == AF_INET6)
48      ((struct sockaddr_in6 *)sa)->sin6_port = htons (port);
49}
50
51static int
52tau_sendto (tr_session * session,
53            struct evutil_addrinfo * ai, tr_port port,
54            const void * buf, size_t buflen)
55{
56    tr_socket_t sockfd;
57
58    if (ai->ai_addr->sa_family == AF_INET)
59        sockfd = session->udp_socket;
60    else if (ai->ai_addr->sa_family == AF_INET6)
61        sockfd = session->udp6_socket;
62    else
63        sockfd = TR_BAD_SOCKET;
64
65    if (sockfd == TR_BAD_SOCKET) {
66        errno = EAFNOSUPPORT;
67        return -1;
68    }
69
70    tau_sockaddr_setport (ai->ai_addr, port);
71    return sendto (sockfd, buf, buflen, 0, ai->ai_addr, ai->ai_addrlen);
72}
73
74/****
75*****
76****/
77
78static uint32_t
79evbuffer_read_ntoh_32 (struct evbuffer * buf)
80{
81    uint32_t val;
82    evbuffer_remove (buf, &val, sizeof (uint32_t));
83    return ntohl (val);
84}
85
86static uint64_t
87evbuffer_read_ntoh_64 (struct evbuffer * buf)
88{
89    uint64_t val;
90    evbuffer_remove (buf, &val, sizeof (uint64_t));
91    return tr_ntohll (val);
92}
93
94/****
95*****
96****/
97
98typedef uint64_t tau_connection_t;
99
100enum
101{
102    TAU_CONNECTION_TTL_SECS = 60
103};
104
105typedef uint32_t tau_transaction_t;
106
107static tau_transaction_t
108tau_transaction_new (void)
109{
110    tau_transaction_t tmp;
111    tr_rand_buffer (&tmp, sizeof (tau_transaction_t));
112    return tmp;
113}
114
115/* used in the "action" field of a request */
116typedef enum
117{
118    TAU_ACTION_CONNECT  = 0,
119    TAU_ACTION_ANNOUNCE = 1,
120    TAU_ACTION_SCRAPE   = 2,
121    TAU_ACTION_ERROR    = 3
122}
123tau_action_t;
124
125static bool
126is_tau_response_message (tau_action_t action, size_t msglen)
127{
128    if (action == TAU_ACTION_CONNECT) return msglen == 16;
129    if (action == TAU_ACTION_ANNOUNCE) return msglen >= 20;
130    if (action == TAU_ACTION_SCRAPE ) return msglen >= 20;
131    if (action == TAU_ACTION_ERROR  ) return msglen >= 8;
132    return false;
133}
134
135enum
136{
137    TAU_REQUEST_TTL = 60
138};
139
140/****
141*****
142*****  SCRAPE
143*****
144****/
145
146struct tau_scrape_request
147{
148    void * payload;
149    size_t payload_len;
150
151    time_t sent_at;
152    time_t created_at;
153    tau_transaction_t transaction_id;
154
155    tr_scrape_response response;
156    tr_scrape_response_func callback;
157    void * user_data;
158};
159
160static struct tau_scrape_request *
161tau_scrape_request_new (const tr_scrape_request  * in,
162                        tr_scrape_response_func    callback,
163                        void                     * user_data)
164{
165    int i;
166    struct evbuffer * buf;
167    struct tau_scrape_request * req;
168    const tau_transaction_t transaction_id = tau_transaction_new ();
169
170    /* build the payload */
171    buf = evbuffer_new ();
172    evbuffer_add_hton_32 (buf, TAU_ACTION_SCRAPE);
173    evbuffer_add_hton_32 (buf, transaction_id);
174    for (i=0; i<in->info_hash_count; ++i)
175        evbuffer_add (buf, in->info_hash[i], SHA_DIGEST_LENGTH);
176
177    /* build the tau_scrape_request */
178    req = tr_new0 (struct tau_scrape_request, 1);
179    req->created_at = tr_time ();
180    req->transaction_id = transaction_id;
181    req->callback = callback;
182    req->user_data = user_data;
183    req->response.url = tr_strdup (in->url);
184    req->response.row_count = in->info_hash_count;
185    req->payload_len = evbuffer_get_length (buf);
186    req->payload = tr_memdup (evbuffer_pullup (buf, -1), req->payload_len);
187    for (i=0; i<req->response.row_count; ++i)
188    {
189        req->response.rows[i].seeders = -1;
190        req->response.rows[i].leechers = -1;
191        req->response.rows[i].downloads = -1;
192        memcpy (req->response.rows[i].info_hash,
193                in->info_hash[i], SHA_DIGEST_LENGTH);
194    }
195
196    /* cleanup */
197    evbuffer_free (buf);
198    return req;
199}
200
201static void
202tau_scrape_request_free (struct tau_scrape_request * req)
203{
204    tr_free (req->response.errmsg);
205    tr_free (req->response.url);
206    tr_free (req->payload);
207    tr_free (req);
208}
209
210static void
211tau_scrape_request_finished (const struct tau_scrape_request  * request)
212{
213    if (request->callback != NULL)
214        request->callback (&request->response, request->user_data);
215}
216
217static void
218tau_scrape_request_fail (struct tau_scrape_request  * request,
219                         bool                         did_connect,
220                         bool                         did_timeout,
221                         const char                 * errmsg)
222{
223    request->response.did_connect = did_connect;
224    request->response.did_timeout = did_timeout;
225    request->response.errmsg = tr_strdup (errmsg);
226    tau_scrape_request_finished (request);
227}
228
229static void
230on_scrape_response (struct tau_scrape_request  * request,
231                    tau_action_t                 action,
232                    struct evbuffer            * buf)
233{
234    request->response.did_connect = true;
235    request->response.did_timeout = false;
236
237    if (action == TAU_ACTION_SCRAPE)
238    {
239        int i;
240        for (i=0; i<request->response.row_count; ++i)
241        {
242            struct tr_scrape_response_row * row;
243
244            if (evbuffer_get_length (buf) < (sizeof (uint32_t) * 3))
245                break;
246
247            row = &request->response.rows[i];
248            row->seeders   = evbuffer_read_ntoh_32 (buf);
249            row->downloads = evbuffer_read_ntoh_32 (buf);
250            row->leechers  = evbuffer_read_ntoh_32 (buf);
251        }
252
253        tau_scrape_request_finished (request);
254    }
255    else
256    {
257        char * errmsg;
258        const size_t buflen = evbuffer_get_length (buf);
259
260        if ((action == TAU_ACTION_ERROR) && (buflen > 0))
261            errmsg = tr_strndup (evbuffer_pullup (buf, -1), buflen);
262        else
263            errmsg = tr_strdup (_("Unknown error"));
264
265        tau_scrape_request_fail (request, true, false, errmsg);
266        tr_free (errmsg);
267    }
268}
269
270/****
271*****
272*****  ANNOUNCE
273*****
274****/
275
276struct tau_announce_request
277{
278    void * payload;
279    size_t payload_len;
280
281    time_t created_at;
282    time_t sent_at;
283    tau_transaction_t transaction_id;
284
285    tr_announce_response response;
286    tr_announce_response_func callback;
287    void * user_data;
288};
289
290typedef enum
291{
292    /* used in the "event" field of an announce request */
293    TAU_ANNOUNCE_EVENT_NONE      = 0,
294    TAU_ANNOUNCE_EVENT_COMPLETED = 1,
295    TAU_ANNOUNCE_EVENT_STARTED   = 2,
296    TAU_ANNOUNCE_EVENT_STOPPED   = 3
297}
298tau_announce_event;
299
300static tau_announce_event
301get_tau_announce_event (tr_announce_event e)
302{
303    switch (e)
304    {
305        case TR_ANNOUNCE_EVENT_COMPLETED: return TAU_ANNOUNCE_EVENT_COMPLETED;
306        case TR_ANNOUNCE_EVENT_STARTED:   return TAU_ANNOUNCE_EVENT_STARTED;
307        case TR_ANNOUNCE_EVENT_STOPPED:   return TAU_ANNOUNCE_EVENT_STOPPED;
308        default:                          return TAU_ANNOUNCE_EVENT_NONE;
309    }
310}
311
312static struct tau_announce_request *
313tau_announce_request_new (const tr_announce_request  * in,
314                          tr_announce_response_func    callback,
315                          void                       * user_data)
316{
317    struct evbuffer * buf;
318    struct tau_announce_request * req;
319    const tau_transaction_t transaction_id = tau_transaction_new ();
320
321    /* build the payload */
322    buf = evbuffer_new ();
323    evbuffer_add_hton_32 (buf, TAU_ACTION_ANNOUNCE);
324    evbuffer_add_hton_32 (buf, transaction_id);
325    evbuffer_add      (buf, in->info_hash, SHA_DIGEST_LENGTH);
326    evbuffer_add      (buf, in->peer_id, PEER_ID_LEN);
327    evbuffer_add_hton_64 (buf, in->down);
328    evbuffer_add_hton_64 (buf, in->leftUntilComplete);
329    evbuffer_add_hton_64 (buf, in->up);
330    evbuffer_add_hton_32 (buf, get_tau_announce_event (in->event));
331    evbuffer_add_hton_32 (buf, 0);
332    evbuffer_add_hton_32 (buf, in->key);
333    evbuffer_add_hton_32 (buf, in->numwant);
334    evbuffer_add_hton_16 (buf, in->port);
335
336    /* build the tau_announce_request */
337    req = tr_new0 (struct tau_announce_request, 1);
338    req->created_at = tr_time ();
339    req->transaction_id = transaction_id;
340    req->callback = callback;
341    req->user_data = user_data;
342    req->payload_len = evbuffer_get_length (buf);
343    req->payload = tr_memdup (evbuffer_pullup (buf, -1), req->payload_len);
344    req->response.seeders = -1;
345    req->response.leechers = -1;
346    req->response.downloads = -1;
347    memcpy (req->response.info_hash, in->info_hash, SHA_DIGEST_LENGTH);
348
349    evbuffer_free (buf);
350    return req;
351}
352
353static void
354tau_announce_request_free (struct tau_announce_request * req)
355{
356    tr_free (req->response.tracker_id_str);
357    tr_free (req->response.warning);
358    tr_free (req->response.errmsg);
359    tr_free (req->response.pex6);
360    tr_free (req->response.pex);
361    tr_free (req->payload);
362    tr_free (req);
363}
364
365static void
366tau_announce_request_finished (const struct tau_announce_request * request)
367{
368    if (request->callback != NULL)
369        request->callback (&request->response, request->user_data);
370}
371
372static void
373tau_announce_request_fail (struct tau_announce_request  * request,
374                           bool                           did_connect,
375                           bool                           did_timeout,
376                           const char                   * errmsg)
377{
378    request->response.did_connect = did_connect;
379    request->response.did_timeout = did_timeout;
380    request->response.errmsg = tr_strdup (errmsg);
381    tau_announce_request_finished (request);
382}
383
384static void
385on_announce_response (struct tau_announce_request  * request,
386                      tau_action_t                   action,
387                      struct evbuffer              * buf)
388{
389    const size_t buflen = evbuffer_get_length (buf);
390
391    request->response.did_connect = true;
392    request->response.did_timeout = false;
393
394    if ((action == TAU_ACTION_ANNOUNCE) && (buflen >= 3*sizeof (uint32_t)))
395    {
396        tr_announce_response * resp = &request->response;
397        resp->interval = evbuffer_read_ntoh_32 (buf);
398        resp->leechers = evbuffer_read_ntoh_32 (buf);
399        resp->seeders  = evbuffer_read_ntoh_32 (buf);
400        resp->pex = tr_peerMgrCompactToPex (evbuffer_pullup (buf, -1),
401                                            evbuffer_get_length (buf),
402                                            NULL, 0,
403                                            &request->response.pex_count);
404        tau_announce_request_finished (request);
405    }
406    else
407    {
408        char * errmsg;
409
410        if ((action == TAU_ACTION_ERROR) && (buflen > 0))
411            errmsg = tr_strndup (evbuffer_pullup (buf, -1), buflen);
412        else
413            errmsg = tr_strdup (_("Unknown error"));
414
415        tau_announce_request_fail (request, true, false, errmsg);
416        tr_free (errmsg);
417    }
418}
419
420/****
421*****
422*****  TRACKERS
423*****
424****/
425
426struct tau_tracker
427{
428    tr_session * session;
429
430    char * key;
431    char * host;
432    int port;
433
434    struct evdns_getaddrinfo_request * dns_request;
435    struct evutil_addrinfo * addr;
436    time_t addr_expiration_time;
437
438    time_t connecting_at;
439    time_t connection_expiration_time;
440    tau_connection_t connection_id;
441    tau_transaction_t connection_transaction_id;
442
443    time_t close_at;
444
445    tr_ptrArray announces;
446    tr_ptrArray scrapes;
447};
448
449static void tau_tracker_upkeep (struct tau_tracker *);
450
451static void
452tau_tracker_free (struct tau_tracker * t)
453{
454    assert (t->dns_request == NULL);
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->dns_request = NULL;
499
500    if (errcode)
501    {
502        char * errmsg = tr_strdup_printf (_("DNS Lookup failed: %s"),
503                                          evutil_gai_strerror (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->dns_request == NULL);
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", (void*)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", (void*)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", (void*)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", (void*)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        && tracker->dns_request == NULL;
659}
660
661static void
662tau_tracker_upkeep (struct tau_tracker * tracker)
663{
664    const time_t now = tr_time ();
665    const bool closing = tracker->close_at != 0;
666
667    /* if the address info is too old, expire it */
668    if (tracker->addr != NULL && (closing || tracker->addr_expiration_time <= now)) {
669        dbgmsg (tracker->host, "Expiring old DNS result");
670        evutil_freeaddrinfo (tracker->addr);
671        tracker->addr = NULL;
672    }
673
674    /* are there any requests pending? */
675    if (tau_tracker_is_idle (tracker))
676        return;
677
678    /* if we don't have an address yet, try & get one now. */
679    if (!closing && tracker->addr == NULL && tracker->dns_request == NULL)
680    {
681        struct evutil_addrinfo hints;
682        memset (&hints, 0, sizeof (hints));
683        hints.ai_family = AF_UNSPEC;
684        hints.ai_socktype = SOCK_DGRAM;
685        hints.ai_protocol = IPPROTO_UDP;
686        dbgmsg (tracker->host, "Trying a new DNS lookup");
687        tracker->dns_request = 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            (void*)tracker->addr,
695            (int)(tracker->connection_expiration_time > now),
696            (size_t)tracker->connection_expiration_time,
697            (size_t)now,
698            (size_t)tracker->connecting_at);
699
700    /* also need a valid connection ID... */
701    if (tracker->addr
702        && (tracker->connection_expiration_time <= now)
703        && (!tracker->connecting_at))
704    {
705        struct evbuffer * buf = evbuffer_new ();
706        tracker->connecting_at = now;
707        tracker->connection_transaction_id = tau_transaction_new ();
708        dbgmsg (tracker->key, "Trying to connect. Transaction ID is %u",
709                tracker->connection_transaction_id);
710        evbuffer_add_hton_64 (buf, 0x41727101980LL);
711        evbuffer_add_hton_32 (buf, TAU_ACTION_CONNECT);
712        evbuffer_add_hton_32 (buf, tracker->connection_transaction_id);
713        tau_sendto (tracker->session, tracker->addr, tracker->port,
714                    evbuffer_pullup (buf, -1),
715                    evbuffer_get_length (buf));
716        evbuffer_free (buf);
717        return;
718    }
719
720    tau_tracker_timeout_reqs (tracker);
721
722    if ((tracker->addr != NULL) && (tracker->connection_expiration_time > now))
723        tau_tracker_send_reqs (tracker);
724}
725
726/****
727*****
728*****  SESSION
729*****
730****/
731
732struct tr_announcer_udp
733{
734    /* tau_tracker */
735    tr_ptrArray trackers;
736
737    tr_session * session;
738};
739
740static struct tr_announcer_udp*
741announcer_udp_get (tr_session * session)
742{
743    struct tr_announcer_udp * tau;
744
745    if (session->announcer_udp != NULL)
746        return session->announcer_udp;
747
748    tau = tr_new0 (struct tr_announcer_udp, 1);
749    tau->trackers = TR_PTR_ARRAY_INIT;
750    tau->session = session;
751    session->announcer_udp = tau;
752    return tau;
753}
754
755/* Finds the tau_tracker struct that corresponds to this url.
756   If it doesn't exist yet, create one. */
757static struct tau_tracker *
758tau_session_get_tracker (struct tr_announcer_udp * tau, const char * url)
759{
760    int i;
761    int n;
762    int port;
763    char * host;
764    char * key;
765    struct tau_tracker * tracker = NULL;
766
767    /* see if we've already got a tracker that matches this host + port */
768    tr_urlParse (url, TR_BAD_SIZE, NULL, &host, &port, NULL);
769    key = tr_strdup_printf ("%s:%d", host, port);
770    for (i=0, n=tr_ptrArraySize (&tau->trackers); !tracker && i<n; ++i) {
771        struct tau_tracker * tmp = tr_ptrArrayNth (&tau->trackers, i);
772        if (tr_strcmp0 (tmp->key, key) == 0)
773            tracker = tmp;
774    }
775
776    /* if we don't have a match, build a new tracker */
777    if (tracker == NULL)
778    {
779        tracker = tr_new0 (struct tau_tracker, 1);
780        tracker->session = tau->session;
781        tracker->key = key;
782        tracker->host = host;
783        tracker->port = port;
784        tracker->scrapes = TR_PTR_ARRAY_INIT;
785        tracker->announces = TR_PTR_ARRAY_INIT;
786        tr_ptrArrayAppend (&tau->trackers, tracker);
787        dbgmsg (tracker->key, "New tau_tracker created");
788    }
789    else
790    {
791        tr_free (key);
792        tr_free (host);
793    }
794
795    return tracker;
796}
797
798/****
799*****
800*****  PUBLIC API
801*****
802****/
803
804void
805tr_tracker_udp_upkeep (tr_session * session)
806{
807    struct tr_announcer_udp * tau = session->announcer_udp;
808
809    if (tau != NULL)
810        tr_ptrArrayForeach (&tau->trackers,
811                          (PtrArrayForeachFunc)tau_tracker_upkeep);
812}
813
814bool
815tr_tracker_udp_is_idle (const tr_session * session)
816{
817    int i;
818    int n;
819    struct tr_announcer_udp * tau = session->announcer_udp;
820
821    if (tau != NULL)
822        for (i=0, n=tr_ptrArraySize (&tau->trackers); i<n; ++i)
823            if (!tau_tracker_is_idle (tr_ptrArrayNth (&tau->trackers, i)))
824                return false;
825
826    return true;
827}
828
829/* drop dead now. */
830void
831tr_tracker_udp_close (tr_session * session)
832{
833    struct tr_announcer_udp * tau = session->announcer_udp;
834
835    if (tau != NULL)
836    {
837        session->announcer_udp = NULL;
838        tr_ptrArrayDestruct (&tau->trackers, (PtrArrayForeachFunc)tau_tracker_free);
839        tr_free (tau);
840    }
841}
842
843/* start shutting down.
844   This doesn't destroy everything if there are requests,
845   but sets a deadline on how much longer to wait for the remaining ones */
846void
847tr_tracker_udp_start_shutdown (tr_session * session)
848{
849    const time_t now = time (NULL);
850    struct tr_announcer_udp * tau = session->announcer_udp;
851
852    if (tau != NULL)
853    {
854        int i, n;
855        for (i=0, n=tr_ptrArraySize (&tau->trackers); i<n; ++i)
856        {
857            struct tau_tracker * tracker = tr_ptrArrayNth (&tau->trackers, i);
858            if (tracker->dns_request != NULL)
859                evdns_getaddrinfo_cancel (tracker->dns_request);
860            tracker->close_at = now + 3;
861            tau_tracker_upkeep (tracker);
862        }
863    }
864}
865
866/* @brief process an incoming udp message if it's a tracker response.
867 * @return true if msg was a tracker response; false otherwise */
868bool
869tau_handle_message (tr_session * session, const uint8_t * msg, size_t msglen)
870{
871    int i;
872    int n;
873    struct tr_announcer_udp * tau;
874    tau_action_t action_id;
875    tau_transaction_t transaction_id;
876    struct evbuffer * buf;
877
878    /*fprintf (stderr, "got an incoming udp message w/len %zu\n", msglen);*/
879
880    if (!session || !session->announcer_udp)
881        return false;
882    if (msglen < (sizeof (uint32_t)*2))
883        return false;
884
885    /* extract the action_id and see if it makes sense */
886    buf = evbuffer_new ();
887    evbuffer_add_reference (buf, msg, msglen, NULL, NULL);
888    action_id = evbuffer_read_ntoh_32 (buf);
889    if (!is_tau_response_message (action_id, msglen)) {
890        evbuffer_free (buf);
891        return false;
892    }
893
894    /* extract the transaction_id and look for a match */
895    tau = session->announcer_udp;
896    transaction_id = evbuffer_read_ntoh_32 (buf);
897    /*fprintf (stderr, "UDP got a transaction_id %u...\n", transaction_id);*/
898    for (i=0, n=tr_ptrArraySize (&tau->trackers); i<n; ++i)
899    {
900        int j, jn;
901        tr_ptrArray * reqs;
902        struct tau_tracker * tracker = tr_ptrArrayNth (&tau->trackers, i);
903
904        /* is it a connection response? */
905        if (tracker->connecting_at
906            && (transaction_id == tracker->connection_transaction_id))
907        {
908            dbgmsg (tracker->key, "%"PRIu32" is my connection request!", transaction_id);
909            on_tracker_connection_response (tracker, action_id, buf);
910            evbuffer_free (buf);
911            return true;
912        }
913
914        /* is it a response to one of this tracker's announces? */
915        reqs = &tracker->announces;
916        for (j=0, jn=tr_ptrArraySize (reqs); j<jn; ++j) {
917            struct tau_announce_request * req = tr_ptrArrayNth (reqs, j);
918            if (req->sent_at && (transaction_id == req->transaction_id)) {
919                dbgmsg (tracker->key, "%"PRIu32" is an announce request!", transaction_id);
920                tr_ptrArrayRemove (reqs, j);
921                on_announce_response (req, action_id, buf);
922                tau_announce_request_free (req);
923                evbuffer_free (buf);
924                return true;
925            }
926        }
927
928        /* is it a response to one of this tracker's scrapes? */
929        reqs = &tracker->scrapes;
930        for (j=0, jn=tr_ptrArraySize (reqs); j<jn; ++j) {
931            struct tau_scrape_request * req = tr_ptrArrayNth (reqs, j);
932            if (req->sent_at && (transaction_id == req->transaction_id)) {
933                dbgmsg (tracker->key, "%"PRIu32" is a scrape request!", transaction_id);
934                tr_ptrArrayRemove (reqs, j);
935                on_scrape_response (req, action_id, buf);
936                tau_scrape_request_free (req);
937                evbuffer_free (buf);
938                return true;
939            }
940        }
941    }
942
943    /* no match... */
944    evbuffer_free (buf);
945    return false;
946}
947
948void
949tr_tracker_udp_announce (tr_session                 * session,
950                         const tr_announce_request  * request,
951                         tr_announce_response_func    response_func,
952                         void                       * user_data)
953{
954    struct tr_announcer_udp * tau = announcer_udp_get (session);
955    struct tau_tracker * tracker = tau_session_get_tracker (tau, request->url);
956    struct tau_announce_request * r = tau_announce_request_new (request,
957                                                                response_func,
958                                                                user_data);
959    tr_ptrArrayAppend (&tracker->announces, r);
960    tau_tracker_upkeep (tracker);
961}
962
963void
964tr_tracker_udp_scrape (tr_session               * session,
965                       const tr_scrape_request  * request,
966                       tr_scrape_response_func    response_func,
967                       void                     * user_data)
968{
969    struct tr_announcer_udp * tau = announcer_udp_get (session);
970    struct tau_tracker * tracker = tau_session_get_tracker (tau, request->url);
971    struct tau_scrape_request * r = tau_scrape_request_new (request,
972                                                            response_func,
973                                                            user_data);
974    tr_ptrArrayAppend (&tracker->scrapes, r);
975    tau_tracker_upkeep (tracker);
976}
Note: See TracBrowser for help on using the repository browser.