mirror of
https://github.com/transmission/transmission
synced 2025-02-24 23:12:35 +00:00
(trunk libT) #117 "UDP tracker protocol support" -- working implementation; needs testing and valgrinding and review.
This commit is contained in:
parent
63917c907b
commit
3cfef5eded
14 changed files with 890 additions and 6 deletions
|
@ -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 \
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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,
|
||||
|
|
780
libtransmission/announcer-udp.c
Normal file
780
libtransmission/announcer-udp.c
Normal file
|
@ -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 );
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
****
|
||||
|
|
|
@ -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 );
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
|
Loading…
Reference in a new issue