From 3cfef5eded44de37806cb03faa7f57478d596eb5 Mon Sep 17 00:00:00 2001 From: Jordan Lee Date: Sun, 13 Mar 2011 00:18:11 +0000 Subject: [PATCH] (trunk libT) #117 "UDP tracker protocol support" -- working implementation; needs testing and valgrinding and review. --- libtransmission/Makefile.am | 1 + libtransmission/announcer-common.h | 12 + libtransmission/announcer-http.c | 2 +- libtransmission/announcer-udp.c | 780 +++++++++++++++++++++++++++++ libtransmission/announcer.c | 9 +- libtransmission/peer-io.c | 7 + libtransmission/peer-io.h | 17 + libtransmission/ptrarray.h | 5 +- libtransmission/session.h | 5 + libtransmission/tr-udp.c | 5 +- libtransmission/tr-udp.h | 7 + libtransmission/trevent.c | 8 + libtransmission/utils.c | 32 +- libtransmission/utils.h | 6 + 14 files changed, 890 insertions(+), 6 deletions(-) create mode 100644 libtransmission/announcer-udp.c diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am index ff90c7687..6450fdcdb 100644 --- a/libtransmission/Makefile.am +++ b/libtransmission/Makefile.am @@ -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 \ diff --git a/libtransmission/announcer-common.h b/libtransmission/announcer-common.h index bf8069c5b..41e050d68 100644 --- a/libtransmission/announcer-common.h +++ b/libtransmission/announcer-common.h @@ -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_ */ diff --git a/libtransmission/announcer-http.c b/libtransmission/announcer-http.c index cc6733c51..9eab32dac 100644 --- a/libtransmission/announcer-http.c +++ b/libtransmission/announcer-http.c @@ -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 index 000000000..c410efef9 --- /dev/null +++ b/libtransmission/announcer-udp.c @@ -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 +#include +#include + +#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; iresponse.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; iinfo_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; iresponse.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); isession, 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); isession, 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); isent_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); isent_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 && itrackers, 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 ); itrackers, 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); jsent_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); jsent_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 ); +} diff --git a/libtransmission/announcer.c b/libtransmission/announcer.c index 93c471d5b..9db8eac68 100644 --- a/libtransmission/announcer.c +++ b/libtransmission/announcer.c @@ -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 diff --git a/libtransmission/peer-io.c b/libtransmission/peer-io.c index 585d67fad..8a2ae57ec 100644 --- a/libtransmission/peer-io.c +++ b/libtransmission/peer-io.c @@ -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 ) ); +} + /*** **** ***/ diff --git a/libtransmission/peer-io.h b/libtransmission/peer-io.h index 5fe3b851c..e764cf19f 100644 --- a/libtransmission/peer-io.h +++ b/libtransmission/peer-io.h @@ -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, diff --git a/libtransmission/ptrarray.h b/libtransmission/ptrarray.h index 4ef2064e6..e154f2ea1 100644 --- a/libtransmission/ptrarray.h +++ b/libtransmission/ptrarray.h @@ -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 ); diff --git a/libtransmission/session.h b/libtransmission/session.h index e6d30e351..f2d41539d 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -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; diff --git a/libtransmission/tr-udp.c b/libtransmission/tr-udp.c index 2f50ad6fe..697d04c63 100644 --- a/libtransmission/tr-udp.c +++ b/libtransmission/tr-udp.c @@ -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); diff --git a/libtransmission/tr-udp.h b/libtransmission/tr-udp.h index 98825b949..46557fddb 100644 --- a/libtransmission/tr-udp.h +++ b/libtransmission/tr-udp.h @@ -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 */ diff --git a/libtransmission/trevent.c b/libtransmission/trevent.c index 1a02c18a4..947f02e0c 100644 --- a/libtransmission/trevent.c +++ b/libtransmission/trevent.c @@ -18,6 +18,7 @@ #include +#include #include #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 ); diff --git a/libtransmission/utils.c b/libtransmission/utils.c index 1d433df77..f2f0d950e 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -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 +} + /*** **** **** diff --git a/libtransmission/utils.h b/libtransmission/utils.h index ab8d99a47..2861d2139 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -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 ); + /*** **** ***/