Prepare opentracker for dual stack capabilities

master
Dirk Engling 8 months ago
parent eb8834f778
commit 2afc4893bf

@ -20,36 +20,36 @@
#include "ot_accesslist.h" #include "ot_accesslist.h"
/* Returns amount of removed peers */ /* Returns amount of removed peers */
static ssize_t clean_single_bucket( ot_peer *peers, size_t peer_count, time_t timedout, int *removed_seeders ) { static ssize_t clean_single_bucket( ot_peer *peers, size_t peer_count, size_t peer_size, time_t timedout, int *removed_seeders ) {
ot_peer *last_peer = peers + peer_count, *insert_point; ot_peer *last_peer = peers + peer_count * peer_size, *insert_point;
time_t timediff;
/* Two scan modes: unless there is one peer removed, just increase ot_peertime */ /* Two scan modes: unless there is one peer removed, just increase ot_peertime */
while( peers < last_peer ) { while( peers < last_peer ) {
if( ( timediff = timedout + OT_PEERTIME( peers ) ) >= OT_PEER_TIMEOUT ) time_t timediff = timedout + OT_PEERTIME( peers, peer_size );
if( timediff >= OT_PEER_TIMEOUT )
break; break;
OT_PEERTIME( peers++ ) = timediff; OT_PEERTIME( peers, peer_size ) = timediff;
peers += peer_size;
} }
/* If we at least remove one peer, we have to copy */ /* If we at least remove one peer, we have to copy */
insert_point = peers; for( insert_point = peers; peers < last_peer; peers += peer_size ) {
while( peers < last_peer ) time_t timediff = timedout + OT_PEERTIME( peers, peer_size );
if( ( timediff = timedout + OT_PEERTIME( peers ) ) < OT_PEER_TIMEOUT ) {
OT_PEERTIME( peers ) = timediff; if( timediff < OT_PEER_TIMEOUT ) {
memcpy( insert_point++, peers++, sizeof(ot_peer)); OT_PEERTIME( peers, peer_size ) = timediff;
memcpy( insert_point, peers, peer_size);
insert_point += peer_size;
} else } else
if( OT_PEERFLAG( peers++ ) & PEER_FLAG_SEEDING ) if( OT_PEERFLAG_D( peers, peer_size ) & PEER_FLAG_SEEDING )
(*removed_seeders)++; (*removed_seeders)++;
}
return peers - insert_point; return peers - insert_point;
} }
/* Clean a single torrent int clean_single_peer_list( ot_peerlist *peer_list, size_t peer_size ) {
return 1 if torrent timed out ot_vector *peer_vector = &peer_list->peers;
*/
int clean_single_torrent( ot_torrent *torrent ) {
ot_peerlist *peer_list = torrent->peer_list;
ot_vector *bucket_list = &peer_list->peers;
time_t timedout = (time_t)( g_now_minutes - peer_list->base ); time_t timedout = (time_t)( g_now_minutes - peer_list->base );
int num_buckets = 1, removed_seeders = 0; int num_buckets = 1, removed_seeders = 0;
@ -69,24 +69,26 @@ int clean_single_torrent( ot_torrent *torrent ) {
} }
if( OT_PEERLIST_HASBUCKETS( peer_list ) ) { if( OT_PEERLIST_HASBUCKETS( peer_list ) ) {
num_buckets = bucket_list->size; num_buckets = peer_vector->size;
bucket_list = (ot_vector *)bucket_list->data; peer_vector = (ot_vector *)peer_vector->data;
} }
while( num_buckets-- ) { while( num_buckets-- ) {
size_t removed_peers = clean_single_bucket( bucket_list->data, bucket_list->size, timedout, &removed_seeders ); size_t removed_peers = clean_single_bucket( peer_vector->data, peer_vector->size, peer_size, timedout, &removed_seeders );
peer_list->peer_count -= removed_peers; peer_list->peer_count -= removed_peers;
bucket_list->size -= removed_peers; peer_vector->size -= removed_peers;
if( bucket_list->size < removed_peers ) if( removed_peers )
vector_fixup_peers( bucket_list ); vector_fixup_peers( peer_vector, peer_size );
++bucket_list;
/* Skip to next bucket, a vector containing peers */
++peer_vector;
} }
peer_list->seed_count -= removed_seeders; peer_list->seed_count -= removed_seeders;
/* See, if we need to convert a torrent from simple vector to bucket list */ /* See if we need to convert a torrent from simple vector to bucket list */
if( ( peer_list->peer_count > OT_PEER_BUCKET_MINCOUNT ) || OT_PEERLIST_HASBUCKETS(peer_list) ) if( ( peer_list->peer_count > OT_PEER_BUCKET_MINCOUNT ) || OT_PEERLIST_HASBUCKETS(peer_list) )
vector_redistribute_buckets( peer_list ); vector_redistribute_buckets( peer_list, peer_size );
if( peer_list->peer_count ) if( peer_list->peer_count )
peer_list->base = g_now_minutes; peer_list->base = g_now_minutes;
@ -96,7 +98,14 @@ int clean_single_torrent( ot_torrent *torrent ) {
peer_list->base = g_now_minutes - OT_PEER_TIMEOUT; peer_list->base = g_now_minutes - OT_PEER_TIMEOUT;
} }
return 0; return 0;
}
/* Clean a single torrent
return 1 if torrent timed out
*/
int clean_single_torrent( ot_torrent *torrent ) {
return clean_single_peer_list( torrent->peer_list6, OT_PEER_SIZE6) *
clean_single_peer_list( torrent->peer_list4, OT_PEER_SIZE4);
} }
/* Clean up all peers in current bucket, remove timedout pools and /* Clean up all peers in current bucket, remove timedout pools and

@ -82,7 +82,11 @@ void fullscrape_deliver( int64 sock, ot_tasktype tasktype ) {
mutex_workqueue_pushtask( sock, tasktype ); mutex_workqueue_pushtask( sock, tasktype );
} }
static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_peerlist *peer_list, ot_hash *hash ) { static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torrent, ot_hash *hash ) {
size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count;
switch( mode & TASK_TASK_MASK ) { switch( mode & TASK_TASK_MASK ) {
case TASK_FULLSCRAPE: case TASK_FULLSCRAPE:
default: default:
@ -90,30 +94,30 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_peerlist *peer
*r++='2'; *r++='0'; *r++=':'; *r++='2'; *r++='0'; *r++=':';
memcpy( r, hash, sizeof(ot_hash) ); r += sizeof(ot_hash); memcpy( r, hash, sizeof(ot_hash) ); r += sizeof(ot_hash);
/* push rest of the scrape string */ /* push rest of the scrape string */
r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", peer_list->seed_count, peer_list->down_count, peer_list->peer_count-peer_list->seed_count ); r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seed_count, down_count, peer_count-seed_count );
break; break;
case TASK_FULLSCRAPE_TPB_ASCII: case TASK_FULLSCRAPE_TPB_ASCII:
to_hex( r, *hash ); r+= 2 * sizeof(ot_hash); to_hex( r, *hash ); r+= 2 * sizeof(ot_hash);
r += sprintf( r, ":%zd:%zd\n", peer_list->seed_count, peer_list->peer_count-peer_list->seed_count ); r += sprintf( r, ":%zd:%zd\n", seed_count, peer_count-seed_count );
break; break;
case TASK_FULLSCRAPE_TPB_ASCII_PLUS: case TASK_FULLSCRAPE_TPB_ASCII_PLUS:
to_hex( r, *hash ); r+= 2 * sizeof(ot_hash); to_hex( r, *hash ); r+= 2 * sizeof(ot_hash);
r += sprintf( r, ":%zd:%zd:%zd\n", peer_list->seed_count, peer_list->peer_count-peer_list->seed_count, peer_list->down_count ); r += sprintf( r, ":%zd:%zd:%zd\n", seed_count, peer_count-seed_count, down_count );
break; break;
case TASK_FULLSCRAPE_TPB_BINARY: case TASK_FULLSCRAPE_TPB_BINARY:
memcpy( r, *hash, sizeof(ot_hash) ); r += sizeof(ot_hash); memcpy( r, *hash, sizeof(ot_hash) ); r += sizeof(ot_hash);
*(uint32_t*)(r+0) = htonl( (uint32_t) peer_list->seed_count ); *(uint32_t*)(r+0) = htonl( (uint32_t) seed_count );
*(uint32_t*)(r+4) = htonl( (uint32_t)( peer_list->peer_count-peer_list->seed_count) ); *(uint32_t*)(r+4) = htonl( (uint32_t)( peer_count-seed_count) );
r+=8; r+=8;
break; break;
case TASK_FULLSCRAPE_TPB_URLENCODED: case TASK_FULLSCRAPE_TPB_URLENCODED:
r += fmt_urlencoded( r, (char *)*hash, 20 ); r += fmt_urlencoded( r, (char *)*hash, 20 );
r += sprintf( r, ":%zd:%zd\n", peer_list->seed_count, peer_list->peer_count-peer_list->seed_count ); r += sprintf( r, ":%zd:%zd\n", seed_count, peer_count-seed_count );
break; break;
case TASK_FULLSCRAPE_TRACKERSTATE: case TASK_FULLSCRAPE_TRACKERSTATE:
to_hex( r, *hash ); r+= 2 * sizeof(ot_hash); to_hex( r, *hash ); r+= 2 * sizeof(ot_hash);
r += sprintf( r, ":%zd:%zd\n", peer_list->base, peer_list->down_count ); r += sprintf( r, ":%zd:%zd\n", torrent->peer_list6->base, down_count );
break; break;
} }
return r; return r;
@ -145,7 +149,7 @@ static void fullscrape_make( int *iovec_entries, struct iovec **iovector, ot_tas
/* For each torrent in this bucket.. */ /* For each torrent in this bucket.. */
for( i=0; i<torrents_list->size; ++i ) { for( i=0; i<torrents_list->size; ++i ) {
r = fullscrape_write_one( mode, r, torrents[i].peer_list, &torrents[i].hash ); r = fullscrape_write_one( mode, r, torrents+i, &torrents[i].hash );
if( r > re) { if( r > re) {
/* Allocate a fresh output buffer at the end of our buffers list */ /* Allocate a fresh output buffer at the end of our buffers list */
@ -210,7 +214,7 @@ static void fullscrape_make_gzip( int *iovec_entries, struct iovec **iovector, o
/* For each torrent in this bucket.. */ /* For each torrent in this bucket.. */
for( i=0; i<torrents_list->size; ++i ) { for( i=0; i<torrents_list->size; ++i ) {
char compress_buffer[OT_SCRAPE_MAXENTRYLEN]; char compress_buffer[OT_SCRAPE_MAXENTRYLEN];
r = fullscrape_write_one( mode, compress_buffer, torrents[i].peer_list, &torrents[i].hash ); r = fullscrape_write_one( mode, compress_buffer, torrents+i, &torrents[i].hash );
strm.next_in = (uint8_t*)compress_buffer; strm.next_in = (uint8_t*)compress_buffer;
strm.avail_in = r - compress_buffer; strm.avail_in = r - compress_buffer;
zres = deflate( &strm, Z_NO_FLUSH ); zres = deflate( &strm, Z_NO_FLUSH );

@ -421,26 +421,18 @@ static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws,
ot_ip6 proxied_ip; ot_ip6 proxied_ip;
char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" ); char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" );
if( fwd && scan_ip6( fwd, proxied_ip ) ) { if( fwd && scan_ip6( fwd, proxied_ip ) ) {
/* If proxy reports an ipv6 address but we can only handle v4 (or vice versa), bail out */ OT_SETIP( ws->peer, proxied_ip );
#ifndef WANT_V6
if( !ip6_isv4mapped(proxied_ip) )
#else
if( ip6_isv4mapped(proxied_ip) )
#endif
HTTPERROR_400_PARAM;
OT_SETIP( &ws->peer, proxied_ip );
} else } else
OT_SETIP( &ws->peer, cookie->ip ); OT_SETIP( ws->peer, cookie->ip );
} else } else
#endif #endif
OT_SETIP( &ws->peer, cookie->ip ); OT_SETIP( ws->peer, cookie->ip );
ws->peer_id = NULL; ws->peer_id = NULL;
ws->hash = NULL; ws->hash = NULL;
OT_SETPORT( &ws->peer, &port ); OT_SETPORT( ws->peer, &port );
OT_PEERFLAG( &ws->peer ) = 0; OT_PEERFLAG( ws->peer ) = 0;
numwant = 50; numwant = 50;
scanon = 1; scanon = 1;

@ -127,13 +127,13 @@ static void livesync_handle_peersync( struct ot_workstruct *ws ) {
/* Now basic sanity checks have been done on the live sync packet /* Now basic sanity checks have been done on the live sync packet
We might add more testing and logging. */ We might add more testing and logging. */
while( off + (ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer ) <= ws->request_size ) { while( off + (ssize_t)sizeof( ot_hash ) + OT_PEER_SIZE4 <= ws->request_size ) {
memcpy( &ws->peer, ws->request + off + sizeof(ot_hash), sizeof( ot_peer ) ); memcpy( &ws->peer, ws->request + off + sizeof(ot_hash), OT_PEER_SIZE4 );
ws->hash = (ot_hash*)(ws->request + off); ws->hash = (ot_hash*)(ws->request + off);
if( !g_opentracker_running ) return; if( !g_opentracker_running ) return;
if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED ) if( OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED )
remove_peer_from_torrent( FLAG_MCA, ws ); remove_peer_from_torrent( FLAG_MCA, ws );
else else
add_peer_to_torrent_and_return_peers( FLAG_MCA, ws, /* amount = */ 0 ); add_peer_to_torrent_and_return_peers( FLAG_MCA, ws, /* amount = */ 0 );
@ -143,7 +143,7 @@ static void livesync_handle_peersync( struct ot_workstruct *ws ) {
stats_issue_event(EVENT_SYNC, 0, stats_issue_event(EVENT_SYNC, 0,
(ws->request_size - sizeof( g_tracker_id ) - sizeof( uint32_t ) ) / (ws->request_size - sizeof( g_tracker_id ) - sizeof( uint32_t ) ) /
((ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer ))); ((ssize_t)sizeof( ot_hash ) + OT_PEER_SIZE4));
} }
/* Tickle the live sync module from time to time, so no events get /* Tickle the live sync module from time to time, so no events get
@ -164,9 +164,9 @@ void livesync_tell( struct ot_workstruct *ws ) {
pthread_mutex_lock(&g_outbuf_mutex); pthread_mutex_lock(&g_outbuf_mutex);
memcpy( g_outbuf + g_outbuf_data, ws->hash, sizeof(ot_hash) ); memcpy( g_outbuf + g_outbuf_data, ws->hash, sizeof(ot_hash) );
memcpy( g_outbuf + g_outbuf_data + sizeof(ot_hash), &ws->peer, sizeof(ot_peer) ); memcpy( g_outbuf + g_outbuf_data + sizeof(ot_hash), &ws->peer, OT_PEER_SIZE4 );
g_outbuf_data += sizeof(ot_hash) + sizeof(ot_peer); g_outbuf_data += sizeof(ot_hash) + OT_PEER_SIZE4;
if( g_outbuf_data >= LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS ) if( g_outbuf_data >= LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS )
livesync_issue_peersync(); livesync_issue_peersync();

@ -73,13 +73,13 @@ static time_t ot_start_time;
#define __LDR(P,D) ((__BYTE((P),(D))>>__SHFT((D)))&__MSK) #define __LDR(P,D) ((__BYTE((P),(D))>>__SHFT((D)))&__MSK)
#define __STR(P,D,V) __BYTE((P),(D))=(__BYTE((P),(D))&~(__MSK<<__SHFT((D))))|((V)<<__SHFT((D))) #define __STR(P,D,V) __BYTE((P),(D))=(__BYTE((P),(D))&~(__MSK<<__SHFT((D))))|((V)<<__SHFT((D)))
#ifdef WANT_V6 //#ifdef WANT_V6
#define STATS_NETWORK_NODE_MAXDEPTH (68-STATS_NETWORK_NODE_BITWIDTH) //#define STATS_NETWORK_NODE_MAXDEPTH (68-STATS_NETWORK_NODE_BITWIDTH)
#define STATS_NETWORK_NODE_LIMIT (48-STATS_NETWORK_NODE_BITWIDTH) //#define STATS_NETWORK_NODE_LIMIT (48-STATS_NETWORK_NODE_BITWIDTH)
#else //#else
#define STATS_NETWORK_NODE_MAXDEPTH (28-STATS_NETWORK_NODE_BITWIDTH) #define STATS_NETWORK_NODE_MAXDEPTH (28-STATS_NETWORK_NODE_BITWIDTH)
#define STATS_NETWORK_NODE_LIMIT (24-STATS_NETWORK_NODE_BITWIDTH) #define STATS_NETWORK_NODE_LIMIT (24-STATS_NETWORK_NODE_BITWIDTH)
#endif //#endif
typedef union stats_network_node stats_network_node; typedef union stats_network_node stats_network_node;
union stats_network_node { union stats_network_node {
@ -219,12 +219,12 @@ static size_t stats_slash24s_txt( char *reply, size_t amount ) {
stats_network_node *slash24s_network_counters_root = NULL; stats_network_node *slash24s_network_counters_root = NULL;
char *r=reply; char *r=reply;
int bucket; int bucket;
size_t i; size_t i, peer_size = OT_PEER_SIZE4;
for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
ot_vector *torrents_list = mutex_bucket_lock( bucket ); ot_vector *torrents_list = mutex_bucket_lock( bucket );
for( i=0; i<torrents_list->size; ++i ) { for( i=0; i<torrents_list->size; ++i ) {
ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[i] ).peer_list; ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[i] ).peer_list4;
ot_vector *bucket_list = &peer_list->peers; ot_vector *bucket_list = &peer_list->peers;
int num_buckets = 1; int num_buckets = 1;
@ -236,9 +236,11 @@ static size_t stats_slash24s_txt( char *reply, size_t amount ) {
while( num_buckets-- ) { while( num_buckets-- ) {
ot_peer *peers = (ot_peer*)bucket_list->data; ot_peer *peers = (ot_peer*)bucket_list->data;
size_t numpeers = bucket_list->size; size_t numpeers = bucket_list->size;
while( numpeers-- ) while( numpeers-- ) {
if( stat_increase_network_count( &slash24s_network_counters_root, 0, (uintptr_t)(peers++) ) ) if( stat_increase_network_count( &slash24s_network_counters_root, 0, (uintptr_t)(peers) ) )
goto bailout_unlock; goto bailout_unlock;
peers += peer_size;
}
++bucket_list; ++bucket_list;
} }
} }
@ -285,8 +287,8 @@ typedef struct {
static int torrent_statter( ot_torrent *torrent, uintptr_t data ) { static int torrent_statter( ot_torrent *torrent, uintptr_t data ) {
torrent_stats *stats = (torrent_stats*)data; torrent_stats *stats = (torrent_stats*)data;
stats->torrent_count++; stats->torrent_count++;
stats->peer_count += torrent->peer_list->peer_count; stats->peer_count += torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
stats->seed_count += torrent->peer_list->seed_count; stats->seed_count += torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
return 0; return 0;
} }
@ -312,21 +314,23 @@ size_t stats_top_txt( char * reply, int amount ) {
ot_vector *torrents_list = mutex_bucket_lock( bucket ); ot_vector *torrents_list = mutex_bucket_lock( bucket );
for( j=0; j<torrents_list->size; ++j ) { for( j=0; j<torrents_list->size; ++j ) {
ot_torrent *torrent = (ot_torrent*)(torrents_list->data) + j; ot_torrent *torrent = (ot_torrent*)(torrents_list->data) + j;
size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
idx = amount - 1; idx = amount - 1;
while( (idx >= 0) && ( torrent->peer_list->peer_count > top100c[idx].val ) ) while( (idx >= 0) && ( peer_count > top100c[idx].val ) )
--idx; --idx;
if ( idx++ != amount - 1 ) { if ( idx++ != amount - 1 ) {
memmove( top100c + idx + 1, top100c + idx, ( amount - 1 - idx ) * sizeof( ot_record ) ); memmove( top100c + idx + 1, top100c + idx, ( amount - 1 - idx ) * sizeof( ot_record ) );
memcpy( &top100c[idx].hash, &torrent->hash, sizeof(ot_hash)); memcpy( &top100c[idx].hash, &torrent->hash, sizeof(ot_hash));
top100c[idx].val = torrent->peer_list->peer_count; top100c[idx].val = peer_count;
} }
idx = amount - 1; idx = amount - 1;
while( (idx >= 0) && ( torrent->peer_list->seed_count > top100s[idx].val ) ) while( (idx >= 0) && ( seed_count > top100s[idx].val ) )
--idx; --idx;
if ( idx++ != amount - 1 ) { if ( idx++ != amount - 1 ) {
memmove( top100s + idx + 1, top100s + idx, ( amount - 1 - idx ) * sizeof( ot_record ) ); memmove( top100s + idx + 1, top100s + idx, ( amount - 1 - idx ) * sizeof( ot_record ) );
memcpy( &top100s[idx].hash, &torrent->hash, sizeof(ot_hash)); memcpy( &top100s[idx].hash, &torrent->hash, sizeof(ot_hash));
top100s[idx].val = torrent->peer_list->seed_count; top100s[idx].val = seed_count;
} }
} }
mutex_bucket_unlock( bucket, 0 ); mutex_bucket_unlock( bucket, 0 );

@ -13,6 +13,7 @@
/* Libowfat */ /* Libowfat */
#include "socket.h" #include "socket.h"
#include "io.h" #include "io.h"
#include "ip6.h"
/* Opentracker */ /* Opentracker */
#include "trackerlogic.h" #include "trackerlogic.h"
@ -73,7 +74,7 @@ int handle_udp6( int64 serversocket, struct ot_workstruct *ws ) {
ot_ip6 remoteip; ot_ip6 remoteip;
uint32_t *inpacket = (uint32_t*)ws->inbuf; uint32_t *inpacket = (uint32_t*)ws->inbuf;
uint32_t *outpacket = (uint32_t*)ws->outbuf; uint32_t *outpacket = (uint32_t*)ws->outbuf;
uint32_t numwant, left, event, scopeid; uint32_t left, event, scopeid;
uint32_t connid[2]; uint32_t connid[2];
uint32_t action; uint32_t action;
uint16_t port, remoteport; uint16_t port, remoteport;
@ -141,34 +142,35 @@ int handle_udp6( int64 serversocket, struct ot_workstruct *ws ) {
/* We do only want to know, if it is zero */ /* We do only want to know, if it is zero */
left = inpacket[64/4] | inpacket[68/4]; left = inpacket[64/4] | inpacket[68/4];
/* Limit amount of peers to OT_MAX_PEERS_UDP */
numwant = ntohl( inpacket[92/4] );
if (numwant > OT_MAX_PEERS_UDP) numwant = OT_MAX_PEERS_UDP;
event = ntohl( inpacket[80/4] ); event = ntohl( inpacket[80/4] );
port = *(uint16_t*)( ((char*)inpacket) + 96 ); port = *(uint16_t*)( ((char*)inpacket) + 96 );
ws->hash = (ot_hash*)( ((char*)inpacket) + 16 ); ws->hash = (ot_hash*)( ((char*)inpacket) + 16 );
OT_SETIP( &ws->peer, remoteip ); OT_SETIP( ws->peer, remoteip );
OT_SETPORT( &ws->peer, &port ); OT_SETPORT( ws->peer, &port );
OT_PEERFLAG( &ws->peer ) = 0; OT_PEERFLAG( ws->peer ) = 0;
switch( event ) { switch( event ) {
case 1: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; break; case 1: OT_PEERFLAG( ws->peer ) |= PEER_FLAG_COMPLETED; break;
case 3: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; break; case 3: OT_PEERFLAG( ws->peer ) |= PEER_FLAG_STOPPED; break;
default: break; default: break;
} }
if( !left ) if( !left )
OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; OT_PEERFLAG( ws->peer ) |= PEER_FLAG_SEEDING;
outpacket[0] = htonl( 1 ); /* announce action */ outpacket[0] = htonl( 1 ); /* announce action */
outpacket[1] = inpacket[12/4]; outpacket[1] = inpacket[12/4];
if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) { /* Peer is gone. */ if( OT_PEERFLAG( ws->peer ) & PEER_FLAG_STOPPED ) { /* Peer is gone. */
ws->reply = ws->outbuf; ws->reply = ws->outbuf;
ws->reply_size = remove_peer_from_torrent( FLAG_UDP, ws ); ws->reply_size = remove_peer_from_torrent( FLAG_UDP, ws );
} else { } else {
/* Limit amount of peers to OT_MAX_PEERS_UDP */
uint32_t numwant = ntohl( inpacket[92/4] );
size_t max_peers = ip6_isv4mapped(remoteip) ? OT_MAX_PEERS_UDP4 : OT_MAX_PEERS_UDP6;
if (numwant > max_peers) numwant = max_peers;
ws->reply = ws->outbuf + 8; ws->reply = ws->outbuf + 8;
ws->reply_size = 8 + add_peer_to_torrent_and_return_peers( FLAG_UDP, ws, numwant ); ws->reply_size = 8 + add_peer_to_torrent_and_return_peers( FLAG_UDP, ws, numwant );
} }

@ -17,8 +17,11 @@
#include "uint32.h" #include "uint32.h"
#include "uint16.h" #include "uint16.h"
static int vector_compare_peer(const void *peer1, const void *peer2 ) { static int vector_compare_peer6(const void *peer1, const void *peer2 ) {
return memcmp( peer1, peer2, OT_PEER_COMPARE_SIZE ); return memcmp( peer1, peer2, OT_PEER_COMPARE_SIZE6 );
}
static int vector_compare_peer4(const void *peer1, const void *peer2 ) {
return memcmp( peer1, peer2, OT_PEER_COMPARE_SIZE4 );
} }
/* This function gives us a binary search that returns a pointer, even if /* This function gives us a binary search that returns a pointer, even if
@ -47,10 +50,10 @@ void *binary_search( const void * const key, const void * base, const size_t mem
return (void*)base; return (void*)base;
} }
static uint8_t vector_hash_peer( ot_peer *peer, int bucket_count ) { static uint8_t vector_hash_peer( ot_peer const *peer, size_t compare_size, int bucket_count ) {
unsigned int hash = 5381, i = OT_PEER_COMPARE_SIZE; unsigned int hash = 5381;
uint8_t *p = (uint8_t*)peer; uint8_t *p = (uint8_t*)peer;
while( i-- ) hash += (hash<<5) + *(p++); while( compare_size-- ) hash += (hash<<5) + *(p++);
return hash % bucket_count; return hash % bucket_count;
} }
@ -82,27 +85,37 @@ void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, s
return match; return match;
} }
ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exactmatch ) { ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch ) {
ot_peer *match; ot_peer *match, *end;
const size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
size_t match_to_end;
/* If space is zero but size is set, we're dealing with a list of vector->size buckets */ /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
if( vector->space < vector->size ) if( vector->space < vector->size )
vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, vector->size ); vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, compare_size, vector->size );
match = (ot_peer*)binary_search( peer, vector->data, vector->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, exactmatch ); match = binary_search( peer, vector->data, vector->size, peer_size, compare_size, exactmatch );
if( *exactmatch ) return match; if( *exactmatch ) return match;
/* This is the amount of bytes that needs to be pushed backwards by peer_size bytes to make room for new peer */
end = (ot_peer*)vector->data + vector->size * peer_size;
match_to_end = end - match;
if( vector->size + 1 > vector->space ) { if( vector->size + 1 > vector->space ) {
ptrdiff_t offset = match - (ot_peer*)vector->data;
size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS; size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
ot_peer *new_data = realloc( vector->data, new_space * sizeof(ot_peer) ); ot_peer *new_data = realloc( vector->data, new_space * peer_size );
if( !new_data ) return NULL; if( !new_data ) return NULL;
/* Adjust pointer if it moved by realloc */ /* Adjust pointer if it moved by realloc */
match = new_data + (match - (ot_peer*)vector->data); match = new_data + offset;
vector->data = new_data; vector->data = new_data;
vector->space = new_space; vector->space = new_space;
} }
memmove( match + 1, match, sizeof(ot_peer) * ( ((ot_peer*)vector->data) + vector->size - match ) );
/* Here we're guaranteed to have enough space in vector to move the block of peers after insertion point */
memmove( match + peer_size, match, match_to_end);
vector->size++; vector->size++;
return match; return match;
@ -113,26 +126,27 @@ ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exac
1 if a non-seeding peer was removed 1 if a non-seeding peer was removed
2 if a seeding peer was removed 2 if a seeding peer was removed
*/ */
int vector_remove_peer( ot_vector *vector, ot_peer *peer ) { int vector_remove_peer( ot_vector *vector, ot_peer const *peer, size_t peer_size) {
int exactmatch; int exactmatch, was_seeder;
ot_peer *match, *end; ot_peer *match, *end;
size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
if( !vector->size ) return 0; if( !vector->size ) return 0;
/* If space is zero but size is set, we're dealing with a list of vector->size buckets */ /* If space is zero but size is set, we're dealing with a list of vector->size buckets */
if( vector->space < vector->size ) if( vector->space < vector->size )
vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, vector->size ); vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, compare_size, vector->size );
end = ((ot_peer*)vector->data) + vector->size; end = ((ot_peer*)vector->data) + peer_size * vector->size;
match = (ot_peer*)binary_search( peer, vector->data, vector->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, &exactmatch ); match = (ot_peer*)binary_search( peer, vector->data, vector->size, peer_size, compare_size, &exactmatch );
if( !exactmatch ) return 0; if( !exactmatch ) return 0;
exactmatch = ( OT_PEERFLAG( match ) & PEER_FLAG_SEEDING ) ? 2 : 1; was_seeder = ( OT_PEERFLAG_D( match, peer_size ) & PEER_FLAG_SEEDING ) ? 2 : 1;
memmove( match, match + 1, sizeof(ot_peer) * ( end - match - 1 ) ); memmove( match, match + peer_size, end - match - peer_size );
vector->size--; vector->size--;
vector_fixup_peers( vector ); vector_fixup_peers( vector, peer_size );
return exactmatch; return was_seeder;
} }
void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) { void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) {
@ -142,7 +156,8 @@ void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) {
/* If this is being called after a unsuccessful malloc() for peer_list /* If this is being called after a unsuccessful malloc() for peer_list
in add_peer_to_torrent, match->peer_list actually might be NULL */ in add_peer_to_torrent, match->peer_list actually might be NULL */
if( match->peer_list) free_peerlist( match->peer_list ); if( match->peer_list6) free_peerlist( match->peer_list6 );
if( match->peer_list4) free_peerlist( match->peer_list4 );
memmove( match, match + 1, sizeof(ot_torrent) * ( end - match - 1 ) ); memmove( match, match + 1, sizeof(ot_torrent) * ( end - match - 1 ) );
if( ( --vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && ( vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS ) ) { if( ( --vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && ( vector->space >= OT_VECTOR_SHRINK_RATIO * OT_VECTOR_MIN_MEMBERS ) ) {
@ -158,9 +173,11 @@ void vector_clean_list( ot_vector * vector, int num_buckets ) {
return; return;
} }
void vector_redistribute_buckets( ot_peerlist * peer_list ) { void vector_redistribute_buckets( ot_peerlist * peer_list, size_t peer_size ) {
int tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1; int tmp, bucket, bucket_size_new, num_buckets_new, num_buckets_old = 1;
ot_vector * bucket_list_new, * bucket_list_old = &peer_list->peers; ot_vector * bucket_list_new, * bucket_list_old = &peer_list->peers;
int (*sort_func)(const void *, const void *) =
peer_size == OT_PEER_SIZE6 ? &vector_compare_peer6 : &vector_compare_peer4;
if( OT_PEERLIST_HASBUCKETS( peer_list ) ) { if( OT_PEERLIST_HASBUCKETS( peer_list ) ) {
num_buckets_old = peer_list->peers.size; num_buckets_old = peer_list->peers.size;
@ -198,33 +215,33 @@ void vector_redistribute_buckets( ot_peerlist * peer_list ) {
/* preallocate vectors to hold all peers */ /* preallocate vectors to hold all peers */
for( bucket=0; bucket<num_buckets_new; ++bucket ) { for( bucket=0; bucket<num_buckets_new; ++bucket ) {
bucket_list_new[bucket].space = bucket_size_new; bucket_list_new[bucket].space = bucket_size_new;
bucket_list_new[bucket].data = malloc( bucket_size_new * sizeof(ot_peer) ); bucket_list_new[bucket].data = malloc( bucket_size_new * peer_size );
if( !bucket_list_new[bucket].data ) if( !bucket_list_new[bucket].data )
return vector_clean_list( bucket_list_new, num_buckets_new ); return vector_clean_list( bucket_list_new, num_buckets_new );
} }
/* Now sort them into the correct bucket */ /* Now sort them into the correct bucket */
for( bucket=0; bucket<num_buckets_old; ++bucket ) { for( bucket=0; bucket<num_buckets_old; ++bucket ) {
ot_peer * peers_old = bucket_list_old[bucket].data, * peers_new; ot_peer * peers_old = bucket_list_old[bucket].data;
int peer_count_old = bucket_list_old[bucket].size; int peer_count_old = bucket_list_old[bucket].size;
while( peer_count_old-- ) { while( peer_count_old-- ) {
ot_vector * bucket_dest = bucket_list_new; ot_vector * bucket_dest = bucket_list_new;
if( num_buckets_new > 1 ) if( num_buckets_new > 1 )
bucket_dest += vector_hash_peer(peers_old, num_buckets_new); bucket_dest += vector_hash_peer(peers_old, OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size), num_buckets_new);
if( bucket_dest->size + 1 > bucket_dest->space ) { if( bucket_dest->size + 1 > bucket_dest->space ) {
void * tmp = realloc( bucket_dest->data, sizeof(ot_peer) * OT_VECTOR_GROW_RATIO * bucket_dest->space ); void * tmp = realloc( bucket_dest->data, peer_size * OT_VECTOR_GROW_RATIO * bucket_dest->space );
if( !tmp ) return vector_clean_list( bucket_list_new, num_buckets_new ); if( !tmp ) return vector_clean_list( bucket_list_new, num_buckets_new );
bucket_dest->data = tmp; bucket_dest->data = tmp;
bucket_dest->space *= OT_VECTOR_GROW_RATIO; bucket_dest->space *= OT_VECTOR_GROW_RATIO;
} }
peers_new = (ot_peer*)bucket_dest->data; memcpy((ot_peer*)bucket_dest->data + peer_size * bucket_dest->size++, peers_old, peer_size);
memcpy(peers_new + bucket_dest->size++, peers_old++, sizeof(ot_peer)); peers_old += peer_size;
} }
} }
/* Now sort each bucket to later allow bsearch */ /* Now sort each bucket to later allow bsearch */
for( bucket=0; bucket<num_buckets_new; ++bucket ) for( bucket=0; bucket<num_buckets_new; ++bucket )
qsort( bucket_list_new[bucket].data, bucket_list_new[bucket].size, sizeof( ot_peer ), vector_compare_peer ); qsort( bucket_list_new[bucket].data, bucket_list_new[bucket].size, peer_size, sort_func );
/* Everything worked fine. Now link new bucket_list to peer_list */ /* Everything worked fine. Now link new bucket_list to peer_list */
if( OT_PEERLIST_HASBUCKETS( peer_list) ) if( OT_PEERLIST_HASBUCKETS( peer_list) )
@ -244,7 +261,7 @@ void vector_redistribute_buckets( ot_peerlist * peer_list ) {
} }
} }
void vector_fixup_peers( ot_vector * vector ) { void vector_fixup_peers( ot_vector * vector, size_t peer_size ) {
int need_fix = 0; int need_fix = 0;
if( !vector->size ) { if( !vector->size ) {
@ -260,7 +277,7 @@ void vector_fixup_peers( ot_vector * vector ) {
need_fix++; need_fix++;
} }
if( need_fix ) if( need_fix )
vector->data = realloc( vector->data, vector->space * sizeof( ot_peer ) ); vector->data = realloc( vector->data, vector->space * peer_size );
} }
const char *g_version_vector_c = "$Source$: $Revision$\n"; const char *g_version_vector_c = "$Source$: $Revision$\n";

@ -24,11 +24,13 @@ typedef struct {
void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size, void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size,
size_t compare_size, int *exactmatch ); size_t compare_size, int *exactmatch );
void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ); void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch );
ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer *peer, int *exactmatch ); ot_peer *vector_find_or_insert_peer( ot_vector *vector, ot_peer const *peer, size_t peer_size, int *exactmatch );
int vector_remove_peer( ot_vector *vector, ot_peer *peer ); int vector_remove_peer( ot_vector *vector, ot_peer const *peer, size_t peer_size);
void vector_remove_torrent( ot_vector *vector, ot_torrent *match ); void vector_remove_torrent( ot_vector *vector, ot_torrent *match );
void vector_redistribute_buckets( ot_peerlist * peer_list );
void vector_fixup_peers( ot_vector * vector ); /* For ot_clean.c */
void vector_redistribute_buckets( ot_peerlist * peer_list, size_t peer_size );
void vector_fixup_peers( ot_vector * vector, size_t peer_size );
#endif #endif

@ -16,6 +16,7 @@
#include "byte.h" #include "byte.h"
#include "io.h" #include "io.h"
#include "iob.h" #include "iob.h"
#include "ip6.h"
#include "array.h" #include "array.h"
/* Opentracker */ /* Opentracker */
@ -57,16 +58,21 @@ void add_torrent_from_saved_state( ot_hash hash, ot_time base, size_t down_count
return mutex_bucket_unlock_by_hash( hash, 0 ); return mutex_bucket_unlock_by_hash( hash, 0 );
/* Create a new torrent entry, then */ /* Create a new torrent entry, then */
byte_zero( torrent, sizeof( ot_torrent ) );
memcpy( torrent->hash, hash, sizeof(ot_hash) ); memcpy( torrent->hash, hash, sizeof(ot_hash) );
if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { if( !( torrent->peer_list6 = malloc( sizeof (ot_peerlist) ) ) ||
!( torrent->peer_list4 = malloc( sizeof (ot_peerlist) ) ) ) {
vector_remove_torrent( torrents_list, torrent ); vector_remove_torrent( torrents_list, torrent );
return mutex_bucket_unlock_by_hash( hash, 0 ); return mutex_bucket_unlock_by_hash( hash, 0 );
} }
byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); byte_zero( torrent->peer_list6, sizeof( ot_peerlist ) );
torrent->peer_list->base = base; byte_zero( torrent->peer_list4, sizeof( ot_peerlist ) );
torrent->peer_list->down_count = down_count; torrent->peer_list6->base = base;
torrent->peer_list4->base = base;
torrent->peer_list6->down_count = down_count;
torrent->peer_list4->down_count = down_count;
return mutex_bucket_unlock_by_hash( hash, 1 ); return mutex_bucket_unlock_by_hash( hash, 1 );
} }
@ -76,6 +82,9 @@ size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstr
ot_torrent *torrent; ot_torrent *torrent;
ot_peer *peer_dest; ot_peer *peer_dest;
ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash ); ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash );
ot_peerlist *peer_list;
size_t peer_size; /* initialized in next line */
ot_peer *peer_src = peer_from_peer6(&ws->peer, &peer_size);
if( !accesslist_hashisvalid( *ws->hash ) ) { if( !accesslist_hashisvalid( *ws->hash ) ) {
mutex_bucket_unlock_by_hash( *ws->hash, 0 ); mutex_bucket_unlock_by_hash( *ws->hash, 0 );
@ -95,82 +104,88 @@ size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstr
if( !exactmatch ) { if( !exactmatch ) {
/* Create a new torrent entry, then */ /* Create a new torrent entry, then */
byte_zero( torrent, sizeof(ot_torrent));
memcpy( torrent->hash, *ws->hash, sizeof(ot_hash) ); memcpy( torrent->hash, *ws->hash, sizeof(ot_hash) );
if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { if( !( torrent->peer_list6 = malloc( sizeof (ot_peerlist) ) ) ||
!( torrent->peer_list4 = malloc( sizeof (ot_peerlist) ) ) ) {
vector_remove_torrent( torrents_list, torrent ); vector_remove_torrent( torrents_list, torrent );
mutex_bucket_unlock_by_hash( *ws->hash, 0 ); mutex_bucket_unlock_by_hash( *ws->hash, 0 );
return 0; return 0;
} }
byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); byte_zero( torrent->peer_list6, sizeof( ot_peerlist ) );
byte_zero( torrent->peer_list4, sizeof( ot_peerlist ) );
delta_torrentcount = 1; delta_torrentcount = 1;
} else } else
clean_single_torrent( torrent ); clean_single_torrent( torrent );
torrent->peer_list->base = g_now_minutes; torrent->peer_list6->base = g_now_minutes;
torrent->peer_list4->base = g_now_minutes;
peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
/* Check for peer in torrent */ /* Check for peer in torrent */
peer_dest = vector_find_or_insert_peer( &(torrent->peer_list->peers), &ws->peer, &exactmatch ); peer_dest = vector_find_or_insert_peer( &(peer_list->peers), peer_src, peer_size, &exactmatch );
if( !peer_dest ) { if( !peer_dest ) {
mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount );
return 0; return 0;
} }
/* Tell peer that it's fresh */ /* Tell peer that it's fresh */
OT_PEERTIME( &ws->peer ) = 0; OT_PEERTIME( ws->peer, OT_PEER_SIZE6 ) = 0;
/* Sanitize flags: Whoever claims to have completed download, must be a seeder */ /* Sanitize flags: Whoever claims to have completed download, must be a seeder */
if( ( OT_PEERFLAG( &ws->peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED ) if( ( OT_PEERFLAG( ws->peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED )
OT_PEERFLAG( &ws->peer ) ^= PEER_FLAG_COMPLETED; OT_PEERFLAG( ws->peer ) ^= PEER_FLAG_COMPLETED;
/* If we hadn't had a match create peer there */ /* If we hadn't had a match create peer there */
if( !exactmatch ) { if( !exactmatch ) {
#ifdef WANT_SYNC_LIVE #ifdef WANT_SYNC_LIVE
if( proto == FLAG_MCA ) if( proto == FLAG_MCA )
OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_FROM_SYNC; OT_PEERFLAG( ws->peer ) |= PEER_FLAG_FROM_SYNC;
else else
livesync_tell( ws ); livesync_tell( ws );
#endif #endif
torrent->peer_list->peer_count++; peer_list->peer_count++;
if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) { if( OT_PEERFLAG( ws->peer ) & PEER_FLAG_COMPLETED ) {
torrent->peer_list->down_count++; peer_list->down_count++;
stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws ); stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws );
} }
if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) if( OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING )
torrent->peer_list->seed_count++; peer_list->seed_count++;
} else { } else {
stats_issue_event( EVENT_RENEW, 0, OT_PEERTIME( peer_dest ) ); stats_issue_event( EVENT_RENEW, 0, OT_PEERTIME( peer_dest, peer_size ) );
#ifdef WANT_SPOT_WOODPECKER #ifdef WANT_SPOT_WOODPECKER
if( ( OT_PEERTIME(peer_dest) > 0 ) && ( OT_PEERTIME(peer_dest) < 20 ) ) if( ( OT_PEERTIME(peer_dest, peer_size) > 0 ) && ( OT_PEERTIME(peer_dest, peer_size) < 20 ) )
stats_issue_event( EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer ); stats_issue_event( EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer );
#endif #endif
#ifdef WANT_SYNC_LIVE #ifdef WANT_SYNC_LIVE
/* Won't live sync peers that come back too fast. Only exception: /* Won't live sync peers that come back too fast. Only exception:
fresh "completed" reports */ fresh "completed" reports */
if( proto != FLAG_MCA ) { if( proto != FLAG_MCA ) {
if( OT_PEERTIME( peer_dest ) > OT_CLIENT_SYNC_RENEW_BOUNDARY || if( OT_PEERTIME( peer_dest, peer_size ) > OT_CLIENT_SYNC_RENEW_BOUNDARY ||
( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) ) ) ( !(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED ) ) )
livesync_tell( ws ); livesync_tell( ws );
} }
#endif #endif
if( (OT_PEERFLAG(peer_dest) & PEER_FLAG_SEEDING ) && !(OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) ) if( (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING ) && !(OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING ) )
torrent->peer_list->seed_count--; peer_list->seed_count--;
if( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_SEEDING ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) ) if( !(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING ) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING ) )
torrent->peer_list->seed_count++; peer_list->seed_count++;
if( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) ) { if( !(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED ) ) {
torrent->peer_list->down_count++; peer_list->down_count++;
stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws ); stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws );
} }
if( OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) if( OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED )
OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; OT_PEERFLAG( ws->peer ) |= PEER_FLAG_COMPLETED;
} }
memcpy( peer_dest, &ws->peer, sizeof(ot_peer) ); memcpy( peer_dest, peer_src, peer_size );
#ifdef WANT_SYNC #ifdef WANT_SYNC
if( proto == FLAG_MCA ) { if( proto == FLAG_MCA ) {
mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount );
@ -183,10 +198,11 @@ size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstr
return ws->reply_size; return ws->reply_size;
} }
static size_t return_peers_all( ot_peerlist *peer_list, char *reply ) { static size_t return_peers_all( ot_peerlist *peer_list, size_t peer_size, char *reply ) {
unsigned int bucket, num_buckets = 1; unsigned int bucket, num_buckets = 1;
ot_vector * bucket_list = &peer_list->peers; ot_vector * bucket_list = &peer_list->peers;
size_t result = OT_PEER_COMPARE_SIZE * peer_list->peer_count; size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
size_t result = compare_size * peer_list->peer_count;
char * r_end = reply + result; char * r_end = reply + result;
if( OT_PEERLIST_HASBUCKETS(peer_list) ) { if( OT_PEERLIST_HASBUCKETS(peer_list) ) {
@ -195,28 +211,30 @@ static size_t return_peers_all( ot_peerlist *peer_list, char *reply ) {
} }
for( bucket = 0; bucket<num_buckets; ++bucket ) { for( bucket = 0; bucket<num_buckets; ++bucket ) {
ot_peer * peers = (ot_peer*)bucket_list[bucket].data; ot_peer *peers = bucket_list[bucket].data;
size_t peer_count = bucket_list[bucket].size; size_t peer_count = bucket_list[bucket].size;
while( peer_count-- ) { while( peer_count-- ) {
if( OT_PEERFLAG(peers) & PEER_FLAG_SEEDING ) { if( OT_PEERFLAG_D(peers, peer_size) & PEER_FLAG_SEEDING ) {
r_end-=OT_PEER_COMPARE_SIZE; r_end -= peer_size;
memcpy(r_end,peers++,OT_PEER_COMPARE_SIZE); memcpy( r_end, peers, compare_size);
} else { } else {
memcpy(reply,peers++,OT_PEER_COMPARE_SIZE); memcpy( reply, peers, compare_size );
reply+=OT_PEER_COMPARE_SIZE; reply += compare_size;
} }
peers += peer_size;
} }
} }
return result; return result;
} }
static size_t return_peers_selection( struct ot_workstruct *ws, ot_peerlist *peer_list, size_t amount, char *reply ) { static size_t return_peers_selection( struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply ) {
unsigned int bucket_offset, bucket_index = 0, num_buckets = 1; unsigned int bucket_offset, bucket_index = 0, num_buckets = 1;
ot_vector * bucket_list = &peer_list->peers; ot_vector * bucket_list = &peer_list->peers;
unsigned int shifted_pc = peer_list->peer_count; unsigned int shifted_pc = peer_list->peer_count;
unsigned int shifted_step = 0; unsigned int shifted_step = 0;
unsigned int shift = 0; unsigned int shift = 0;
size_t result = OT_PEER_COMPARE_SIZE * amount; size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
size_t result = compare_size * amount;
char * r_end = reply + result; char * r_end = reply + result;
if( OT_PEERLIST_HASBUCKETS(peer_list) ) { if( OT_PEERLIST_HASBUCKETS(peer_list) ) {
@ -246,13 +264,13 @@ static size_t return_peers_selection( struct ot_workstruct *ws, ot_peerlist *pee
bucket_offset -= bucket_list[bucket_index].size; bucket_offset -= bucket_list[bucket_index].size;
bucket_index = ( bucket_index + 1 ) % num_buckets; bucket_index = ( bucket_index + 1 ) % num_buckets;
} }
peer = ((ot_peer*)bucket_list[bucket_index].data) + bucket_offset; peer = bucket_list[bucket_index].data + peer_size * bucket_offset;
if( OT_PEERFLAG(peer) & PEER_FLAG_SEEDING ) { if( OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_SEEDING ) {
r_end-=OT_PEER_COMPARE_SIZE; r_end -= compare_size;
memcpy(r_end,peer,OT_PEER_COMPARE_SIZE); memcpy(r_end, peer, compare_size);
} else { } else {
memcpy(reply,peer,OT_PEER_COMPARE_SIZE); memcpy(reply, peer, compare_size);
reply+=OT_PEER_COMPARE_SIZE; reply += compare_size;
} }
} }
return result; return result;
@ -267,15 +285,17 @@ static size_t return_peers_selection( struct ot_workstruct *ws, ot_peerlist *pee
* Does not yet check not to return self * Does not yet check not to return self
*/ */
size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto ) { size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto ) {
ot_peerlist *peer_list = torrent->peer_list; size_t peer_size = peer_size_from_peer6(&ws->peer);
ot_peerlist *peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
char *r = reply; char *r = reply;
size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
if( amount > peer_list->peer_count ) if( amount > peer_list->peer_count )
amount = peer_list->peer_count; amount = peer_list->peer_count;
if( proto == FLAG_TCP ) { if( proto == FLAG_TCP ) {
int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie" PEERS_BENCODED "%zd:", peer_list->seed_count, peer_list->down_count, peer_list->peer_count-peer_list->seed_count, erval, erval/2, OT_PEER_COMPARE_SIZE*amount ); r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s%zd:", peer_list->seed_count, peer_list->down_count, peer_list->peer_count-peer_list->seed_count, erval, erval/2, peer_size == OT_PEER_SIZE6 ? PEERS_BENCODED6 : PEERS_BENCODED4, compare_size * amount );
} else { } else {
*(uint32_t*)(r+0) = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM ); *(uint32_t*)(r+0) = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM );
*(uint32_t*)(r+4) = htonl( peer_list->peer_count - peer_list->seed_count ); *(uint32_t*)(r+4) = htonl( peer_list->peer_count - peer_list->seed_count );
@ -285,9 +305,9 @@ size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent,
if( amount ) { if( amount ) {
if( amount == peer_list->peer_count ) if( amount == peer_list->peer_count )
r += return_peers_all( peer_list, r ); r += return_peers_all( peer_list, peer_size, r );
else else
r += return_peers_selection( ws, peer_list, amount, r ); r += return_peers_selection( ws, peer_list, peer_size, amount, r );
} }
if( proto == FLAG_TCP ) if( proto == FLAG_TCP )
@ -312,9 +332,10 @@ size_t return_udp_scrape_for_torrent( ot_hash hash, char *reply ) {
memset( reply, 0, 12); memset( reply, 0, 12);
delta_torrentcount = -1; delta_torrentcount = -1;
} else { } else {
r[0] = htonl( torrent->peer_list->seed_count ); r[0] = htonl( torrent->peer_list6->seed_count + torrent->peer_list4->seed_count );
r[1] = htonl( torrent->peer_list->down_count ); r[1] = htonl( torrent->peer_list6->down_count + torrent->peer_list4->down_count );
r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count ); r[2] = htonl( torrent->peer_list6->peer_count + torrent->peer_list4->peer_count -
torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
} }
} }
mutex_bucket_unlock_by_hash( hash, delta_torrentcount ); mutex_bucket_unlock_by_hash( hash, delta_torrentcount );
@ -342,7 +363,10 @@ size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *repl
*r++='2';*r++='0';*r++=':'; *r++='2';*r++='0';*r++=':';
memcpy( r, hash, sizeof(ot_hash) ); r+=sizeof(ot_hash); memcpy( r, hash, sizeof(ot_hash) ); r+=sizeof(ot_hash);
r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee",
torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ); torrent->peer_list6->seed_count + torrent->peer_list4->seed_count,
torrent->peer_list6->down_count + torrent->peer_list4->down_count,
torrent->peer_list6->peer_count + torrent->peer_list4->peer_count -
torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
} }
} }
mutex_bucket_unlock_by_hash( *hash, delta_torrentcount ); mutex_bucket_unlock_by_hash( *hash, delta_torrentcount );
@ -358,17 +382,19 @@ size_t remove_peer_from_torrent( PROTO_FLAG proto, struct ot_workstruct *ws ) {
ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash ); ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash );
ot_torrent *torrent = binary_search( ws->hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); ot_torrent *torrent = binary_search( ws->hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
ot_peerlist *peer_list = &dummy_list; ot_peerlist *peer_list = &dummy_list;
size_t peer_size; /* initialized in next line */
ot_peer *peer_src = peer_from_peer6(&ws->peer, &peer_size);
#ifdef WANT_SYNC_LIVE #ifdef WANT_SYNC_LIVE
if( proto != FLAG_MCA ) { if( proto != FLAG_MCA ) {
OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; OT_PEERFLAG( ws->peer ) |= PEER_FLAG_STOPPED;
livesync_tell( ws ); livesync_tell( ws );
} }
#endif #endif
if( exactmatch ) { if( exactmatch ) {
peer_list = torrent->peer_list; peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
switch( vector_remove_peer( &peer_list->peers, &ws->peer ) ) { switch( vector_remove_peer( &peer_list->peers, peer_src, peer_size ) ) {
case 2: peer_list->seed_count--; /* Intentional fallthrough */ case 2: peer_list->seed_count--; /* Intentional fallthrough */
case 1: peer_list->peer_count--; /* Intentional fallthrough */ case 1: peer_list->peer_count--; /* Intentional fallthrough */
default: break; default: break;
@ -377,7 +403,7 @@ size_t remove_peer_from_torrent( PROTO_FLAG proto, struct ot_workstruct *ws ) {
if( proto == FLAG_TCP ) { if( proto == FLAG_TCP ) {
int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
ws->reply_size = sprintf( ws->reply, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie" PEERS_BENCODED "0:e", peer_list->seed_count, peer_list->peer_count - peer_list->seed_count, erval, erval / 2 ); ws->reply_size = sprintf( ws->reply, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s0:e", peer_list->seed_count, peer_list->peer_count - peer_list->seed_count, erval, erval / 2, peer_size == OT_PEER_SIZE6 ? PEERS_BENCODED6 : PEERS_BENCODED4 );
} }
/* Handle UDP reply */ /* Handle UDP reply */
@ -409,6 +435,23 @@ void iterate_all_torrents( int (*for_each)( ot_torrent* torrent, uintptr_t data
} }
} }
ot_peer *peer_from_peer6( ot_peer6 *peer, size_t *peer_size ) {
ot_ip6 *ip = (ot_ip6*)peer;
if( !ip6_isv4mapped(ip) ) {
*peer_size = OT_PEER_SIZE6;
return (ot_peer*)peer;
}
*peer_size = OT_PEER_SIZE4;
return (ot_peer*)(((uint8_t*)peer) + 12);
}
size_t peer_size_from_peer6(ot_peer6 *peer) {
ot_ip6 *ip = (ot_ip6*)peer;
if( !ip6_isv4mapped(ip))
return OT_PEER_SIZE6;
return OT_PEER_SIZE4;
}
void exerr( char * message ) { void exerr( char * message ) {
fprintf( stderr, "%s\n", message ); fprintf( stderr, "%s\n", message );
exit( 111 ); exit( 111 );
@ -440,7 +483,8 @@ void trackerlogic_deinit( void ) {
if( torrents_list->size ) { if( torrents_list->size ) {
for( j=0; j<torrents_list->size; ++j ) { for( j=0; j<torrents_list->size; ++j ) {
ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + j; ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + j;
free_peerlist( torrent->peer_list ); free_peerlist( torrent->peer_list6 );
free_peerlist( torrent->peer_list4 );
delta_torrentcount -= 1; delta_torrentcount -= 1;
} }
free( torrents_list->data ); free( torrents_list->data );

@ -24,16 +24,12 @@ typedef time_t ot_time;
typedef char ot_ip6[16]; typedef char ot_ip6[16];
typedef struct { ot_ip6 address; int bits; } typedef struct { ot_ip6 address; int bits; }
ot_net; ot_net;
#ifdef WANT_V6
#define OT_IP_SIZE 16
#define PEERS_BENCODED "6:peers6"
/* List of peers should fit in a single UDP packet (around 1200 bytes) */ /* List of peers should fit in a single UDP packet (around 1200 bytes) */
#define OT_MAX_PEERS_UDP 66 #define OT_MAX_PEERS_UDP6 66
#else #define OT_MAX_PEERS_UDP4 200
#define OT_IP_SIZE 4
#define PEERS_BENCODED "5:peers" #define OT_IP_SIZE6 16
#define OT_MAX_PEERS_UDP 200 #define OT_IP_SIZE4 4
#endif
#define OT_PORT_SIZE 2 #define OT_PORT_SIZE 2
#define OT_FLAG_SIZE 1 #define OT_FLAG_SIZE 1
#define OT_TIME_SIZE 1 #define OT_TIME_SIZE 1
@ -61,6 +57,7 @@ typedef struct { ot_ip6 address; int bits; }
#define OT_ADMINIP_MAX 64 #define OT_ADMINIP_MAX 64
#define OT_MAX_THREADS 64 #define OT_MAX_THREADS 64
/* Number of minutes after announce before peer is removed */
#define OT_PEER_TIMEOUT 45 #define OT_PEER_TIMEOUT 45
/* We maintain a list of 1024 pointers to sorted list of ot_torrent structs /* We maintain a list of 1024 pointers to sorted list of ot_torrent structs
@ -78,23 +75,35 @@ extern volatile int g_opentracker_running;
extern uint32_t g_tracker_id; extern uint32_t g_tracker_id;
typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG;
#define OT_PEER_COMPARE_SIZE ((OT_IP_SIZE)+(OT_PORT_SIZE)) #define OT_PEER_COMPARE_SIZE6 ((OT_IP_SIZE6)+(OT_PORT_SIZE))
#define OT_PEER_SIZE ((OT_TIME_SIZE)+(OT_FLAG_SIZE)+(OT_PEER_COMPARE_SIZE)) #define OT_PEER_COMPARE_SIZE4 ((OT_IP_SIZE4)+(OT_PORT_SIZE))
typedef uint8_t ot_peer[OT_PEER_SIZE]; #define OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(PEER_SIZE) ((PEER_SIZE)-(OT_TIME_SIZE)-(OT_FLAG_SIZE))
#define OT_PEER_SIZE6 ((OT_TIME_SIZE)+(OT_FLAG_SIZE)+(OT_PEER_COMPARE_SIZE6))
#define OT_PEER_SIZE4 ((OT_TIME_SIZE)+(OT_FLAG_SIZE)+(OT_PEER_COMPARE_SIZE4))
typedef uint8_t ot_peer[1];
typedef uint8_t ot_peer6[OT_PEER_SIZE6];
typedef uint8_t ot_peer4[OT_PEER_SIZE4];
static const uint8_t PEER_FLAG_SEEDING = 0x80; static const uint8_t PEER_FLAG_SEEDING = 0x80;
static const uint8_t PEER_FLAG_COMPLETED = 0x40; static const uint8_t PEER_FLAG_COMPLETED = 0x40;
static const uint8_t PEER_FLAG_STOPPED = 0x20; static const uint8_t PEER_FLAG_STOPPED = 0x20;
static const uint8_t PEER_FLAG_FROM_SYNC = 0x10; static const uint8_t PEER_FLAG_FROM_SYNC = 0x10;
static const uint8_t PEER_FLAG_LEECHING = 0x00; static const uint8_t PEER_FLAG_LEECHING = 0x00;
#ifdef WANT_V6 /* Takes an ot_peer6 and returns the proper pointer to the peer and sets peer_size */
#define OT_SETIP(peer,ip) memcpy((peer),(ip),(OT_IP_SIZE)) ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size);
#else size_t peer_size_from_peer6(ot_peer6 *peer);
#define OT_SETIP(peer,ip) memcpy((peer),(((uint8_t*)ip)+12),(OT_IP_SIZE))
#endif /* New style */
#define OT_SETPORT(peer,port) memcpy(((uint8_t*)(peer))+(OT_IP_SIZE),(port),2) #define OT_SETIP(peer,ip) memcpy((peer),(ip),OT_IP_SIZE6)
#define OT_PEERFLAG(peer) (((uint8_t*)(peer))[(OT_IP_SIZE)+2]) #define OT_SETPORT(peer,port) memcpy(((uint8_t*)(peer))+(OT_IP_SIZE6),(port),2)
#define OT_PEERTIME(peer) (((uint8_t*)(peer))[(OT_IP_SIZE)+3]) #define OT_PEERFLAG(peer) (((uint8_t*)(peer))[(OT_IP_SIZE6)+2])
#define OT_PEERFLAG_D(peer,peersize) (((uint8_t*)(peer))[(peersize)-2])
#define OT_PEERTIME(peer,peersize) (((uint8_t*)(peer))[(peersize)-1])
#define PEERS_BENCODED6 "6:peers6"
#define PEERS_BENCODED4 "5:peers"
#define OT_HASH_COMPARE_SIZE (sizeof(ot_hash)) #define OT_HASH_COMPARE_SIZE (sizeof(ot_hash))
@ -102,7 +111,8 @@ struct ot_peerlist;
typedef struct ot_peerlist ot_peerlist; typedef struct ot_peerlist ot_peerlist;
typedef struct { typedef struct {
ot_hash hash; ot_hash hash;
ot_peerlist *peer_list; ot_peerlist *peer_list6;
ot_peerlist *peer_list4;
} ot_torrent; } ot_torrent;
#include "ot_vector.h" #include "ot_vector.h"
@ -131,7 +141,7 @@ struct ot_workstruct {
#endif #endif
/* The peer currently in the working */ /* The peer currently in the working */
ot_peer peer; ot_peer6 peer; /* Can fit v6 and v4 peers */
/* Pointers into the request buffer */ /* Pointers into the request buffer */
ot_hash *hash; ot_hash *hash;

Loading…
Cancel
Save