mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 10:28:32 +00:00
(trunk libT) #117 "UDP tracker protocol support (BEP #15)" -- refactor announcer.c so that alternate tracker protocols can be supported.
This commit adds a set of package-visible structs and functions to allow delegating announces and scrapes to different protocol handlers. (Examples: struct tr_announce_request, struct tr_announce_response, struct tr_scrape_request, struct tr_scrape_response.) HTTP is the only protocol handler currently implemented; however, this provides a clean API for other protocol handlers, and having this in trunk will help shake out any bugs in this refactoring. In addition, logging via the TR_DEBUG_FD environment variable is vastly improved in the announcer module now.
This commit is contained in:
@@ -18,6 +18,7 @@ noinst_LIBRARIES = libtransmission.a
|
|||||||
|
|
||||||
libtransmission_a_SOURCES = \
|
libtransmission_a_SOURCES = \
|
||||||
announcer.c \
|
announcer.c \
|
||||||
|
announcer-http.c \
|
||||||
bandwidth.c \
|
bandwidth.c \
|
||||||
bencode.c \
|
bencode.c \
|
||||||
bitfield.c \
|
bitfield.c \
|
||||||
@@ -69,6 +70,7 @@ libtransmission_a_SOURCES = \
|
|||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
announcer.h \
|
announcer.h \
|
||||||
|
announcer-common.h \
|
||||||
bandwidth.h \
|
bandwidth.h \
|
||||||
bencode.h \
|
bencode.h \
|
||||||
bitfield.h \
|
bitfield.h \
|
||||||
|
|||||||
231
libtransmission/announcer-common.h
Normal file
231
libtransmission/announcer-common.h
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* 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:$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBTRANSMISSION_ANNOUNCER_MODULE___
|
||||||
|
#error only the libtransmission announcer module should #include this header.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _TR_ANNOUNCER_COMMON_H_
|
||||||
|
#define _TR_ANNOUNCER_COMMON_H_
|
||||||
|
|
||||||
|
#include "transmission.h" /* SHA_DIGEST_LENGTH */
|
||||||
|
#include "session.h" /* PEER_ID_LEN */
|
||||||
|
|
||||||
|
/***
|
||||||
|
**** SCRAPE
|
||||||
|
***/
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
/* pick a number small enough for common tracker software:
|
||||||
|
* - ocelot has no upper bound
|
||||||
|
* - opentracker has an upper bound of 64
|
||||||
|
* - udp protocol has an upper bound of 74
|
||||||
|
* - xbtt has no upper bound */
|
||||||
|
TR_MULTISCRAPE_MAX = 64
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* the scrape URL */
|
||||||
|
char * url;
|
||||||
|
|
||||||
|
/* the name to use when deep logging is enabled */
|
||||||
|
char log_name[128];
|
||||||
|
|
||||||
|
/* info hashes of the torrents to scrape */
|
||||||
|
uint8_t info_hash[TR_MULTISCRAPE_MAX][SHA_DIGEST_LENGTH];
|
||||||
|
|
||||||
|
/* how many hashes to use in the info_hash field */
|
||||||
|
int info_hash_count;
|
||||||
|
}
|
||||||
|
tr_scrape_request;
|
||||||
|
|
||||||
|
struct tr_scrape_response_row
|
||||||
|
{
|
||||||
|
/* the torrent's info_hash */
|
||||||
|
uint8_t info_hash[SHA_DIGEST_LENGTH];
|
||||||
|
|
||||||
|
/* how many peers are seeding this torrent */
|
||||||
|
int seeders;
|
||||||
|
|
||||||
|
/* how many peers are downloading this torrent */
|
||||||
|
int leechers;
|
||||||
|
|
||||||
|
/* how many times this torrent has been downloaded */
|
||||||
|
int downloads;
|
||||||
|
|
||||||
|
/* the number of active downloaders in the swarm.
|
||||||
|
* this is a BEP 21 extension that some trackers won't support.
|
||||||
|
* http://www.bittorrent.org/beps/bep_0021.html#tracker-scrapes */
|
||||||
|
int downloaders;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* whether or not we managed to connect to the tracker */
|
||||||
|
tr_bool did_connect;
|
||||||
|
|
||||||
|
/* whether or not the scrape timed out */
|
||||||
|
tr_bool did_timeout;
|
||||||
|
|
||||||
|
/* how many info hashes are in the 'rows' field */
|
||||||
|
int row_count;
|
||||||
|
|
||||||
|
/* the individual torrents' scrape results */
|
||||||
|
struct tr_scrape_response_row rows[TR_MULTISCRAPE_MAX];
|
||||||
|
|
||||||
|
/* the raw scrape url */
|
||||||
|
char * url;
|
||||||
|
|
||||||
|
/* human-readable error string on failure, or NULL */
|
||||||
|
char * errmsg;
|
||||||
|
|
||||||
|
/* minimum interval (in seconds) allowed between scrapes.
|
||||||
|
* this is an unofficial extension that some trackers won't support. */
|
||||||
|
int min_request_interval;
|
||||||
|
}
|
||||||
|
tr_scrape_response;
|
||||||
|
|
||||||
|
typedef void tr_scrape_response_func( tr_session * session,
|
||||||
|
const tr_scrape_response * response,
|
||||||
|
void * user_data );
|
||||||
|
|
||||||
|
void tr_tracker_http_scrape( tr_session * session,
|
||||||
|
const tr_scrape_request * req,
|
||||||
|
tr_scrape_response_func response_func,
|
||||||
|
void * user_data );
|
||||||
|
|
||||||
|
/***
|
||||||
|
**** ANNOUNCE
|
||||||
|
***/
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
TR_ANNOUNCE_EVENT_NONE,
|
||||||
|
TR_ANNOUNCE_EVENT_COMPLETED,
|
||||||
|
TR_ANNOUNCE_EVENT_STARTED,
|
||||||
|
TR_ANNOUNCE_EVENT_STOPPED
|
||||||
|
}
|
||||||
|
tr_announce_event;
|
||||||
|
|
||||||
|
const char * tr_announce_event_get_string( tr_announce_event );
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
tr_announce_event event;
|
||||||
|
tr_bool partial_seed;
|
||||||
|
|
||||||
|
/* per-session key */
|
||||||
|
int key;
|
||||||
|
|
||||||
|
/* the number of peers we'd like to get back in the response */
|
||||||
|
int numwant;
|
||||||
|
|
||||||
|
/* the number of bytes we uploaded since the last 'started' event */
|
||||||
|
uint64_t up;
|
||||||
|
|
||||||
|
/* the number of good bytes we downloaded since the last 'started' event */
|
||||||
|
uint64_t down;
|
||||||
|
|
||||||
|
/* the number of bad bytes we downloaded since the last 'started' event */
|
||||||
|
uint64_t corrupt;
|
||||||
|
|
||||||
|
/* the total size of the torrent minus the number of bytes completed */
|
||||||
|
uint64_t left;
|
||||||
|
|
||||||
|
/* the tracker's announce URL */
|
||||||
|
char * url;
|
||||||
|
|
||||||
|
/* key generated by and returned from an http tracker.
|
||||||
|
* see tr_announce_response.tracker_id_str */
|
||||||
|
char * tracker_id_str;
|
||||||
|
|
||||||
|
/* the torrent's peer id.
|
||||||
|
* this changes when a torrent is stopped -> restarted. */
|
||||||
|
char peer_id[PEER_ID_LEN];
|
||||||
|
|
||||||
|
/* the torrent's info_hash */
|
||||||
|
uint8_t info_hash[SHA_DIGEST_LENGTH];
|
||||||
|
|
||||||
|
/* the name to use when deep logging is enabled */
|
||||||
|
char log_name[128];
|
||||||
|
}
|
||||||
|
tr_announce_request;
|
||||||
|
|
||||||
|
struct tr_pex;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* the torrent's info hash */
|
||||||
|
uint8_t info_hash[SHA_DIGEST_LENGTH];
|
||||||
|
|
||||||
|
/* whether or not we managed to connect to the tracker */
|
||||||
|
tr_bool did_connect;
|
||||||
|
|
||||||
|
/* whether or not the scrape timed out */
|
||||||
|
tr_bool did_timeout;
|
||||||
|
|
||||||
|
/* preferred interval between announces.
|
||||||
|
* transmission treats this as the interval for periodic announces */
|
||||||
|
int interval;
|
||||||
|
|
||||||
|
/* minimum interval between announces. (optional)
|
||||||
|
* transmission treats this as the min interval for manual announces */
|
||||||
|
int min_interval;
|
||||||
|
|
||||||
|
/* how many peers are seeding this torrent */
|
||||||
|
int seeders;
|
||||||
|
|
||||||
|
/* how many peers are downloading this torrent */
|
||||||
|
int leechers;
|
||||||
|
|
||||||
|
/* how many times this torrent has been downloaded */
|
||||||
|
int downloads;
|
||||||
|
|
||||||
|
/* number of items in the 'pex' field */
|
||||||
|
size_t pex_count;
|
||||||
|
|
||||||
|
/* IPv4 peers that we acquired from the tracker */
|
||||||
|
struct tr_pex * pex;
|
||||||
|
|
||||||
|
/* number of items in the 'pex6' field */
|
||||||
|
size_t pex6_count;
|
||||||
|
|
||||||
|
/* IPv6 peers that we acquired from the tracker */
|
||||||
|
struct tr_pex * pex6;
|
||||||
|
|
||||||
|
/* human-readable error string on failure, or NULL */
|
||||||
|
char * errmsg;
|
||||||
|
|
||||||
|
/* human-readable warning string or NULL */
|
||||||
|
char * warning;
|
||||||
|
|
||||||
|
/* key generated by and returned from an http tracker.
|
||||||
|
* if this is provided, subsequent http announces must include this. */
|
||||||
|
char * tracker_id_str;
|
||||||
|
}
|
||||||
|
tr_announce_response;
|
||||||
|
|
||||||
|
typedef void tr_announce_response_func( tr_session * session,
|
||||||
|
const tr_announce_response * response,
|
||||||
|
void * userdata );
|
||||||
|
|
||||||
|
void tr_tracker_http_announce( tr_session * session,
|
||||||
|
const tr_announce_request * req,
|
||||||
|
tr_announce_response_func response_func,
|
||||||
|
void * user_data );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _TR_ANNOUNCER_COMMON_H_ */
|
||||||
447
libtransmission/announcer-http.c
Normal file
447
libtransmission/announcer-http.c
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
/*
|
||||||
|
* 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:$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <event2/buffer.h>
|
||||||
|
#include <event2/http.h> /* for HTTP_OK */
|
||||||
|
|
||||||
|
#define __LIBTRANSMISSION_ANNOUNCER_MODULE___
|
||||||
|
|
||||||
|
#include "transmission.h"
|
||||||
|
#include "announcer-common.h"
|
||||||
|
#include "limits.h"
|
||||||
|
#include "net.h" /* tr_globalIPv6() */
|
||||||
|
#include "peer-mgr.h" /* pex */
|
||||||
|
#include "torrent.h"
|
||||||
|
#include "trevent.h" /* tr_runInEventThread() */
|
||||||
|
#include "web.h" /* tr_http_escape() */
|
||||||
|
|
||||||
|
#define dbgmsg( name, ... ) \
|
||||||
|
if( tr_deepLoggingIsActive( ) ) do { \
|
||||||
|
tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
/****
|
||||||
|
*****
|
||||||
|
***** ANNOUNCE
|
||||||
|
*****
|
||||||
|
****/
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
get_event_string( const tr_announce_request * req )
|
||||||
|
{
|
||||||
|
if( req->partial_seed )
|
||||||
|
if( req->event != TR_ANNOUNCE_EVENT_STOPPED )
|
||||||
|
return "paused";
|
||||||
|
|
||||||
|
return tr_announce_event_get_string( req->event );
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
announce_url_new( const tr_session * session, const tr_announce_request * req )
|
||||||
|
{
|
||||||
|
const char * str;
|
||||||
|
const unsigned char * ipv6;
|
||||||
|
struct evbuffer * buf = evbuffer_new( );
|
||||||
|
char escaped_info_hash[SHA_DIGEST_LENGTH*3 + 1];
|
||||||
|
|
||||||
|
tr_http_escape_sha1( escaped_info_hash, req->info_hash );
|
||||||
|
|
||||||
|
evbuffer_expand( buf, 1024 );
|
||||||
|
|
||||||
|
evbuffer_add_printf( buf, "%s"
|
||||||
|
"%c"
|
||||||
|
"info_hash=%s"
|
||||||
|
"&peer_id=%*.*s"
|
||||||
|
"&port=%d"
|
||||||
|
"&uploaded=%" PRIu64
|
||||||
|
"&downloaded=%" PRIu64
|
||||||
|
"&left=%" PRIu64
|
||||||
|
"&numwant=%d"
|
||||||
|
"&key=%x"
|
||||||
|
"&compact=1"
|
||||||
|
"&supportcrypto=1",
|
||||||
|
req->url,
|
||||||
|
strchr( req->url, '?' ) ? '&' : '?',
|
||||||
|
escaped_info_hash,
|
||||||
|
PEER_ID_LEN, PEER_ID_LEN, req->peer_id,
|
||||||
|
(int)tr_sessionGetPublicPeerPort( session ),
|
||||||
|
req->up,
|
||||||
|
req->down,
|
||||||
|
req->left,
|
||||||
|
req->numwant,
|
||||||
|
req->key );
|
||||||
|
|
||||||
|
if( session->encryptionMode == TR_ENCRYPTION_REQUIRED )
|
||||||
|
evbuffer_add_printf( buf, "&requirecrypto=1" );
|
||||||
|
|
||||||
|
if( req->corrupt )
|
||||||
|
evbuffer_add_printf( buf, "&corrupt=%" PRIu64, req->corrupt );
|
||||||
|
|
||||||
|
str = get_event_string( req );
|
||||||
|
if( str && *str )
|
||||||
|
evbuffer_add_printf( buf, "&event=%s", str );
|
||||||
|
|
||||||
|
str = req->tracker_id_str;
|
||||||
|
if( str && *str )
|
||||||
|
evbuffer_add_printf( buf, "&trackerid=%s", str );
|
||||||
|
|
||||||
|
/* There are two incompatible techniques for announcing an IPv6 address.
|
||||||
|
BEP-7 suggests adding an "ipv6=" parameter to the announce URL,
|
||||||
|
while OpenTracker requires that peers announce twice, once over IPv4
|
||||||
|
and once over IPv6.
|
||||||
|
|
||||||
|
To be safe, we should do both: add the "ipv6=" parameter and
|
||||||
|
announce twice. At any rate, we're already computing our IPv6
|
||||||
|
address (for the LTEP handshake), so this comes for free. */
|
||||||
|
|
||||||
|
ipv6 = tr_globalIPv6( );
|
||||||
|
if( ipv6 ) {
|
||||||
|
char ipv6_readable[INET6_ADDRSTRLEN];
|
||||||
|
inet_ntop( AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN );
|
||||||
|
evbuffer_add_printf( buf, "&ipv6=");
|
||||||
|
tr_http_escape( buf, ipv6_readable, -1, TRUE );
|
||||||
|
}
|
||||||
|
|
||||||
|
return evbuffer_free_to_str( buf );
|
||||||
|
}
|
||||||
|
|
||||||
|
static tr_pex*
|
||||||
|
listToPex( tr_benc * peerList, size_t * setme_len )
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
size_t n;
|
||||||
|
const size_t len = tr_bencListSize( peerList );
|
||||||
|
tr_pex * pex = tr_new0( tr_pex, len );
|
||||||
|
|
||||||
|
for( i=n=0; i<len; ++i )
|
||||||
|
{
|
||||||
|
int64_t port;
|
||||||
|
const char * ip;
|
||||||
|
tr_address addr;
|
||||||
|
tr_benc * peer = tr_bencListChild( peerList, i );
|
||||||
|
|
||||||
|
if( peer == NULL )
|
||||||
|
continue;
|
||||||
|
if( !tr_bencDictFindStr( peer, "ip", &ip ) )
|
||||||
|
continue;
|
||||||
|
if( tr_pton( ip, &addr ) == NULL )
|
||||||
|
continue;
|
||||||
|
if( !tr_bencDictFindInt( peer, "port", &port ) )
|
||||||
|
continue;
|
||||||
|
if( ( port < 0 ) || ( port > USHRT_MAX ) )
|
||||||
|
continue;
|
||||||
|
if( !tr_isValidPeerAddress( &addr, port ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pex[n].addr = addr;
|
||||||
|
pex[n].port = htons( (uint16_t)port );
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
|
||||||
|
*setme_len = n;
|
||||||
|
return pex;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct announce_data
|
||||||
|
{
|
||||||
|
int torrent_id;
|
||||||
|
tr_session * session;
|
||||||
|
tr_announce_response response;
|
||||||
|
tr_announce_response_func * response_func;
|
||||||
|
void * response_func_user_data;
|
||||||
|
char log_name[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_announce_done_eventthread( void * vdata )
|
||||||
|
{
|
||||||
|
struct announce_data * data = vdata;
|
||||||
|
|
||||||
|
if( data->response_func != NULL )
|
||||||
|
data->response_func( data->session,
|
||||||
|
&data->response,
|
||||||
|
data->response_func_user_data );
|
||||||
|
|
||||||
|
tr_free( data->response.pex6 );
|
||||||
|
tr_free( data->response.pex );
|
||||||
|
tr_free( data->response.tracker_id_str );
|
||||||
|
tr_free( data->response.warning );
|
||||||
|
tr_free( data->response.errmsg );
|
||||||
|
tr_free( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_announce_done( tr_session * session,
|
||||||
|
tr_bool did_connect,
|
||||||
|
tr_bool did_timeout,
|
||||||
|
long response_code,
|
||||||
|
const void * msg,
|
||||||
|
size_t msglen,
|
||||||
|
void * vdata )
|
||||||
|
{
|
||||||
|
tr_announce_response * response;
|
||||||
|
struct announce_data * data = vdata;
|
||||||
|
|
||||||
|
response = &data->response;
|
||||||
|
response->did_connect = did_connect;
|
||||||
|
response->did_timeout = did_timeout;
|
||||||
|
dbgmsg( data->log_name, "Got announce response" );
|
||||||
|
|
||||||
|
if( response_code != HTTP_OK )
|
||||||
|
{
|
||||||
|
const char * fmt = _( "Tracker gave HTTP response code %1$ld (%2$s)" );
|
||||||
|
const char * response_str = tr_webGetResponseStr( response_code );
|
||||||
|
response->errmsg = tr_strdup_printf( fmt, response_code, response_str );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tr_benc benc;
|
||||||
|
const int benc_loaded = !tr_bencLoad( msg, msglen, &benc, NULL );
|
||||||
|
|
||||||
|
if( getenv( "TR_CURL_VERBOSE" ) != NULL )
|
||||||
|
{
|
||||||
|
char * str = tr_bencToStr( &benc, TR_FMT_JSON, NULL );
|
||||||
|
fprintf( stderr, "Announce response:\n< %s\n", str );
|
||||||
|
tr_free( str );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( benc_loaded && tr_bencIsDict( &benc ) )
|
||||||
|
{
|
||||||
|
int64_t i;
|
||||||
|
size_t rawlen;
|
||||||
|
tr_benc * tmp;
|
||||||
|
const char * str;
|
||||||
|
const uint8_t * raw;
|
||||||
|
|
||||||
|
if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
|
||||||
|
response->errmsg = tr_strdup( str );
|
||||||
|
|
||||||
|
if( tr_bencDictFindStr( &benc, "warning message", &str ) )
|
||||||
|
response->warning = tr_strdup( str );
|
||||||
|
|
||||||
|
if( tr_bencDictFindInt( &benc, "interval", &i ) )
|
||||||
|
response->interval = i;
|
||||||
|
|
||||||
|
if( tr_bencDictFindInt( &benc, "min interval", &i ) )
|
||||||
|
response->min_interval = i;
|
||||||
|
|
||||||
|
if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
|
||||||
|
response->tracker_id_str = tr_strdup( str );
|
||||||
|
|
||||||
|
if( tr_bencDictFindInt( &benc, "complete", &i ) )
|
||||||
|
response->seeders = i;
|
||||||
|
|
||||||
|
if( tr_bencDictFindInt( &benc, "incomplete", &i ) )
|
||||||
|
response->leechers = i;
|
||||||
|
|
||||||
|
if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
|
||||||
|
response->downloads = i;
|
||||||
|
|
||||||
|
if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) ) {
|
||||||
|
dbgmsg( data->log_name, "got a peers6 length of %zu", rawlen );
|
||||||
|
response->pex6 = tr_peerMgrCompact6ToPex( raw, rawlen,
|
||||||
|
NULL, 0, &response->pex6_count );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) ) {
|
||||||
|
dbgmsg( data->log_name, "got a compact peers length of %zu", rawlen );
|
||||||
|
response->pex = tr_peerMgrCompactToPex( raw, rawlen,
|
||||||
|
NULL, 0, &response->pex_count );
|
||||||
|
} else if( tr_bencDictFindList( &benc, "peers", &tmp ) ) {
|
||||||
|
response->pex = listToPex( tmp, &response->pex_count );
|
||||||
|
dbgmsg( data->log_name, "got a peers list with %zu entries",
|
||||||
|
response->pex_count );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( benc_loaded )
|
||||||
|
tr_bencFree( &benc );
|
||||||
|
}
|
||||||
|
|
||||||
|
tr_runInEventThread( session, on_announce_done_eventthread, data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tr_tracker_http_announce( tr_session * session,
|
||||||
|
const tr_announce_request * request,
|
||||||
|
tr_announce_response_func response_func,
|
||||||
|
void * response_func_user_data )
|
||||||
|
{
|
||||||
|
struct announce_data * d;
|
||||||
|
char * url = announce_url_new( session, request );
|
||||||
|
|
||||||
|
d = tr_new0( struct announce_data, 1 );
|
||||||
|
d->session = session;
|
||||||
|
d->response_func = response_func;
|
||||||
|
d->response_func_user_data = response_func_user_data;
|
||||||
|
memcpy( d->response.info_hash, request->info_hash, SHA_DIGEST_LENGTH );
|
||||||
|
tr_strlcpy( d->log_name, request->log_name, sizeof( d->log_name ) );
|
||||||
|
|
||||||
|
dbgmsg( request->log_name, "Sending announce to libcurl: \"%s\"", url );
|
||||||
|
tr_webRun( session, url, NULL, on_announce_done, d );
|
||||||
|
tr_free( url );
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
*****
|
||||||
|
***** SCRAPE
|
||||||
|
*****
|
||||||
|
****/
|
||||||
|
|
||||||
|
struct scrape_data
|
||||||
|
{
|
||||||
|
tr_session * session;
|
||||||
|
tr_scrape_response response;
|
||||||
|
tr_scrape_response_func * response_func;
|
||||||
|
void * response_func_user_data;
|
||||||
|
char log_name[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_scrape_done_eventthread( void * vdata )
|
||||||
|
{
|
||||||
|
struct scrape_data * data = vdata;
|
||||||
|
|
||||||
|
if( data->response_func != NULL )
|
||||||
|
data->response_func( data->session,
|
||||||
|
&data->response,
|
||||||
|
data->response_func_user_data );
|
||||||
|
|
||||||
|
tr_free( data->response.errmsg );
|
||||||
|
tr_free( data->response.url );
|
||||||
|
tr_free( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_scrape_done( tr_session * session,
|
||||||
|
tr_bool did_connect,
|
||||||
|
tr_bool did_timeout,
|
||||||
|
long response_code,
|
||||||
|
const void * msg,
|
||||||
|
size_t msglen,
|
||||||
|
void * vdata )
|
||||||
|
{
|
||||||
|
tr_scrape_response * response;
|
||||||
|
struct scrape_data * data = vdata;
|
||||||
|
|
||||||
|
response = &data->response;
|
||||||
|
response->did_connect = did_connect;
|
||||||
|
response->did_timeout = did_timeout;
|
||||||
|
dbgmsg( data->log_name, "Got scrape response for \"%s\"", response->url );
|
||||||
|
|
||||||
|
if( response_code != HTTP_OK )
|
||||||
|
{
|
||||||
|
const char * fmt = _( "Tracker gave HTTP response code %1$ld (%2$s)" );
|
||||||
|
const char * response_str = tr_webGetResponseStr( response_code );
|
||||||
|
response->errmsg = tr_strdup_printf( fmt, response_code, response_str );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tr_benc top;
|
||||||
|
int64_t intVal;
|
||||||
|
tr_benc * files;
|
||||||
|
const char * str;
|
||||||
|
const int benc_loaded = !tr_bencLoad( msg, msglen, &top, NULL );
|
||||||
|
if( benc_loaded )
|
||||||
|
{
|
||||||
|
if( tr_bencDictFindStr( &top, "failure reason", &str ) )
|
||||||
|
response->errmsg = tr_strdup( str );
|
||||||
|
|
||||||
|
if( tr_bencDictFindInt( &top, "min_request_interval", &intVal ) )
|
||||||
|
response->min_request_interval = intVal;
|
||||||
|
|
||||||
|
if( tr_bencDictFindDict( &top, "files", &files ) )
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
tr_benc * val;
|
||||||
|
const char * key;
|
||||||
|
|
||||||
|
/* get the next "file" */
|
||||||
|
if( !tr_bencDictChild( files, i++, &key, &val ) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* populate the corresponding row in our response array */
|
||||||
|
for( j=0; j<response->row_count; ++j )
|
||||||
|
{
|
||||||
|
struct tr_scrape_response_row * row = &response->rows[j];
|
||||||
|
if( !memcmp( key, row->info_hash, SHA_DIGEST_LENGTH ) )
|
||||||
|
{
|
||||||
|
if( tr_bencDictFindInt( val, "complete", &intVal ) )
|
||||||
|
row->seeders = intVal;
|
||||||
|
if( tr_bencDictFindInt( val, "incomplete", &intVal ) )
|
||||||
|
row->leechers = intVal;
|
||||||
|
if( tr_bencDictFindInt( val, "downloaded", &intVal ) )
|
||||||
|
row->downloads = intVal;
|
||||||
|
if( tr_bencDictFindInt( val, "downloaders", &intVal ) )
|
||||||
|
row->downloaders = intVal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr_bencFree( &top );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr_runInEventThread( session, on_scrape_done_eventthread, data );
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
scrape_url_new( const tr_scrape_request * req )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char delimiter;
|
||||||
|
struct evbuffer * buf = evbuffer_new( );
|
||||||
|
|
||||||
|
evbuffer_add_printf( buf, "%s", req->url );
|
||||||
|
delimiter = strchr( req->url, '?' ) ? '&' : '?';
|
||||||
|
for( i=0; i<req->info_hash_count; ++i )
|
||||||
|
{
|
||||||
|
char str[SHA_DIGEST_LENGTH*3 + 1];
|
||||||
|
tr_http_escape_sha1( str, req->info_hash[i] );
|
||||||
|
evbuffer_add_printf( buf, "%cinfo_hash=%s", delimiter, str );
|
||||||
|
delimiter = '&';
|
||||||
|
}
|
||||||
|
|
||||||
|
return evbuffer_free_to_str( buf );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tr_tracker_http_scrape( tr_session * session,
|
||||||
|
const tr_scrape_request * request,
|
||||||
|
tr_scrape_response_func response_func,
|
||||||
|
void * response_func_user_data )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct scrape_data * d;
|
||||||
|
char * url = scrape_url_new( request );
|
||||||
|
|
||||||
|
d = tr_new0( struct scrape_data, 1 );
|
||||||
|
d->session = session;
|
||||||
|
d->response.url = tr_strdup( request->url );
|
||||||
|
d->response_func = response_func;
|
||||||
|
d->response_func_user_data = response_func_user_data;
|
||||||
|
d->response.row_count = request->info_hash_count;
|
||||||
|
for( i=0; i<d->response.row_count; ++i )
|
||||||
|
memcpy( d->response.rows[i].info_hash, request->info_hash[i], SHA_DIGEST_LENGTH );
|
||||||
|
tr_strlcpy( d->log_name, request->log_name, sizeof( d->log_name ) );
|
||||||
|
|
||||||
|
dbgmsg( request->log_name, "Sending scrape to libcurl: \"%s\"", url );
|
||||||
|
tr_webRun( session, url, NULL, on_scrape_done, d );
|
||||||
|
tr_free( url );
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -383,32 +383,6 @@ geturllist( tr_info * inf,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
is_rfc2396_alnum( char ch )
|
|
||||||
{
|
|
||||||
return ( '0' <= ch && ch <= '9' )
|
|
||||||
|| ( 'A' <= ch && ch <= 'Z' )
|
|
||||||
|| ( 'a' <= ch && ch <= 'z' )
|
|
||||||
|| ch == '.'
|
|
||||||
|| ch == '-'
|
|
||||||
|| ch == '_'
|
|
||||||
|| ch == '~';
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
escape( char * out, const uint8_t * in, size_t in_len ) /* rfc2396 */
|
|
||||||
{
|
|
||||||
const uint8_t *end = in + in_len;
|
|
||||||
|
|
||||||
while( in != end )
|
|
||||||
if( is_rfc2396_alnum( *in ) )
|
|
||||||
*out++ = (char) *in++;
|
|
||||||
else
|
|
||||||
out += tr_snprintf( out, 4, "%%%02x", (unsigned int)*in++ );
|
|
||||||
|
|
||||||
*out = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char*
|
static const char*
|
||||||
tr_metainfoParseImpl( const tr_session * session,
|
tr_metainfoParseImpl( const tr_session * session,
|
||||||
tr_info * inf,
|
tr_info * inf,
|
||||||
@@ -446,7 +420,6 @@ tr_metainfoParseImpl( const tr_session * session,
|
|||||||
return "info_hash";
|
return "info_hash";
|
||||||
memcpy( inf->hash, raw, raw_len );
|
memcpy( inf->hash, raw, raw_len );
|
||||||
tr_sha1_to_hex( inf->hashString, inf->hash );
|
tr_sha1_to_hex( inf->hashString, inf->hash );
|
||||||
escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
|
|
||||||
|
|
||||||
/* maybe get the display name */
|
/* maybe get the display name */
|
||||||
if( tr_bencDictFindStr( d, "display-name", &str ) ) {
|
if( tr_bencDictFindStr( d, "display-name", &str ) ) {
|
||||||
@@ -468,7 +441,6 @@ tr_metainfoParseImpl( const tr_session * session,
|
|||||||
char * bstr = tr_bencToStr( infoDict, TR_FMT_BENC, &len );
|
char * bstr = tr_bencToStr( infoDict, TR_FMT_BENC, &len );
|
||||||
tr_sha1( inf->hash, bstr, len, NULL );
|
tr_sha1( inf->hash, bstr, len, NULL );
|
||||||
tr_sha1_to_hex( inf->hashString, inf->hash );
|
tr_sha1_to_hex( inf->hashString, inf->hash );
|
||||||
escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
|
|
||||||
|
|
||||||
if( infoDictLength != NULL )
|
if( infoDictLength != NULL )
|
||||||
*infoDictLength = len;
|
*infoDictLength = len;
|
||||||
|
|||||||
@@ -1677,9 +1677,6 @@ struct tr_info
|
|||||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||||
char hashString[2 * SHA_DIGEST_LENGTH + 1];
|
char hashString[2 * SHA_DIGEST_LENGTH + 1];
|
||||||
|
|
||||||
/* hash, escaped as per rfc2396 for tracker announces */
|
|
||||||
char hashEscaped[3 * SHA_DIGEST_LENGTH + 1];
|
|
||||||
|
|
||||||
/* Flags */
|
/* Flags */
|
||||||
tr_bool isPrivate;
|
tr_bool isPrivate;
|
||||||
tr_bool isMultifile;
|
tr_bool isMultifile;
|
||||||
|
|||||||
@@ -509,3 +509,30 @@ tr_http_unescape( const char * str, int len )
|
|||||||
curl_free( tmp );
|
curl_free( tmp );
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_rfc2396_alnum( uint8_t ch )
|
||||||
|
{
|
||||||
|
return ( '0' <= ch && ch <= '9' )
|
||||||
|
|| ( 'A' <= ch && ch <= 'Z' )
|
||||||
|
|| ( 'a' <= ch && ch <= 'z' )
|
||||||
|
|| ch == '.'
|
||||||
|
|| ch == '-'
|
||||||
|
|| ch == '_'
|
||||||
|
|| ch == '~';
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tr_http_escape_sha1( char * out, const uint8_t * sha1_digest )
|
||||||
|
{
|
||||||
|
const uint8_t * in = sha1_digest;
|
||||||
|
const uint8_t * end = in + SHA_DIGEST_LENGTH;
|
||||||
|
|
||||||
|
while( in != end )
|
||||||
|
if( is_rfc2396_alnum( *in ) )
|
||||||
|
*out++ = (char) *in++;
|
||||||
|
else
|
||||||
|
out += tr_snprintf( out, 4, "%%%02x", (unsigned int)*in++ );
|
||||||
|
|
||||||
|
*out = '\0';
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ void tr_webRunWithBuffer( tr_session * session,
|
|||||||
|
|
||||||
void tr_http_escape( struct evbuffer *out, const char *str, int len, tr_bool escape_slashes );
|
void tr_http_escape( struct evbuffer *out, const char *str, int len, tr_bool escape_slashes );
|
||||||
|
|
||||||
|
void tr_http_escape_sha1( char * out, const uint8_t * sha1_digest );
|
||||||
|
|
||||||
char* tr_http_unescape( const char * str, int len );
|
char* tr_http_unescape( const char * str, int len );
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -197,14 +197,17 @@ doScrape( const tr_info * inf )
|
|||||||
struct evbuffer * buf;
|
struct evbuffer * buf;
|
||||||
const char * scrape = inf->trackers[i].scrape;
|
const char * scrape = inf->trackers[i].scrape;
|
||||||
char * url;
|
char * url;
|
||||||
|
char escaped[SHA_DIGEST_LENGTH*3 + 1];
|
||||||
|
|
||||||
if( scrape == NULL )
|
if( scrape == NULL )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
tr_http_escape_sha1( escaped, inf->hash );
|
||||||
|
|
||||||
url = tr_strdup_printf( "%s%cinfo_hash=%s",
|
url = tr_strdup_printf( "%s%cinfo_hash=%s",
|
||||||
scrape,
|
scrape,
|
||||||
strchr( scrape, '?' ) ? '&' : '?',
|
strchr( scrape, '?' ) ? '&' : '?',
|
||||||
inf->hashEscaped );
|
escaped );
|
||||||
|
|
||||||
printf( "%s ... ", url );
|
printf( "%s ... ", url );
|
||||||
fflush( stdout );
|
fflush( stdout );
|
||||||
|
|||||||
Reference in New Issue
Block a user