]> granicus.if.org Git - transmission/commitdiff
(trunk libT) #117 "UDP tracker protocol support" -- working implementation; needs...
authorJordan Lee <jordan@transmissionbt.com>
Sun, 13 Mar 2011 00:18:11 +0000 (00:18 +0000)
committerJordan Lee <jordan@transmissionbt.com>
Sun, 13 Mar 2011 00:18:11 +0000 (00:18 +0000)
14 files changed:
libtransmission/Makefile.am
libtransmission/announcer-common.h
libtransmission/announcer-http.c
libtransmission/announcer-udp.c [new file with mode: 0644]
libtransmission/announcer.c
libtransmission/peer-io.c
libtransmission/peer-io.h
libtransmission/ptrarray.h
libtransmission/session.h
libtransmission/tr-udp.c
libtransmission/tr-udp.h
libtransmission/trevent.c
libtransmission/utils.c
libtransmission/utils.h

index ff90c7687c0f415af9678b39938265f7e87a96f2..6450fdcdbbc4aef19e94cbf9e5c29b03c05c2ee9 100644 (file)
@@ -19,6 +19,7 @@ noinst_LIBRARIES = libtransmission.a
 libtransmission_a_SOURCES = \
     announcer.c \
     announcer-http.c \
+    announcer-udp.c \
     bandwidth.c \
     bencode.c \
     bitfield.c \
index bf8069c5b2f1efd4b7a26dadcaaa7d1a68bb21ba..41e050d686d545a6017cf2fb87f6e148af87e503 100644 (file)
@@ -105,6 +105,11 @@ void tr_tracker_http_scrape( tr_session               * session,
                              tr_scrape_response_func    response_func,
                              void                     * user_data );
 
+void tr_tracker_udp_scrape( tr_session               * session,
+                            const tr_scrape_request  * req,
+                            tr_scrape_response_func    response_func,
+                            void                     * user_data );
+
 /***
 ****  ANNOUNCE
 ***/
@@ -125,6 +130,9 @@ typedef struct
     tr_announce_event event;
     tr_bool partial_seed;
 
+    /* the port we listen for incoming peers on */
+    int port;
+
     /* per-session key */
     int key;
 
@@ -225,5 +233,9 @@ void tr_tracker_http_announce( tr_session                 * session,
                                tr_announce_response_func    response_func,
                                void                       * user_data );
 
+void tr_tracker_udp_announce( tr_session                 * session,
+                              const tr_announce_request  * req,
+                              tr_announce_response_func    response_func,
+                              void                       * user_data );
 
 #endif /* _TR_ANNOUNCER_COMMON_H_ */
index cc6733c51c66c5b11fdf52dd9ed4ab46b6c928a6..9eab32dac9b85ac7a8367d9d753ca39f8ee92180 100644 (file)
@@ -73,7 +73,7 @@ announce_url_new( const tr_session * session, const tr_announce_request * req )
                               strchr( req->url, '?' ) ? '&' : '?',
                               escaped_info_hash,
                               PEER_ID_LEN, PEER_ID_LEN, req->peer_id,
-                              (int)tr_sessionGetPublicPeerPort( session ),
+                              req->port,
                               req->up,
                               req->down,
                               req->left,
diff --git a/libtransmission/announcer-udp.c b/libtransmission/announcer-udp.c
new file mode 100644 (file)
index 0000000..c410efe
--- /dev/null
@@ -0,0 +1,780 @@
+/*
+ * This file Copyright (C) Mnemosyne LLC
+ *
+ * This file is licensed by the GPL version 2. Works owned by the
+ * Transmission project are granted a special exemption to clause 2(b)
+ * so that the bulk of its code can remain under the MIT license.
+ * This exemption does not extend to derived works not owned by
+ * the Transmission project.
+ *
+ * $Id:$
+ */
+
+#define __LIBTRANSMISSION_ANNOUNCER_MODULE___
+
+#include <event2/buffer.h>
+#include <event2/dns.h>
+#include <event2/util.h>
+
+#include "transmission.h"
+#include "announcer-common.h"
+#include "crypto.h"
+#include "peer-io.h"
+#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
+#include "ptrarray.h"
+#include "tr-udp.h"
+#include "utils.h"
+
+#define dbgmsg( name, ... ) \
+if( tr_deepLoggingIsActive( ) ) do { \
+  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
+} while( 0 )
+
+/****
+*****
+****/
+
+static void
+tau_sockaddr_setport( struct sockaddr * sa, tr_port port )
+{
+    if( sa->sa_family == AF_INET )
+        ((struct sockaddr_in *)sa)->sin_port = htons(port);
+    else if (sa->sa_family == AF_INET6)
+        ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
+}
+
+static int
+tau_sendto( tr_session * session,
+            struct evutil_addrinfo * ai, tr_port port,
+            const void * buf, size_t buflen )
+{
+    int sockfd;
+
+    tau_sockaddr_setport( ai->ai_addr, port );
+    
+    if( ai->ai_addr->sa_family == AF_INET )
+        sockfd = session->udp_socket;
+    else if( ai->ai_addr->sa_family == AF_INET6 )
+        sockfd = session->udp6_socket;
+    else
+        sockfd = -1;
+
+    if( sockfd <  0 ) {
+        errno = EAFNOSUPPORT;
+        return -1;
+    }
+
+    return sendto( sockfd, buf, buflen, 0, ai->ai_addr, ai->ai_addrlen );
+}
+
+/****
+*****
+****/
+
+static uint32_t
+evbuffer_read_ntoh_32( struct evbuffer * buf )
+{
+    uint32_t val;
+    evbuffer_remove( buf, &val, sizeof( uint32_t ) );
+    return ntohl( val );
+}
+
+static uint64_t
+evbuffer_read_ntoh_64( struct evbuffer * buf )
+{
+    uint64_t val;
+    evbuffer_remove( buf, &val, sizeof( uint64_t ) );
+    return tr_ntohll( val );
+}
+
+/****
+*****
+****/
+
+typedef uint64_t tau_connection_t;
+
+enum
+{
+    TAU_CONNECTION_TTL_SECS = 60
+};
+
+typedef uint32_t tau_transaction_t;
+
+static tau_transaction_t
+tau_transaction_new( void )
+{
+    tau_transaction_t tmp;
+    tr_cryptoRandBuf( &tmp, sizeof( tau_transaction_t ) );
+    return tmp;
+}
+
+/* used in the "action" field of a request */
+typedef enum
+{
+    TAU_ACTION_CONNECT  = 0,
+    TAU_ACTION_ANNOUNCE = 1,
+    TAU_ACTION_SCRAPE   = 2,
+    TAU_ACTION_ERROR    = 3,
+
+    TAU_ACTION_MAX      = 3
+}
+tau_action_t;
+
+/****
+*****
+*****  SCRAPE
+*****
+****/
+
+struct tau_scrape_request
+{
+    void * payload;
+    size_t payload_len;
+
+    time_t sent_at;
+    tau_transaction_t transaction_id;
+
+    tr_scrape_response response;
+    tr_scrape_response_func * callback;
+    void * user_data;
+};
+
+static struct tau_scrape_request *
+tau_scrape_request_new( const tr_scrape_request  * in,
+                        tr_scrape_response_func    callback,
+                        void                     * user_data )
+{
+    int i;
+    struct evbuffer * buf;
+
+    struct tau_scrape_request * req = tr_new0( struct tau_scrape_request, 1 );
+    req->transaction_id = tau_transaction_new( );
+    req->callback = callback;
+    req->user_data = user_data;
+    req->response.url = tr_strdup( in->url );
+    req->response.row_count = in->info_hash_count;
+    for( i=0; i<req->response.row_count; ++i )
+        memcpy( req->response.rows[i].info_hash,
+                in->info_hash[i], SHA_DIGEST_LENGTH );
+
+    /* build the scrape payload */
+    buf = evbuffer_new( );
+    evbuffer_add_hton_32( buf, TAU_ACTION_SCRAPE );
+    evbuffer_add_hton_32( buf, req->transaction_id );
+    for( i=0; i<in->info_hash_count; ++i )
+        evbuffer_add( buf, in->info_hash[i], SHA_DIGEST_LENGTH );
+    req->payload_len = evbuffer_get_length( buf );
+    req->payload = tr_memdup( evbuffer_pullup( buf, -1 ), req->payload_len );
+    evbuffer_free( buf );
+
+    return req;
+}
+
+static void
+tau_scrape_request_free( struct tau_scrape_request * req )
+{
+    tr_free( req->payload );
+    tr_free( req->response.errmsg );
+    tr_free( req->response.url );
+    tr_free( req );
+}
+
+static void
+tau_scrape_request_finished( tr_session                       * session,
+                             const struct tau_scrape_request  * request )
+{
+    if( request->callback != NULL )
+        request->callback( session, &request->response, request->user_data );
+}
+
+static void
+tau_scrape_request_fail( tr_session                 * session,
+                         struct tau_scrape_request  * request,
+                         tr_bool                      did_connect,
+                         tr_bool                      did_timeout,
+                         const char                 * errmsg )
+{
+    request->response.did_connect = did_connect;
+    request->response.did_timeout = did_timeout;
+    request->response.errmsg = tr_strdup( errmsg );
+    tau_scrape_request_finished( session, request );
+}
+
+static void
+on_scrape_response( tr_session                  * session,
+                     struct tau_scrape_request  * request,
+                     tau_action_t                 action,
+                     struct evbuffer            * buf )
+{
+    request->response.did_connect = TRUE;
+    request->response.did_timeout = FALSE;
+
+    if( action == TAU_ACTION_SCRAPE )
+    {
+        int i;
+        for( i=0; i<request->response.row_count; ++i )
+        {
+            struct tr_scrape_response_row * row;
+
+            if( evbuffer_get_length( buf ) < ( sizeof( uint32_t ) * 3 ) )
+                break;
+
+            row = &request->response.rows[i];
+            row->seeders   = evbuffer_read_ntoh_32( buf );
+            row->downloads = evbuffer_read_ntoh_32( buf );
+            row->leechers  = evbuffer_read_ntoh_32( buf );
+
+        }
+
+        tau_scrape_request_finished( session, request );
+    }
+    else
+    {
+        assert( action == TAU_ACTION_ERROR );
+
+        tau_scrape_request_fail( session, request,
+                                 TRUE, FALSE, _( "Unknown error" ) );
+    }
+}
+
+/****
+*****
+*****  ANNOUNCE
+*****
+****/
+
+struct tau_announce_request
+{
+    void * payload;
+    size_t payload_len;
+
+    time_t sent_at;
+    tau_transaction_t transaction_id;
+
+    tr_announce_response response;
+    tr_announce_response_func * callback;
+    void * user_data;
+};
+
+typedef enum
+{
+    /* used in the "event" field of an announce request */
+    UDP_TRACKER_EVENT_NONE      = 0,
+    UDP_TRACKER_EVENT_COMPLETED = 1,
+    UDP_TRACKER_EVENT_STARTED   = 2,
+    UDP_TRACKER_EVENT_STOPPED   = 3
+}
+tau_announce_event;
+
+static tau_announce_event
+get_tau_announce_event( tr_announce_event e )
+{
+    switch( e )
+    {
+        case TR_ANNOUNCE_EVENT_COMPLETED:  return UDP_TRACKER_EVENT_COMPLETED;
+        case TR_ANNOUNCE_EVENT_STARTED:    return UDP_TRACKER_EVENT_STARTED;
+        case TR_ANNOUNCE_EVENT_STOPPED:    return UDP_TRACKER_EVENT_STOPPED;
+        default:                           return UDP_TRACKER_EVENT_NONE;
+    }
+}
+
+static struct tau_announce_request *
+tau_announce_request_new( const tr_announce_request  * in,
+                          tr_announce_response_func    callback,
+                          void                       * user_data )
+{
+    struct evbuffer * buf;
+
+    struct tau_announce_request * req = tr_new0( struct tau_announce_request, 1 );
+    req->transaction_id = tau_transaction_new( );
+    req->callback = callback;
+    req->user_data = user_data;
+    memcpy( req->response.info_hash, in->info_hash, SHA_DIGEST_LENGTH );
+
+    /* build the announce payload */
+    buf = evbuffer_new( );
+    evbuffer_add_hton_32( buf, TAU_ACTION_ANNOUNCE );
+    evbuffer_add_hton_32( buf, req->transaction_id );
+    evbuffer_add        ( buf, in->info_hash, SHA_DIGEST_LENGTH );
+    evbuffer_add        ( buf, in->peer_id, PEER_ID_LEN );
+    evbuffer_add_hton_64( buf, in->down );
+    evbuffer_add_hton_64( buf, in->left );
+    evbuffer_add_hton_64( buf, in->up );
+    evbuffer_add_hton_32( buf, get_tau_announce_event( in->event ) );
+    evbuffer_add_hton_32( buf, 0 );
+    evbuffer_add_hton_32( buf, in->key );
+    evbuffer_add_hton_32( buf, in->numwant );
+    evbuffer_add_hton_16( buf, in->port );
+    req->payload_len = evbuffer_get_length( buf );
+    req->payload = tr_memdup( evbuffer_pullup( buf, -1 ), req->payload_len );
+    evbuffer_free( buf );
+
+    return req;
+}
+
+static void
+tau_announce_request_free( struct tau_announce_request * req )
+{
+    tr_free( req->payload );
+    tr_free( req->response.tracker_id_str );
+    tr_free( req->response.warning );
+    tr_free( req->response.errmsg );
+    tr_free( req->response.pex6 );
+    tr_free( req->response.pex );
+    tr_free( req );
+}
+
+static void
+tau_announce_request_finished( tr_session                        * session,
+                               const struct tau_announce_request * request )
+{
+    if( request->callback != NULL )
+        request->callback( session, &request->response, request->user_data );
+}
+
+static void
+tau_announce_request_fail( tr_session                   * session,
+                           struct tau_announce_request  * request,
+                           tr_bool                        did_connect,
+                           tr_bool                        did_timeout,
+                           const char                   * errmsg )
+{
+    request->response.did_connect = did_connect;
+    request->response.did_timeout = did_timeout;
+    request->response.errmsg = tr_strdup( errmsg );
+    tau_announce_request_finished( session, request );
+}
+
+static void
+on_announce_response( tr_session                  * session,
+                     struct tau_announce_request  * request,
+                     tau_action_t                   action,
+                     struct evbuffer              * buf )
+{
+    request->response.did_connect = TRUE;
+    request->response.did_timeout = FALSE;
+
+    if( ( action == TAU_ACTION_ANNOUNCE )
+        && ( evbuffer_get_length( buf ) >= 3*sizeof(uint32_t) ) )
+    {
+        tr_announce_response * resp = &request->response;
+        resp->interval = evbuffer_read_ntoh_32( buf );
+        resp->leechers = evbuffer_read_ntoh_32( buf );
+        resp->seeders  = evbuffer_read_ntoh_32( buf );
+        resp->pex = tr_peerMgrCompactToPex( evbuffer_pullup( buf, -1 ),
+                                            evbuffer_get_length( buf ),
+                                            NULL, 0,
+                                            &request->response.pex_count );
+        tau_announce_request_finished( session, request );
+    }
+    else
+    {
+        assert( action == TAU_ACTION_ERROR );
+
+        tau_announce_request_fail( session, request,
+                                   TRUE, FALSE, _( "Unknown error" ) );
+    }
+}
+
+/****
+*****
+*****  TRACKERS
+*****
+****/
+
+struct tau_tracker
+{
+    tr_session * session;
+
+    char * key;
+    char * host;
+    int port;
+
+    tr_bool is_asking_dns;
+    struct evutil_addrinfo * addr;
+    time_t addr_expiration_time;
+
+    tr_bool is_connecting;
+    time_t connection_expiration_time;
+    tau_connection_t connection_id;
+    tau_transaction_t connection_transaction_id;
+
+    tr_ptrArray announces;
+    tr_ptrArray scrapes;
+};
+
+static void tau_tracker_upkeep( struct tau_tracker * );
+
+#if 0
+static void
+tau_tracker_free( struct tau_tracker * t )
+{
+    if( t->addr )
+        evutil_freeaddrinfo( t->addr );
+    tr_ptrArrayDestruct( &t->announces, (PtrArrayForeachFunc)tau_announce_request_free );
+    tr_ptrArrayDestruct( &t->scrapes, (PtrArrayForeachFunc)tau_scrape_request_free );
+    tr_free( t->host );
+    tr_free( t->key );
+    tr_free( t );
+}
+#endif
+
+static void
+tau_tracker_fail_all( struct tau_tracker  * tracker,
+                      tr_bool               did_connect,
+                      tr_bool               did_timeout,
+                      const char          * errmsg )
+{
+    int i;
+    int n;
+    tr_ptrArray * reqs;
+
+    /* fail all the scrapes */
+    reqs = &tracker->scrapes;
+    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i )
+        tau_scrape_request_fail( tracker->session, tr_ptrArrayNth( reqs, i ),
+                                 did_connect, did_timeout, errmsg );
+    tr_ptrArrayDestruct( reqs, (PtrArrayForeachFunc)tau_scrape_request_free );
+    *reqs = TR_PTR_ARRAY_INIT;
+
+    /* fail all the announces */
+    reqs = &tracker->announces;
+    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i )
+        tau_announce_request_fail( tracker->session, tr_ptrArrayNth( reqs, i ),
+                                   did_connect, did_timeout, errmsg );
+    tr_ptrArrayDestruct( reqs, (PtrArrayForeachFunc)tau_announce_request_free );
+    *reqs = TR_PTR_ARRAY_INIT;
+
+}
+
+static void
+tau_tracker_on_dns( int errcode, struct evutil_addrinfo *addr, void * vtracker )
+{
+    struct tau_tracker * tracker = vtracker;
+
+    tracker->is_asking_dns = FALSE;
+
+    if ( errcode )
+    {
+        char * errmsg = tr_strdup_printf( _( "DNS Lookup failed: %s" ), 
+                                          evdns_err_to_string( errcode ) );
+        dbgmsg( tracker->key, "%s", errmsg );
+        tau_tracker_fail_all( tracker, FALSE, FALSE, errmsg );
+        tr_free( errmsg );
+    }
+    else
+    {
+        dbgmsg( tracker->key, "DNS lookup succeeded" );
+        tracker->addr = addr;
+        tracker->addr_expiration_time = tr_time() + 1800;
+        tau_tracker_upkeep( tracker );
+    }
+}
+
+static void
+tau_tracker_send_request( struct tau_tracker * tracker,
+                          const void * payload,
+                          size_t payload_len )
+{
+    struct evbuffer * buf = evbuffer_new( );
+    dbgmsg( tracker->key, "sending request w/connection id %"PRIu64"\n",
+                          tracker->connection_id );
+    evbuffer_add_hton_64( buf, tracker->connection_id );
+    evbuffer_add_reference( buf, payload, payload_len, NULL, NULL );
+    tau_sendto( tracker->session, tracker->addr, tracker->port,
+                evbuffer_pullup( buf, -1 ),
+                evbuffer_get_length( buf ) );
+    evbuffer_free( buf );
+}
+
+static void
+tau_tracker_upkeep( struct tau_tracker * tracker )
+{
+    int i;
+    int n;
+    tr_ptrArray * reqs;
+    const time_t now = tr_time( );
+
+    /* FIXME: look for timed-out requests */
+
+    /* if the address info is too old, expire it */
+    if( tracker->addr && ( tracker->addr_expiration_time <= now ) ) {
+        dbgmsg( tracker->host, "Expiring old DNS result" );     
+        evutil_freeaddrinfo( tracker->addr );
+        tracker->addr = NULL;
+    }
+
+    /* if no requests, there's nothing to do */
+    if( tr_ptrArrayEmpty( &tracker->announces ) && tr_ptrArrayEmpty( &tracker->scrapes ) )
+        return;
+
+    /* can't do anything without an address */
+    if( !tracker->addr ) {
+        if( !tracker->is_asking_dns ) {
+            struct evutil_addrinfo hints;
+            memset( &hints, 0, sizeof( hints ) );
+            hints.ai_family = AF_UNSPEC;
+            hints.ai_flags = EVUTIL_AI_CANONNAME;
+            hints.ai_socktype = SOCK_DGRAM;
+            hints.ai_protocol = IPPROTO_UDP;
+            tracker->is_asking_dns = TRUE;
+            dbgmsg( tracker->host, "Trying a new DNS lookup" );     
+            evdns_getaddrinfo( tracker->session->evdns_base,
+                               tracker->host, NULL, &hints, tau_tracker_on_dns, tracker );
+        }
+        return;
+    }
+
+    /* also need a valid connection ID... */
+    if( tracker->connection_expiration_time < now ) {
+        if( !tracker->is_connecting ) {
+            struct evbuffer * buf = evbuffer_new( );
+            tracker->is_connecting = TRUE;
+            tracker->connection_transaction_id = tau_transaction_new( );
+            dbgmsg( tracker->key, "Trying to connect. Transaction ID is %u",
+                    tracker->connection_transaction_id );
+            evbuffer_add_hton_64( buf, 0x41727101980LL );
+            evbuffer_add_hton_32( buf, TAU_ACTION_CONNECT );
+            evbuffer_add_hton_32( buf, tracker->connection_transaction_id );
+            tau_sendto( tracker->session, tracker->addr, tracker->port,
+                        evbuffer_pullup( buf, -1 ),
+                        evbuffer_get_length( buf ) );
+            evbuffer_free( buf );
+        }
+        return;
+    }
+
+    /* send the announce requests */
+    reqs = &tracker->announces;
+    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i ) {
+        struct tau_announce_request * req = tr_ptrArrayNth( reqs, i );
+        if( req->sent_at == 0 ) {
+            dbgmsg( tracker->key, "Sending an announce request" );
+            req->sent_at = now;
+            tau_tracker_send_request( tracker, req->payload, req->payload_len );
+        }
+    }
+
+    /* send the scrape requests */
+    reqs = &tracker->scrapes;
+    for( i=0, n=tr_ptrArraySize(reqs); i<n; ++i ) {
+        struct tau_scrape_request * req = tr_ptrArrayNth( reqs, i );
+        if( req->sent_at == 0 ) {
+            dbgmsg( tracker->key, "Sending a scrape request" );
+            req->sent_at = now;
+            tau_tracker_send_request( tracker, req->payload, req->payload_len );
+        }
+    }
+}
+
+static void
+on_tracker_connection_response( struct tau_tracker  * tracker,
+                                tau_action_t          action,
+                                struct evbuffer     * buf )
+{
+    const time_t now = tr_time( );
+
+    tracker->is_connecting = FALSE;
+    tracker->connection_transaction_id = 0;
+
+    if( action == TAU_ACTION_CONNECT )
+    {
+        tracker->connection_id = evbuffer_read_ntoh_64( buf );
+        tracker->connection_expiration_time = now + TAU_CONNECTION_TTL_SECS;
+        dbgmsg( tracker->key, "Got a new connection ID from tracker: %"PRIu64,
+                tracker->connection_id );
+    }
+    else
+    {
+        const char * errmsg = _( "Connection refused" );
+        assert( action == TAU_ACTION_ERROR );
+        dbgmsg( tracker->key, "%s", errmsg );
+        tau_tracker_fail_all( tracker, TRUE, FALSE, errmsg );
+    }
+
+    tau_tracker_upkeep( tracker );
+}
+
+/****
+*****
+*****  SESSION
+*****
+****/
+
+struct tr_announcer_udp
+{
+    /* tau_tracker */
+    tr_ptrArray trackers;
+
+    tr_session * session;
+};
+
+static struct tr_announcer_udp*
+announcer_udp_get( tr_session * session )
+{
+    struct tr_announcer_udp * tau;
+
+    if( session->announcer_udp != NULL )
+        return session->announcer_udp;
+
+    tau = tr_new0( struct tr_announcer_udp, 1 );
+    tau->trackers = TR_PTR_ARRAY_INIT;
+    tau->session = session;
+    session->announcer_udp = tau;
+    return tau;
+}
+
+/* Finds the tau_tracker struct that corresponds to this url.
+   If it doesn't exist yet, create one. */
+static struct tau_tracker *
+tau_session_get_tracker( struct tr_announcer_udp * tau, const char * url )
+{
+    int i;
+    int n;
+    int port;
+    char * host;
+    char * key;
+    struct tau_tracker * tracker = NULL;
+
+    /* see if we've already got a tracker that matches this host + port */
+    tr_urlParse( url, -1, NULL, &host, &port, NULL );
+    key = tr_strdup_printf( "%s:%d", host, port );
+    for( i=0, n=tr_ptrArraySize( &tau->trackers ); !tracker && i<n; ++i ) {
+        struct tau_tracker * tmp = tr_ptrArrayNth( &tau->trackers, i );
+        if( !tr_strcmp0( tmp->key, key ) )
+            tracker = tmp;
+    }
+
+    /* if we don't have a match, build a new tracker */
+    if( tracker == NULL )
+    {
+        tracker = tr_new0( struct tau_tracker, 1 );
+        tracker->session = tau->session;
+        tracker->key = key;
+        tracker->host = host;
+        tracker->port = port;
+        tracker->scrapes = TR_PTR_ARRAY_INIT;
+        tracker->announces = TR_PTR_ARRAY_INIT;
+        tr_ptrArrayAppend( &tau->trackers, tracker );
+        dbgmsg( tracker->key, "New tau_tracker created" );
+    }
+    else
+    {
+        tr_free( key );
+        tr_free( host );
+    }
+
+    return tracker;
+}
+
+/****
+*****
+*****  PUBLIC API
+*****
+****/
+
+tr_bool
+tau_handle_message( tr_session     * session,
+                    const uint8_t  * msg,
+                    size_t           msglen )
+{
+    int i;
+    int n;
+    struct tr_announcer_udp * tau;
+    tau_action_t action_id;
+    tau_transaction_t transaction_id;
+    struct evbuffer * buf;
+
+/*fprintf( stderr, "got an incoming udp message w/len %zu\n", msglen );*/
+    if( !session || !session->announcer_udp )
+        return FALSE;
+    if( msglen < (sizeof(uint32_t)*2) )
+        return FALSE;
+
+    /* extract the action_id and transaction_id */
+    buf = evbuffer_new( );
+    evbuffer_add_reference( buf, msg, msglen, NULL, NULL );
+    action_id      = evbuffer_read_ntoh_32( buf );
+    transaction_id = evbuffer_read_ntoh_32( buf );
+/*fprintf( stderr, "UDP got a transaction_id of %u...\n", transaction_id );*/
+    if( action_id > TAU_ACTION_MAX ) {
+        evbuffer_free( buf );
+        return FALSE;
+    }
+
+    /* look for a match to this transaction id */
+    tau = session->announcer_udp;
+    for( i=0, n=tr_ptrArraySize( &tau->trackers ); i<n; ++i )
+    {
+        int j, jn;
+        tr_ptrArray * reqs;
+        struct tau_tracker * tracker = tr_ptrArrayNth( &tau->trackers, i );
+
+        /* is it a connection response? */
+        if( tracker->is_connecting && ( transaction_id == tracker->connection_transaction_id ) )
+        {
+            dbgmsg( tracker->key, "%"PRIu32" matches my connection request!", transaction_id );
+            on_tracker_connection_response( tracker, action_id, buf );
+            evbuffer_free( buf );
+            return TRUE;
+        }
+
+        /* is it a response to one of this tracker's announces? */
+        reqs = &tracker->announces;
+        for( j=0, jn=tr_ptrArraySize(reqs); j<jn; ++j ) {
+            struct tau_announce_request * req = tr_ptrArrayNth( reqs, j );
+            if( req->sent_at && ( transaction_id == req->transaction_id ) ) {
+                dbgmsg( tracker->key, "%"PRIu32" matches one of my announce requests!", transaction_id );
+                tr_ptrArrayRemove( reqs, j );
+                on_announce_response( session, req, action_id, buf );
+                tau_announce_request_free( req );
+                evbuffer_free( buf );
+                return TRUE;
+            }
+        }
+
+        /* is it a response to one of this tracker's scrapes? */
+        reqs = &tracker->scrapes;
+        for( j=0, jn=tr_ptrArraySize(reqs); j<jn; ++j ) {
+            struct tau_scrape_request * req = tr_ptrArrayNth( reqs, j );
+            if( req->sent_at && ( transaction_id == req->transaction_id ) ) {
+                dbgmsg( tracker->key, "%"PRIu32" matches one of my scrape requests!", transaction_id );
+                tr_ptrArrayRemove( reqs, j );
+                on_scrape_response( session, req, action_id, buf );
+                tau_scrape_request_free( req );
+                evbuffer_free( buf );
+                return TRUE;
+            }
+        }
+    }
+
+    /* no match... */
+    evbuffer_free( buf );
+    return FALSE;
+}
+
+void
+tr_tracker_udp_announce( tr_session                 * session,
+                         const tr_announce_request  * request,
+                         tr_announce_response_func    response_func,
+                         void                       * user_data )
+{
+    struct tr_announcer_udp * tau = announcer_udp_get( session );
+    struct tau_tracker * tracker = tau_session_get_tracker( tau, request->url );
+    struct tau_announce_request * r = tau_announce_request_new( request, response_func, user_data );
+    tr_ptrArrayAppend( &tracker->announces, r );
+    tau_tracker_upkeep( tracker );
+}
+
+void
+tr_tracker_udp_scrape( tr_session               * session,
+                       const tr_scrape_request  * request,
+                       tr_scrape_response_func    response_func,
+                       void                     * user_data )
+{
+    struct tr_announcer_udp * tau = announcer_udp_get( session );
+    struct tau_tracker * tracker = tau_session_get_tracker( tau, request->url );
+    struct tau_scrape_request * r = tau_scrape_request_new( request, response_func, user_data );
+    tr_ptrArrayAppend( &tracker->scrapes, r );
+    tau_tracker_upkeep( tracker );
+}
index 93c471d5ba650e8294258d93a6fd38d9b43b5142..9db8eac6841a70c767b1a557cafea607ae36acf6 100644 (file)
@@ -806,6 +806,7 @@ announce_request_new( const tr_announcer  * announcer,
                       tr_announce_event     event )
 {
     tr_announce_request * req = tr_new0( tr_announce_request, 1 );
+    req->port = tr_sessionGetPublicPeerPort( announcer->session );
     req->url = tr_strdup( tier->currentTracker->announce );
     req->tracker_id_str = tr_strdup( tier->currentTracker->tracker_id_str );
     memcpy( req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH );
@@ -1045,8 +1046,10 @@ announce_request_delegate( tr_announcer               * announcer,
 
     if( !memcmp( request->url, "http", 4 ) )
         tr_tracker_http_announce( session, request, callback, callback_data );
+    else if( !memcmp( request->url, "udp://", 6 ) )
+        tr_tracker_udp_announce( session, request, callback, callback_data );
     else
-        abort();//fprintf( stderr, "can't handle [%s] yet\n", request->url );
+        tr_err( "Unsupported ur: %s", request->url );
 
     tr_free( request->tracker_id_str );
     tr_free( request->url );
@@ -1216,8 +1219,10 @@ scrape_request_delegate( tr_announcer             * announcer,
 
     if( !memcmp( request->url, "http", 4 ) )
         tr_tracker_http_scrape( session, request, callback, callback_data );
+    else if( !memcmp( request->url, "udp://", 6 ) )
+        tr_tracker_udp_scrape( session, request, callback, callback_data );
     else
-        abort();//fprintf( stderr, "can't handle [%s] yet\n", request->url );
+        tr_err( "Unsupported ur: %s", request->url );
 }
 
 static void
index 585d67fad9697e97f9cfa8fb78e553ff55e636a4..8a2ae57ec3bb6c694c09832da9c00667e882fd1c 100644 (file)
@@ -1069,6 +1069,13 @@ evbuffer_add_uint32( struct evbuffer * outbuf, uint32_t addme_hl )
     evbuffer_add( outbuf, &nl, sizeof( nl ) );
 }
 
+void
+evbuffer_add_uint64( struct evbuffer * outbuf, uint64_t addme_hll )
+{
+    const uint64_t nll = tr_htonll( addme_hll );
+    evbuffer_add( outbuf, &nll, sizeof( nll ) );
+}
+
 /***
 ****
 ***/
index 5fe3b851cd4471b1e886a741516232c0a6c1647a..e764cf19ff724c3e4a926ae7feaf61bbe4dd4b49 100644 (file)
@@ -291,6 +291,23 @@ tr_peerIoIsEncrypted( const tr_peerIo * io )
 void evbuffer_add_uint8 ( struct evbuffer * outbuf, uint8_t byte );
 void evbuffer_add_uint16( struct evbuffer * outbuf, uint16_t hs );
 void evbuffer_add_uint32( struct evbuffer * outbuf, uint32_t hl );
+void evbuffer_add_uint64( struct evbuffer * outbuf, uint64_t hll );
+
+static inline void
+evbuffer_add_hton_16( struct evbuffer * buf, uint16_t val )
+{
+   evbuffer_add_uint16( buf, val );
+}
+static inline void
+evbuffer_add_hton_32( struct evbuffer * buf, uint32_t val )
+{
+   evbuffer_add_uint32( buf, val );
+}
+static inline void
+evbuffer_add_hton_64( struct evbuffer * buf, uint64_t val )
+{
+   evbuffer_add_uint64( buf, val );
+}
 
 void tr_peerIoReadBytes( tr_peerIo        * io,
                          struct evbuffer  * inbuf,
index 4ef2064e669dadfb36b70ffb2e166c27029ac6bb..e154f2ea1c26934b946ada860d94d8f219a522e0 100644 (file)
@@ -67,7 +67,10 @@ static inline void* tr_ptrArrayBack( tr_ptrArray * array )
 
 void tr_ptrArrayErase( tr_ptrArray * t, int begin, int end );
 
-
+static inline void tr_ptrArrayRemove( tr_ptrArray * t, int pos )
+{
+    tr_ptrArrayErase( t, pos, pos+1 );
+}
 
 /** @brief Peek at the array pointer and its size, for easy iteration */
 void** tr_ptrArrayPeek( tr_ptrArray * array, int * size );
index e6d30e3514ee47a3d2a86dacf4a1b95876ab9253..f2d41539da3f59c76182320db670d192f1900c66 100644 (file)
@@ -41,8 +41,11 @@ enum
 void tr_peerIdInit( uint8_t * setme );
 
 struct event_base;
+struct evdns_base;
+
 struct tr_address;
 struct tr_announcer;
+struct tr_announcer_udp;
 struct tr_bandwidth;
 struct tr_bindsockets;
 struct tr_cache;
@@ -124,6 +127,7 @@ struct tr_session
     tr_preallocation_mode        preallocationMode;
 
     struct event_base          * event_base;
+    struct evdns_base          * evdns_base;
     struct tr_event_handle     * events;
 
     uint16_t                     peerLimitPerTorrent;
@@ -186,6 +190,7 @@ struct tr_session
     struct tr_stats_handle     * sessionStats;
 
     struct tr_announcer        * announcer;
+    struct tr_announcer_udp    * announcer_udp;
 
     tr_benc                    * metainfoLookup;
 
index 2f50ad6fef5a9976487da8ce173d1890aa095c5d..697d04c63e0120ad48bd329f73c1ce8869810d39 100644 (file)
@@ -205,7 +205,10 @@ event_callback(int s, short type UNUSED, void *sv)
     rc = recvfrom(s, buf, 4096 - 1, 0,
                   (struct sockaddr*)&from, &fromlen);
     if(rc > 0) {
-        if( buf[0] == 'd' ) {
+        if( tau_handle_message( ss, buf, rc ) ) {
+            tr_ndbg("UDP", "Received UDP Tracker packet");
+        }
+        else if( buf[0] == 'd' ) {
             /* DHT packet. */
             buf[rc] = '\0';
             tr_dhtCallback(buf, rc, (struct sockaddr*)&from, fromlen, sv);
index 98825b949fe717c5fe363be7fbd903517c20883f..46557fddbc97582a20e3d48c235f024651cc6653 100644 (file)
@@ -21,6 +21,9 @@ THE SOFTWARE.
 
 */
 
+#ifndef TR_UDP_H
+#define TR_UDP_H
+
 #ifndef __TRANSMISSION__
  #error only libtransmission should #include this header.
 #endif
@@ -28,3 +31,7 @@ THE SOFTWARE.
 void tr_udpInit( tr_session * );
 void tr_udpUninit( tr_session * );
 void tr_udpSetSocketBuffers(tr_session *);
+
+tr_bool tau_handle_message( tr_session * session, const uint8_t  * msg, size_t msglen );
+
+#endif /* #ifndef TR_UDP_H */
index 1a02c18a43e994c1ef290227b1a49f0087492382..947f02e0ca9fdf246f478c1aedcb5f5be23a07b0 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <signal.h>
 
+#include <event2/dns.h>
 #include <event2/event.h>
 
 #include "transmission.h"
@@ -223,6 +224,7 @@ static void
 libeventThreadFunc( void * veh )
 {
     struct event_base * base;
+    struct evdns_base * evdns_base;
     tr_event_handle * eh = veh;
 
 #ifndef WIN32
@@ -230,9 +232,14 @@ libeventThreadFunc( void * veh )
     signal( SIGPIPE, SIG_IGN );
 #endif
 
+    /* create the libevent bases */
     base = event_base_new( );
+    evdns_base = evdns_base_new( base, TRUE );
+
+    /* set the struct's fields */
     eh->base = base;
     eh->session->event_base = base;
+    eh->session->evdns_base = evdns_base;
     eh->session->events = eh;
 
     /* listen to the pipe's read fd */
@@ -246,6 +253,7 @@ libeventThreadFunc( void * veh )
 
     /* shut down the thread */
     tr_lockFree( eh->lock );
+    evdns_base_free( evdns_base, FALSE );
     event_base_free( base );
     eh->session->events = NULL;
     tr_free( eh );
index 1d433df77eb7ff17df0814be2e4cace46ca3d75a..f2f0d950e2283f720ecb82c7ed584dbdc718d6aa 100644 (file)
@@ -1028,7 +1028,7 @@ tr_urlIsValidTracker( const char * url )
     valid = isValidURLChars( url, len )
          && !tr_urlParse( url, len, &scheme, NULL, NULL, NULL )
          && ( scheme != NULL )
-         && ( !strcmp(scheme,"http") || !strcmp(scheme,"https") );
+         && ( !strcmp(scheme,"http") || !strcmp(scheme,"https") || !strcmp(scheme,"udp") );
 
     tr_free( scheme );
     return valid;
@@ -1649,6 +1649,36 @@ tr_realpath( const char * path, char * resolved_path )
 #endif
 }
 
+/***
+****
+***/
+
+uint64_t
+tr_ntohll( uint64_t v )
+{
+#ifdef HAVE_NTOHLL
+    return ntohll( v );
+#else
+    union { unsigned long lv[2]; unsigned long long llv; } u;
+    u.lv[0] = ntohl(v >> 32);
+    u.lv[1] = ntohl(v & 0xFFFFFFFFULL);
+    return u.llv;
+#endif
+}
+
+uint64_t
+tr_htonll( uint64_t v )
+{
+#ifdef HAVE_HTONLL
+    return htonll( v );
+#else
+    union { unsigned long lv[2]; unsigned long long llv; } u;
+    u.lv[0] = htonl(v >> 32);
+    u.lv[1] = htonl(v & 0xFFFFFFFFULL);
+    return u.llv;
+#endif
+}
+
 /***
 ****
 ****
index ab8d99a472663d1ac26f83f6cc9124dae61d6f44..2861d2139379287abddee4ba39c628839ad8bf16 100644 (file)
@@ -562,6 +562,12 @@ static inline void tr_timeUpdate( time_t now ) { __tr_current_time = now; }
 /** @brief Portability wrapper for realpath() that uses the system implementation if available */
 char* tr_realpath( const char *path, char * resolved_path );
 
+/** @brief Portability wrapper for htonll() that uses the system implementation if available */
+uint64_t tr_htonll( uint64_t );
+
+/** @brief Portability wrapper for htonll() that uses the system implementation if available */
+uint64_t tr_ntohll( uint64_t );
+
 /***
 ****
 ***/