summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <>2007-11-06 11:58:32 +0000
committererdgeist <>2007-11-06 11:58:32 +0000
commit8900cc0dd980cb08a0af957a1d0dd849bf3c2ac6 (patch)
tree70aeed1dbaceea343e6ebd000d46df025bae21fc
parent5749f1d8fe80cbb84d66a265bcf9bafe159985ab (diff)
No one can get access to buckets now without locking them. Also split up the trackerlogic.c-monster in functional sub-units. HEADS UP: this code is untested and not considered stable.
-rw-r--r--Makefile6
-rw-r--r--opentracker.c2
-rw-r--r--ot_clean.c119
-rw-r--r--ot_clean.h15
-rw-r--r--ot_mutex.c31
-rw-r--r--ot_mutex.h9
-rw-r--r--ot_stats.c201
-rw-r--r--ot_stats.h13
-rw-r--r--ot_sync.c107
-rw-r--r--ot_sync.h14
-rw-r--r--ot_vector.c110
-rw-r--r--ot_vector.h26
-rw-r--r--trackerlogic.c600
-rw-r--r--trackerlogic.h48
14 files changed, 723 insertions, 578 deletions
diff --git a/Makefile b/Makefile
index 4dd8957..2360025 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,13 @@
1CC?=gcc 1CC?=gcc
2FEATURES=#-DWANT_CLOSED_TRACKER -DWANT_UTORRENT1600_WORKAROUND #-DWANT_IP_FROM_QUERY_STRING -D_DEBUG_HTTPERROR -DWANT_TRACKER_SYNC 2FEATURES=-DWANT_TRACKER_SYNC #-DWANT_CLOSED_TRACKER -DWANT_UTORRENT1600_WORKAROUND #-DWANT_IP_FROM_QUERY_STRING -D_DEBUG_HTTPERROR -DWANT_TRACKER_SYNC
3OPTS_debug=-g -ggdb #-pg # -fprofile-arcs -ftest-coverage 3OPTS_debug=-g -ggdb #-pg # -fprofile-arcs -ftest-coverage
4OPTS_production=-s -Os 4OPTS_production=-s -Os
5CFLAGS+=-I../libowfat -Wall -pipe -Wextra #-pedantic #-ansi 5CFLAGS+=-I../libowfat -Wall -pipe -Wextra #-pedantic #-ansi
6LDFLAGS+=-L../libowfat/ -lowfat 6LDFLAGS+=-L../libowfat/ -lowfat
7 7
8BINARY = opentracker 8BINARY = opentracker
9HEADERS=trackerlogic.h scan_urlencoded_query.h mutex.h 9HEADERS=trackerlogic.h scan_urlencoded_query.h ot_mutex.h ot_stats.h ot_sync.h ot_vector.h ot_clean.h
10SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c mutex.c 10SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_mutex.c ot_stats.c ot_sync.c ot_vector.c ot_clean.c
11 11
12all: $(BINARY) $(BINARY).debug 12all: $(BINARY) $(BINARY).debug
13 13
diff --git a/opentracker.c b/opentracker.c
index 00fe65f..0345177 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -29,6 +29,8 @@
29 29
30#include "trackerlogic.h" 30#include "trackerlogic.h"
31#include "scan_urlencoded_query.h" 31#include "scan_urlencoded_query.h"
32#include "ot_stats.h"
33#include "ot_sync.h"
32 34
33/* Globals */ 35/* Globals */
34static unsigned long long ot_overall_tcp_connections = 0; 36static unsigned long long ot_overall_tcp_connections = 0;
diff --git a/ot_clean.c b/ot_clean.c
new file mode 100644
index 0000000..46b3e0c
--- /dev/null
+++ b/ot_clean.c
@@ -0,0 +1,119 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <stdlib.h>
6#include <string.h>
7
8/* Libowfat */
9#include "byte.h"
10
11/* Opentracker */
12#include "trackerlogic.h"
13#include "ot_mutex.h"
14
15/* To remember, when we last cleaned up */
16static ot_time all_torrents_clean[OT_BUCKET_COUNT];
17
18/* Clean a single torrent
19 return 1 if torrent timed out
20*/
21int clean_single_torrent( ot_torrent *torrent ) {
22 ot_peerlist *peer_list = torrent->peer_list;
23 size_t peers_count = 0, seeds_count;
24 time_t timedout = (int)( NOW - peer_list->base );
25 int i;
26#ifdef WANT_TRACKER_SYNC
27 char *new_peers;
28#endif
29
30 /* Torrent has idled out */
31 if( timedout > OT_TORRENT_TIMEOUT )
32 return 1;
33
34 /* Nothing to be cleaned here? Test if torrent is worth keeping */
35 if( timedout > OT_POOLS_COUNT ) {
36 if( !peer_list->peer_count )
37 return peer_list->down_count ? 0 : 1;
38 timedout = OT_POOLS_COUNT;
39 }
40
41 /* Release vectors that have timed out */
42 for( i = OT_POOLS_COUNT - timedout; i < OT_POOLS_COUNT; ++i )
43 free( peer_list->peers[i].data);
44
45 /* Shift vectors back by the amount of pools that were shifted out */
46 memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * ( OT_POOLS_COUNT - timedout ) );
47 byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
48
49 /* Shift back seed counts as well */
50 memmove( peer_list->seed_counts + timedout, peer_list->seed_counts, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout ) );
51 byte_zero( peer_list->seed_counts, sizeof( size_t ) * timedout );
52
53#ifdef WANT_TRACKER_SYNC
54 /* Save the block modified within last OT_POOLS_TIMEOUT */
55 if( peer_list->peers[1].size &&
56 ( new_peers = realloc( peer_list->changeset.data, sizeof( ot_peer ) * peer_list->peers[1].size ) ) )
57 {
58 memmove( new_peers, peer_list->peers[1].data, peer_list->peers[1].size );
59 peer_list->changeset.data = new_peers;
60 peer_list->changeset.size = sizeof( ot_peer ) * peer_list->peers[1].size;
61 } else {
62 free( peer_list->changeset.data );
63
64 memset( &peer_list->changeset, 0, sizeof( ot_vector ) );
65 }
66#endif
67
68 peers_count = seeds_count = 0;
69 for( i = 0; i < OT_POOLS_COUNT; ++i ) {
70 peers_count += peer_list->peers[i].size;
71 seeds_count += peer_list->seed_counts[i];
72 }
73 peer_list->seed_count = seeds_count;
74 peer_list->peer_count = peers_count;
75
76 if( peers_count )
77 peer_list->base = NOW;
78 else {
79 /* When we got here, the last time that torrent
80 has been touched is OT_POOLS_COUNT units before */
81 peer_list->base = NOW - OT_POOLS_COUNT;
82 }
83 return 0;
84}
85
86/* Clean up all peers in current bucket, remove timedout pools and
87 torrents */
88void clean_all_torrents( void ) {
89 ot_vector *torrents_list;
90 size_t i;
91 static int bucket;
92 ot_time time_now = NOW;
93
94 /* Search for an uncleaned bucked */
95 while( ( all_torrents_clean[bucket] == time_now ) && ( ++bucket < OT_BUCKET_COUNT ) );
96 if( bucket >= OT_BUCKET_COUNT ) {
97 bucket = 0; return;
98 }
99
100 all_torrents_clean[bucket] = time_now;
101
102 torrents_list = mutex_bucket_lock( bucket );
103 for( i=0; i<torrents_list->size; ++i ) {
104 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
105 if( clean_single_torrent( torrent ) ) {
106 vector_remove_torrent( torrents_list, torrent );
107 --i; continue;
108 }
109 }
110 mutex_bucket_unlock( bucket );
111}
112
113void clean_init( void ) {
114 byte_zero( all_torrents_clean, sizeof( all_torrents_clean ) );
115}
116
117void clean_deinit( void ) {
118 byte_zero( all_torrents_clean, sizeof( all_torrents_clean ) );
119} \ No newline at end of file
diff --git a/ot_clean.h b/ot_clean.h
new file mode 100644
index 0000000..9c2fe11
--- /dev/null
+++ b/ot_clean.h
@@ -0,0 +1,15 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4#ifndef __OT_CLEAN_H__
5#define __OT_CLEAN_H__
6
7#include "trackerlogic.h"
8
9void clean_init( void );
10void clean_deinit( void );
11
12void clean_all_torrents( void );
13int clean_single_torrent( ot_torrent *torrent );
14
15#endif \ No newline at end of file
diff --git a/ot_mutex.c b/ot_mutex.c
index 5c14e45..bb82f46 100644
--- a/ot_mutex.c
+++ b/ot_mutex.c
@@ -1,11 +1,19 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org> 1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */ 2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3 3
4/* System */
4#include <pthread.h> 5#include <pthread.h>
5#include <stdio.h> 6#include <stdio.h>
6 7
8/* Libowfat */
9#include "byte.h"
10
11/* Opentracker */
7#include "trackerlogic.h" 12#include "trackerlogic.h"
8#include "mutex.h" 13#include "ot_mutex.h"
14
15/* Our global all torrents list */
16static ot_vector all_torrents[OT_BUCKET_COUNT];
9 17
10static int bucket_locklist[ OT_MAX_THREADS ]; 18static int bucket_locklist[ OT_MAX_THREADS ];
11static int bucket_locklist_count = 0; 19static int bucket_locklist_count = 0;
@@ -51,12 +59,23 @@ static void bucket_remove( int bucket ) {
51 --bucket_locklist_count; 59 --bucket_locklist_count;
52} 60}
53 61
54void mutex_bucket_lock( int bucket ) { 62ot_vector *mutex_bucket_lock( int bucket ) {
55 pthread_mutex_lock( &bucket_mutex ); 63 pthread_mutex_lock( &bucket_mutex );
56 while( bucket_check( bucket ) ) 64 while( bucket_check( bucket ) )
57 pthread_cond_wait( &bucket_being_unlocked, &bucket_mutex ); 65 pthread_cond_wait( &bucket_being_unlocked, &bucket_mutex );
58 bucket_push( bucket ); 66 bucket_push( bucket );
59 pthread_mutex_unlock( &bucket_mutex ); 67 pthread_mutex_unlock( &bucket_mutex );
68 return all_torrents + bucket;
69}
70
71ot_vector *mutex_bucket_lock_by_hash( ot_hash *hash ) {
72 unsigned char *local_hash = hash[0];
73 int bucket = ( local_hash[0] << 2 ) | ( local_hash[1] >> 6 );
74
75 /* Can block */
76 mutex_bucket_lock( bucket );
77
78 return all_torrents + bucket;
60} 79}
61 80
62void mutex_bucket_unlock( int bucket ) { 81void mutex_bucket_unlock( int bucket ) {
@@ -66,12 +85,20 @@ void mutex_bucket_unlock( int bucket ) {
66 pthread_mutex_unlock( &bucket_mutex ); 85 pthread_mutex_unlock( &bucket_mutex );
67} 86}
68 87
88void mutex_bucket_unlock_by_hash( ot_hash *hash ) {
89 unsigned char *local_hash = hash[0];
90 int bucket = ( local_hash[0] << 2 ) | ( local_hash[1] >> 6 );
91 mutex_bucket_unlock( bucket );
92}
93
69void mutex_init( ) { 94void mutex_init( ) {
70 pthread_mutex_init(&bucket_mutex, NULL); 95 pthread_mutex_init(&bucket_mutex, NULL);
71 pthread_cond_init (&bucket_being_unlocked, NULL); 96 pthread_cond_init (&bucket_being_unlocked, NULL);
97 byte_zero( all_torrents, sizeof( all_torrents ) );
72} 98}
73 99
74void mutex_deinit( ) { 100void mutex_deinit( ) {
75 pthread_mutex_destroy(&bucket_mutex); 101 pthread_mutex_destroy(&bucket_mutex);
76 pthread_cond_destroy(&bucket_being_unlocked); 102 pthread_cond_destroy(&bucket_being_unlocked);
103 byte_zero( all_torrents, sizeof( all_torrents ) );
77} 104}
diff --git a/ot_mutex.h b/ot_mutex.h
index 8d91ab3..2d30c69 100644
--- a/ot_mutex.h
+++ b/ot_mutex.h
@@ -1,13 +1,16 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org> 1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */ 2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3 3
4#ifndef __MUTEX_H__ 4#ifndef __OT_MUTEX_H__
5#define __MUTEX_H__ 5#define __OT_MUTEX_H__
6 6
7void mutex_init( ); 7void mutex_init( );
8void mutex_deinit( ); 8void mutex_deinit( );
9 9
10void mutex_bucket_lock( int bucket ); 10ot_vector *mutex_bucket_lock( int bucket );
11ot_vector *mutex_bucket_lock_by_hash( ot_hash *hash );
12
11void mutex_bucket_unlock( int bucket ); 13void mutex_bucket_unlock( int bucket );
14void mutex_bucket_unlock_by_hash( ot_hash *hash );
12 15
13#endif 16#endif
diff --git a/ot_stats.c b/ot_stats.c
new file mode 100644
index 0000000..215d557
--- /dev/null
+++ b/ot_stats.c
@@ -0,0 +1,201 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <stdlib.h>
6#include <sys/types.h>
7#include <sys/mman.h>
8#include <stdio.h>
9#include <string.h>
10
11/* Libowfat */
12#include "byte.h"
13
14/* Opentracker */
15#include "trackerlogic.h"
16#include "ot_mutex.h"
17#include "ot_stats.h"
18
19/* Converter function from memory to human readable hex strings */
20static char*to_hex(char*d,ot_byte*s){const char*m="0123456789ABCDEF";char*e=d+40;while(d<e){*d++=m[*s>>4];*d++=m[*s++&15];}*d=0;return d;}
21
22typedef struct { size_t val; ot_torrent * torrent; } ot_record;
23
24/* Fetches stats from tracker */
25size_t return_stats_for_tracker( char *reply, int mode ) {
26 size_t torrent_count = 0, peer_count = 0, seed_count = 0, j;
27 ot_record top5s[5], top5c[5];
28 char *r = reply;
29 int bucket;
30
31 byte_zero( top5s, sizeof( top5s ) );
32 byte_zero( top5c, sizeof( top5c ) );
33
34 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
35 ot_vector *torrents_list = mutex_bucket_lock( bucket );
36 torrent_count += torrents_list->size;
37 for( j=0; j<torrents_list->size; ++j ) {
38 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
39 if( mode == STATS_TOP5 ) {
40 int idx = 4; while( (idx >= 0) && ( peer_list->peer_count > top5c[idx].val ) ) --idx;
41 if ( idx++ != 4 ) {
42 memmove( top5c + idx + 1, top5c + idx, ( 4 - idx ) * sizeof( ot_record ) );
43 top5c[idx].val = peer_list->peer_count;
44 top5c[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
45 }
46 idx = 4; while( (idx >= 0) && ( peer_list->seed_count > top5s[idx].val ) ) --idx;
47 if ( idx++ != 4 ) {
48 memmove( top5s + idx + 1, top5s + idx, ( 4 - idx ) * sizeof( ot_record ) );
49 top5s[idx].val = peer_list->seed_count;
50 top5s[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
51 }
52 }
53 peer_count += peer_list->peer_count; seed_count += peer_list->seed_count;
54 }
55 mutex_bucket_unlock( bucket );
56 }
57 if( mode == STATS_TOP5 ) {
58 char hex_out[42];
59 int idx;
60 r += sprintf( r, "Top5 torrents by peers:\n" );
61 for( idx=0; idx<5; ++idx )
62 if( top5c[idx].torrent )
63 r += sprintf( r, "\t%zd\t%s\n", top5c[idx].val, to_hex( hex_out, top5c[idx].torrent->hash) );
64 r += sprintf( r, "Top5 torrents by seeds:\n" );
65 for( idx=0; idx<5; ++idx )
66 if( top5s[idx].torrent )
67 r += sprintf( r, "\t%zd\t%s\n", top5s[idx].val, to_hex( hex_out, top5s[idx].torrent->hash) );
68 } else
69 r += sprintf( r, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", peer_count, seed_count, torrent_count );
70
71 return r - reply;
72}
73
74/* This function collects 4096 /24s in 4096 possible
75 malloc blocks
76*/
77size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh ) {
78
79#define NUM_TOPBITS 12
80#define NUM_LOWBITS (24-NUM_TOPBITS)
81#define NUM_BUFS (1<<NUM_TOPBITS)
82#define NUM_S24S (1<<NUM_LOWBITS)
83#define MSK_S24S (NUM_S24S-1)
84
85 ot_dword *counts[ NUM_BUFS ];
86 ot_dword slash24s[amount*2]; /* first dword amount, second dword subnet */
87 int bucket;
88 size_t i, j, k, l;
89 char *r = reply;
90
91 byte_zero( counts, sizeof( counts ) );
92 byte_zero( slash24s, amount * 2 * sizeof(ot_dword) );
93
94 r += sprintf( r, "Stats for all /24s with more than %u announced torrents:\n\n", thresh );
95
96 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
97 ot_vector *torrents_list = mutex_bucket_lock( bucket );
98 for( j=0; j<torrents_list->size; ++j ) {
99 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
100 for( k=0; k<OT_POOLS_COUNT; ++k ) {
101 ot_peer *peers = peer_list->peers[k].data;
102 size_t numpeers = peer_list->peers[k].size;
103 for( l=0; l<numpeers; ++l ) {
104 ot_dword s24 = ntohl(*(ot_dword*)(peers+l)) >> 8;
105 ot_dword *count = counts[ s24 >> NUM_LOWBITS ];
106 if( !count ) {
107 count = malloc( sizeof(ot_dword) * NUM_S24S );
108 if( !count )
109 goto bailout_cleanup;
110 byte_zero( count, sizeof( ot_dword ) * NUM_S24S );
111 counts[ s24 >> NUM_LOWBITS ] = count;
112 }
113 count[ s24 & MSK_S24S ]++;
114 }
115 }
116 }
117 mutex_bucket_unlock( bucket );
118 }
119
120 k = l = 0; /* Debug: count allocated bufs */
121 for( i=0; i < NUM_BUFS; ++i ) {
122 ot_dword *count = counts[i];
123 if( !counts[i] )
124 continue;
125 ++k; /* Debug: count allocated bufs */
126 for( j=0; j < NUM_S24S; ++j ) {
127 if( count[j] > thresh ) {
128 /* This subnet seems to announce more torrents than the last in our list */
129 int insert_pos = amount - 1;
130 while( ( insert_pos >= 0 ) && ( count[j] > slash24s[ 2 * insert_pos ] ) )
131 --insert_pos;
132 ++insert_pos;
133 memmove( slash24s + 2 * ( insert_pos + 1 ), slash24s + 2 * ( insert_pos ), 2 * sizeof( ot_dword ) * ( amount - insert_pos - 1 ) );
134 slash24s[ 2 * insert_pos ] = count[j];
135 slash24s[ 2 * insert_pos + 1 ] = ( i << NUM_TOPBITS ) + j;
136 if( slash24s[ 2 * amount - 2 ] > thresh )
137 thresh = slash24s[ 2 * amount - 2 ];
138 }
139 if( count[j] ) ++l;
140 }
141 free( count );
142 }
143
144 r += sprintf( r, "Allocated bufs: %zd, used s24s: %zd\n", k, l );
145
146 for( i=0; i < amount; ++i )
147 if( slash24s[ 2*i ] >= thresh ) {
148 ot_dword ip = slash24s[ 2*i +1 ];
149 r += sprintf( r, "% 10ld %d.%d.%d.0/24\n", (long)slash24s[ 2*i ], (int)(ip >> 16), (int)(255 & ( ip >> 8 )), (int)(ip & 255) );
150 }
151
152 return r - reply;
153
154bailout_cleanup:
155
156 for( i=0; i < NUM_BUFS; ++i )
157 free( counts[i] );
158
159 return 0;
160}
161
162size_t return_memstat_for_tracker( char **reply ) {
163 size_t torrent_count = 0, j;
164 size_t allocated, replysize;
165 ot_vector *torrents_list;
166 int bucket, k;
167 char *r;
168
169 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
170 torrents_list = mutex_bucket_lock(bucket);
171 torrent_count += torrents_list->size;
172 mutex_bucket_unlock(bucket);
173 }
174
175 allocated = OT_BUCKET_COUNT*32 + (43+OT_POOLS_COUNT*32)*torrent_count;
176 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0;
177
178 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
179 torrents_list = mutex_bucket_lock(bucket);
180 r += sprintf( r, "%02X: %08X %08X\n", bucket, (unsigned int)torrents_list->size, (unsigned int)torrents_list->space );
181 mutex_bucket_unlock(bucket);
182 }
183
184 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
185 ot_vector *torrents_list = mutex_bucket_lock(bucket);
186 char hex_out[42];
187 for( j=0; j<torrents_list->size; ++j ) {
188 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
189 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
190 r += sprintf( r, "\n%s:\n", to_hex( hex_out, (ot_byte*)hash) );
191 for( k=0; k<OT_POOLS_COUNT; ++k )
192 r += sprintf( r, "\t%05X %05X\n", ((unsigned int)peer_list->peers[k].size), (unsigned int)peer_list->peers[k].space );
193 }
194 mutex_bucket_unlock(bucket);
195 }
196
197 replysize = ( r - *reply );
198 fix_mmapallocation( *reply, allocated, replysize );
199
200 return replysize;
201} \ No newline at end of file
diff --git a/ot_stats.h b/ot_stats.h
new file mode 100644
index 0000000..e4d7c06
--- /dev/null
+++ b/ot_stats.h
@@ -0,0 +1,13 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4#ifndef __OT_STATS_H__
5#define __OT_STATS_H__
6
7enum { STATS_CONNS, STATS_PEERS, STATS_TOP5, STATS_DMEM, STATS_TCP, STATS_UDP, STATS_SLASH24S, SYNC_IN, SYNC_OUT, STATS_FULLSCRAPE };
8
9size_t return_stats_for_tracker( char *reply, int mode );
10size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh );
11size_t return_memstat_for_tracker( char **reply );
12
13#endif
diff --git a/ot_sync.c b/ot_sync.c
new file mode 100644
index 0000000..6e95a98
--- /dev/null
+++ b/ot_sync.c
@@ -0,0 +1,107 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <sys/types.h>
6#include <sys/mman.h>
7#include <stdio.h>
8#include <string.h>
9
10/* Libowfat */
11#include "scan.h"
12#include "byte.h"
13
14/* Opentracker */
15#include "trackerlogic.h"
16#include "ot_mutex.h"
17#include "ot_sync.h"
18
19#ifdef WANT_TRACKER_SYNC
20/* Import Changeset from an external authority
21 format: d4:syncd[..]ee
22 [..]: ( 20:01234567890abcdefghij16:XXXXYYYY )+
23*/
24int add_changeset_to_tracker( ot_byte *data, size_t len ) {
25 ot_hash *hash;
26 ot_byte *end = data + len;
27 unsigned long peer_count;
28
29 /* We do know, that the string is \n terminated, so it cant
30 overflow */
31 if( byte_diff( data, 8, "d4:syncd" ) ) return -1;
32 data += 8;
33
34 while( 1 ) {
35 if( byte_diff( data, 3, "20:" ) ) {
36 if( byte_diff( data, 2, "ee" ) )
37 return -1;
38 return 0;
39 }
40 data += 3;
41 hash = (ot_hash*)data;
42 data += sizeof( ot_hash );
43
44 /* Scan string length indicator */
45 data += ( len = scan_ulong( (char*)data, &peer_count ) );
46
47 /* If no long was scanned, it is not divisible by 8, it is not
48 followed by a colon or claims to need to much memory, we fail */
49 if( !len || !peer_count || ( peer_count & 7 ) || ( *data++ != ':' ) || ( data + peer_count > end ) )
50 return -1;
51
52 while( peer_count > 0 ) {
53 add_peer_to_torrent( hash, (ot_peer*)data, 1 );
54 data += 8; peer_count -= 8;
55 }
56 }
57 return 0;
58}
59
60/* Proposed output format
61 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee
62*/
63size_t return_changeset_for_tracker( char **reply ) {
64 size_t allocated = 0, i, replysize;
65 ot_vector *torrents_list;
66 int bucket;
67 char *r;
68
69 /* Maybe there is time to clean_all_torrents(); */
70
71 /* Determine space needed for whole changeset */
72 for( bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket ) {
73 torrents_list = mutex_bucket_lock(bucket);
74 for( i=0; i<torrents_list->size; ++i ) {
75 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
76 allocated += sizeof( ot_hash ) + sizeof(ot_peer) * torrent->peer_list->changeset.size + 13;
77 }
78 mutex_bucket_unlock(bucket);
79 }
80
81 /* add "d4:syncd" and "ee" */
82 allocated += 8 + 2;
83
84 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) )
85 return 0;
86
87 memmove( r, "d4:syncd", 8 ); r += 8;
88 for( bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket ) {
89 torrents_list = mutex_bucket_lock(bucket);
90 for( i=0; i<torrents_list->size; ++i ) {
91 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
92 const size_t byte_count = sizeof(ot_peer) * torrent->peer_list->changeset.size;
93 *r++ = '2'; *r++ = '0'; *r++ = ':';
94 memmove( r, torrent->hash, sizeof( ot_hash ) ); r += sizeof( ot_hash );
95 r += sprintf( r, "%zd:", byte_count );
96 memmove( r, torrent->peer_list->changeset.data, byte_count ); r += byte_count;
97 }
98 mutex_bucket_unlock(bucket);
99 }
100 *r++ = 'e'; *r++ = 'e';
101
102 replysize = ( r - *reply );
103 fix_mmapallocation( *reply, allocated, replysize );
104
105 return replysize;
106}
107#endif
diff --git a/ot_sync.h b/ot_sync.h
new file mode 100644
index 0000000..cf0fb3d
--- /dev/null
+++ b/ot_sync.h
@@ -0,0 +1,14 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4#ifndef __OT_SYNC_H__
5#define __OT_SYNC_H__
6
7#include "trackerlogic.h"
8
9#ifdef WANT_TRACKER_SYNC
10size_t return_changeset_for_tracker( char **reply );
11int add_changeset_to_tracker( ot_byte *data, size_t len );
12#endif
13
14#endif \ No newline at end of file
diff --git a/ot_vector.c b/ot_vector.c
new file mode 100644
index 0000000..aa71279
--- /dev/null
+++ b/ot_vector.c
@@ -0,0 +1,110 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <stdlib.h>
6#include <string.h>
7
8/* Opentracker */
9#include "trackerlogic.h"
10#include "ot_vector.h"
11
12/* This function gives us a binary search that returns a pointer, even if
13 no exact match is found. In that case it sets exactmatch 0 and gives
14 calling functions the chance to insert data
15*/
16void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size,
17 size_t compare_size, int *exactmatch ) {
18 size_t mc = member_count;
19 ot_byte *lookat = ((ot_byte*)base) + member_size * (member_count >> 1);
20 *exactmatch = 1;
21
22 while( mc ) {
23 int cmp = memcmp( lookat, key, compare_size);
24 if (cmp == 0) return (void *)lookat;
25 if (cmp < 0) {
26 base = (void*)(lookat + member_size);
27 --mc;
28 }
29 mc >>= 1;
30 lookat = ((ot_byte*)base) + member_size * (mc >> 1);
31 }
32 *exactmatch = 0;
33 return (void*)lookat;
34}
35
36/* This is the generic insert operation for our vector type.
37 It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with
38 those of objects in vector. Our special "binary_search" function does that and either returns the match or a
39 pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it,
40 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
41 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
42*/
43void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ) {
44 ot_byte *match = binary_search( key, vector->data, vector->size, member_size, compare_size, exactmatch );
45
46 if( *exactmatch ) return match;
47
48 if( vector->size + 1 >= vector->space ) {
49 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
50 ot_byte *new_data = realloc( vector->data, new_space * member_size );
51 if( !new_data ) return NULL;
52
53 /* Adjust pointer if it moved by realloc */
54 match = new_data + (match - (ot_byte*)vector->data);
55
56 vector->data = new_data;
57 vector->space = new_space;
58 }
59 memmove( match + member_size, match, ((ot_byte*)vector->data) + member_size * vector->size - match );
60 vector->size++;
61 return match;
62}
63
64/* This is the non-generic delete from vector-operation specialized for peers in pools.
65 Set hysteresis == 0 if you expect the vector not to ever grow again.
66 It returns 0 if no peer was found (and thus not removed)
67 1 if a non-seeding peer was removed
68 2 if a seeding peer was removed
69*/
70int vector_remove_peer( ot_vector *vector, ot_peer *peer, int hysteresis ) {
71 int exactmatch;
72 size_t shrink_thresh = hysteresis ? OT_VECTOR_SHRINK_THRESH : OT_VECTOR_SHRINK_RATIO;
73 ot_peer *end = ((ot_peer*)vector->data) + vector->size;
74 ot_peer *match;
75
76 if( !vector->size ) return 0;
77 match = binary_search( peer, vector->data, vector->size, sizeof( ot_peer ), OT_PEER_COMPARE_SIZE, &exactmatch );
78
79 if( !exactmatch ) return 0;
80 exactmatch = ( OT_FLAG( match ) & PEER_FLAG_SEEDING ) ? 2 : 1;
81 memmove( match, match + 1, sizeof(ot_peer) * ( end - match - 1 ) );
82 if( ( --vector->size * shrink_thresh < vector->space ) && ( vector->space > OT_VECTOR_MIN_MEMBERS ) ) {
83 vector->space /= OT_VECTOR_SHRINK_RATIO;
84 vector->data = realloc( vector->data, vector->space * sizeof( ot_peer ) );
85 }
86 if( !vector->size ) {
87 /* for peer pools its safe to let them go,
88 in 999 of 1000 this happens in older pools, that won't ever grow again */
89 free( vector->data );
90 vector->data = NULL;
91 vector->space = 0;
92 }
93 return exactmatch;
94}
95
96void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) {
97 ot_torrent *end = ((ot_torrent*)vector->data) + vector->size;
98
99 if( !vector->size ) return;
100
101 /* If this is being called after a unsuccessful malloc() for peer_list
102 in add_peer_to_torrent, match->peer_list actually might be NULL */
103 if( match->peer_list) free_peerlist( match->peer_list );
104
105 memmove( match, match + 1, sizeof(ot_torrent) * ( end - match - 1 ) );
106 if( ( --vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && ( vector->space > OT_VECTOR_MIN_MEMBERS ) ) {
107 vector->space /= OT_VECTOR_SHRINK_RATIO;
108 vector->data = realloc( vector->data, vector->space * sizeof( ot_torrent ) );
109 }
110}
diff --git a/ot_vector.h b/ot_vector.h
new file mode 100644
index 0000000..8863855
--- /dev/null
+++ b/ot_vector.h
@@ -0,0 +1,26 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4#ifndef __OT_VECTOR_H__
5#define __OT_VECTOR_H__
6
7#include "trackerlogic.h"
8
9#define OT_VECTOR_MIN_MEMBERS 4
10#define OT_VECTOR_GROW_RATIO 8
11#define OT_VECTOR_SHRINK_THRESH 6
12#define OT_VECTOR_SHRINK_RATIO 4
13typedef struct {
14 void *data;
15 size_t size;
16 size_t space;
17} ot_vector;
18
19void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size,
20 size_t compare_size, int *exactmatch );
21void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch );
22
23int vector_remove_peer( ot_vector *vector, ot_peer *peer, int hysteresis );
24void vector_remove_torrent( ot_vector *vector, ot_torrent *match );
25
26#endif \ No newline at end of file
diff --git a/trackerlogic.c b/trackerlogic.c
index 2725399..c09f1b9 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -1,149 +1,33 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org> 1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */ 2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3 3
4#include "trackerlogic.h" 4/* System */
5
6#include <stdlib.h> 5#include <stdlib.h>
7#include <string.h> 6#include <string.h>
8#include <stdio.h> 7#include <stdio.h>
9#include <fcntl.h>
10#include <sys/types.h> 8#include <sys/types.h>
11#include <sys/mman.h> 9#include <sys/mman.h>
12#include <arpa/inet.h>
13#include <unistd.h> 10#include <unistd.h>
14#include <time.h> 11#include <time.h>
15#include <math.h> 12#include <math.h>
16#include <glob.h>
17 13
18#include <errno.h> 14/* Libowfat */
19#include "scan.h" 15#include "scan.h"
20#include "byte.h" 16#include "byte.h"
21#include "mutex.h"
22 17
23/* GLOBAL VARIABLES */ 18/* Opentracker */
19#include "trackerlogic.h"
20#include "ot_mutex.h"
21#include "ot_stats.h"
22#include "ot_clean.h"
24 23
25/* We maintain a list of 1024 pointers to sorted list of ot_torrent structs 24/* GLOBAL VARIABLES */
26 Sort key is, of course, its hash */
27#define OT_BUCKET_COUNT 1024
28static ot_vector all_torrents[OT_BUCKET_COUNT];
29static ot_time all_torrents_clean[OT_BUCKET_COUNT];
30#if defined ( WANT_BLACKLISTING ) || defined( WANT_CLOSED_TRACKER ) 25#if defined ( WANT_BLACKLISTING ) || defined( WANT_CLOSED_TRACKER )
31static ot_vector accesslist; 26static ot_vector accesslist;
32#define WANT_ACCESS_CONTROL 27#define WANT_ACCESS_CONTROL
33#endif 28#endif
34 29
35static int clean_single_torrent( ot_torrent *torrent ); 30void free_peerlist( ot_peerlist *peer_list ) {
36
37/* these functions protect our buckets from other threads that
38 try to commit announces or clean up */
39static ot_vector *lock_bucket_by_hash( ot_hash *hash ) {
40 unsigned char *local_hash = hash[0];
41 int bucket = ( local_hash[0] << 2 ) | ( local_hash[1] >> 6 );
42
43 /* Can block */
44 mutex_bucket_lock( bucket );
45
46 return all_torrents + bucket;
47}
48
49static void *unlock_bucket_by_hash( ot_hash *hash ) {
50 unsigned char *local_hash = hash[0];
51 int bucket = ( local_hash[0] << 2 ) | ( local_hash[1] >> 6 );
52 mutex_bucket_unlock( bucket );
53
54 /* To make caller's code look better, allow
55 return unlock_bucket_by_hash() */
56 return NULL;
57}
58
59/* Converter function from memory to human readable hex strings */
60static char*to_hex(char*d,ot_byte*s){const char*m="0123456789ABCDEF";char*e=d+40;while(d<e){*d++=m[*s>>4];*d++=m[*s++&15];}*d=0;return d;}
61
62/* This function gives us a binary search that returns a pointer, even if
63 no exact match is found. In that case it sets exactmatch 0 and gives
64 calling functions the chance to insert data
65*/
66static void *binary_search( const void * const key, const void * base, const size_t member_count, const size_t member_size,
67 size_t compare_size, int *exactmatch ) {
68 size_t mc = member_count;
69 ot_byte *lookat = ((ot_byte*)base) + member_size * (member_count >> 1);
70 *exactmatch = 1;
71
72 while( mc ) {
73 int cmp = memcmp( lookat, key, compare_size);
74 if (cmp == 0) return (void *)lookat;
75 if (cmp < 0) {
76 base = (void*)(lookat + member_size);
77 --mc;
78 }
79 mc >>= 1;
80 lookat = ((ot_byte*)base) + member_size * (mc >> 1);
81 }
82 *exactmatch = 0;
83 return (void*)lookat;
84}
85
86/* This is the generic insert operation for our vector type.
87 It tries to locate the object at "key" with size "member_size" by comparing its first "compare_size" bytes with
88 those of objects in vector. Our special "binary_search" function does that and either returns the match or a
89 pointer to where the object is to be inserted. vector_find_or_insert makes space for the object and copies it,
90 if it wasn't found in vector. Caller needs to check the passed "exactmatch" variable to see, whether an insert
91 took place. If resizing the vector failed, NULL is returned, else the pointer to the object in vector.
92*/
93static void *vector_find_or_insert( ot_vector *vector, void *key, size_t member_size, size_t compare_size, int *exactmatch ) {
94 ot_byte *match = binary_search( key, vector->data, vector->size, member_size, compare_size, exactmatch );
95
96 if( *exactmatch ) return match;
97
98 if( vector->size + 1 >= vector->space ) {
99 size_t new_space = vector->space ? OT_VECTOR_GROW_RATIO * vector->space : OT_VECTOR_MIN_MEMBERS;
100 ot_byte *new_data = realloc( vector->data, new_space * member_size );
101 if( !new_data ) return NULL;
102
103 /* Adjust pointer if it moved by realloc */
104 match = new_data + (match - (ot_byte*)vector->data);
105
106 vector->data = new_data;
107 vector->space = new_space;
108 }
109 memmove( match + member_size, match, ((ot_byte*)vector->data) + member_size * vector->size - match );
110 vector->size++;
111 return match;
112}
113
114/* This is the non-generic delete from vector-operation specialized for peers in pools.
115 Set hysteresis == 0 if you expect the vector not to ever grow again.
116 It returns 0 if no peer was found (and thus not removed)
117 1 if a non-seeding peer was removed
118 2 if a seeding peer was removed
119*/
120static int vector_remove_peer( ot_vector *vector, ot_peer *peer, int hysteresis ) {
121 int exactmatch;
122 size_t shrink_thresh = hysteresis ? OT_VECTOR_SHRINK_THRESH : OT_VECTOR_SHRINK_RATIO;
123 ot_peer *end = ((ot_peer*)vector->data) + vector->size;
124 ot_peer *match;
125
126 if( !vector->size ) return 0;
127 match = binary_search( peer, vector->data, vector->size, sizeof( ot_peer ), OT_PEER_COMPARE_SIZE, &exactmatch );
128
129 if( !exactmatch ) return 0;
130 exactmatch = ( OT_FLAG( match ) & PEER_FLAG_SEEDING ) ? 2 : 1;
131 memmove( match, match + 1, sizeof(ot_peer) * ( end - match - 1 ) );
132 if( ( --vector->size * shrink_thresh < vector->space ) && ( vector->space > OT_VECTOR_MIN_MEMBERS ) ) {
133 vector->space /= OT_VECTOR_SHRINK_RATIO;
134 vector->data = realloc( vector->data, vector->space * sizeof( ot_peer ) );
135 }
136 if( !vector->size ) {
137 /* for peer pools its safe to let them go,
138 in 999 of 1000 this happens in older pools, that won't ever grow again */
139 free( vector->data );
140 vector->data = NULL;
141 vector->space = 0;
142 }
143 return exactmatch;
144}
145
146static void free_peerlist( ot_peerlist *peer_list ) {
147 size_t i; 31 size_t i;
148 for( i=0; i<OT_POOLS_COUNT; ++i ) 32 for( i=0; i<OT_POOLS_COUNT; ++i )
149 if( peer_list->peers[i].data ) 33 if( peer_list->peers[i].data )
@@ -154,27 +38,11 @@ static void free_peerlist( ot_peerlist *peer_list ) {
154 free( peer_list ); 38 free( peer_list );
155} 39}
156 40
157static void vector_remove_torrent( ot_vector *vector, ot_torrent *match ) {
158 ot_torrent *end = ((ot_torrent*)vector->data) + vector->size;
159
160 if( !vector->size ) return;
161
162 /* If this is being called after a unsuccessful malloc() for peer_list
163 in add_peer_to_torrent, match->peer_list actually might be NULL */
164 if( match->peer_list) free_peerlist( match->peer_list );
165
166 memmove( match, match + 1, sizeof(ot_torrent) * ( end - match - 1 ) );
167 if( ( --vector->size * OT_VECTOR_SHRINK_THRESH < vector->space ) && ( vector->space > OT_VECTOR_MIN_MEMBERS ) ) {
168 vector->space /= OT_VECTOR_SHRINK_RATIO;
169 vector->data = realloc( vector->data, vector->space * sizeof( ot_torrent ) );
170 }
171}
172
173ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC_PARAM( int from_changeset ) ) { 41ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC_PARAM( int from_changeset ) ) {
174 int exactmatch; 42 int exactmatch;
175 ot_torrent *torrent; 43 ot_torrent *torrent;
176 ot_peer *peer_dest; 44 ot_peer *peer_dest;
177 ot_vector *torrents_list = lock_bucket_by_hash( hash ), *peer_pool; 45 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ), *peer_pool;
178 int base_pool = 0; 46 int base_pool = 0;
179 47
180#ifdef WANT_ACCESS_CONTROL 48#ifdef WANT_ACCESS_CONTROL
@@ -184,13 +52,17 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
184 exactmatch = !exactmatch; 52 exactmatch = !exactmatch;
185#endif 53#endif
186 54
187 if( exactmatch ) 55 if( exactmatch ) {
188 return unlock_bucket_by_hash( hash ); 56 mutex_bucket_unlock_by_hash( hash );
57 return NULL;
58 }
189#endif 59#endif
190 60
191 torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 61 torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
192 if( !torrent ) 62 if( !torrent ) {
193 return unlock_bucket_by_hash( hash ); 63 mutex_bucket_unlock_by_hash( hash );
64 return NULL;
65 }
194 66
195 if( !exactmatch ) { 67 if( !exactmatch ) {
196 /* Create a new torrent entry, then */ 68 /* Create a new torrent entry, then */
@@ -198,7 +70,8 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
198 70
199 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { 71 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) {
200 vector_remove_torrent( torrents_list, torrent ); 72 vector_remove_torrent( torrents_list, torrent );
201 return unlock_bucket_by_hash( hash ); 73 mutex_bucket_unlock_by_hash( hash );
74 return NULL;
202 } 75 }
203 76
204 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 77 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) );
@@ -216,7 +89,7 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
216 peer_pool = &torrent->peer_list->peers[0]; 89 peer_pool = &torrent->peer_list->peers[0];
217 binary_search( peer, peer_pool->data, peer_pool->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, &exactmatch ); 90 binary_search( peer, peer_pool->data, peer_pool->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, &exactmatch );
218 if( exactmatch ) { 91 if( exactmatch ) {
219 unlock_bucket_by_hash( hash ); 92 mutex_bucket_unlock_by_hash( hash );
220 return torrent; 93 return torrent;
221 } 94 }
222 base_pool = 1; 95 base_pool = 1;
@@ -248,7 +121,7 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
248 torrent->peer_list->seed_count--; 121 torrent->peer_list->seed_count--;
249 case 1: default: 122 case 1: default:
250 torrent->peer_list->peer_count--; 123 torrent->peer_list->peer_count--;
251 unlock_bucket_by_hash( hash ); 124 mutex_bucket_unlock_by_hash( hash );
252 return torrent; 125 return torrent;
253 } 126 }
254 } 127 }
@@ -269,7 +142,7 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
269 memmove( peer_dest, peer, sizeof( ot_peer ) ); 142 memmove( peer_dest, peer, sizeof( ot_peer ) );
270 } 143 }
271 144
272 unlock_bucket_by_hash( hash ); 145 mutex_bucket_unlock_by_hash( hash );
273 return torrent; 146 return torrent;
274} 147}
275 148
@@ -282,13 +155,13 @@ ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC
282size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int is_tcp ) { 155size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int is_tcp ) {
283 char *r = reply; 156 char *r = reply;
284 int exactmatch; 157 int exactmatch;
285 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 158 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
286 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 159 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
287 ot_peerlist *peer_list = torrent->peer_list; 160 ot_peerlist *peer_list = torrent->peer_list;
288 size_t index; 161 size_t index;
289 162
290 if( !torrent ) { 163 if( !torrent ) {
291 unlock_bucket_by_hash( hash ); 164 mutex_bucket_unlock_by_hash( hash );
292 return 0; 165 return 0;
293 } 166 }
294 167
@@ -338,12 +211,12 @@ size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int
338 if( is_tcp ) 211 if( is_tcp )
339 *r++ = 'e'; 212 *r++ = 'e';
340 213
341 unlock_bucket_by_hash( hash ); 214 mutex_bucket_unlock_by_hash( hash );
342 return r - reply; 215 return r - reply;
343} 216}
344 217
345/* Release memory we allocated too much */ 218/* Release memory we allocated too much */
346static void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc ) { 219void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc ) {
347 int page_size = getpagesize(); 220 int page_size = getpagesize();
348 size_t old_pages = 1 + old_alloc / page_size; 221 size_t old_pages = 1 + old_alloc / page_size;
349 size_t new_pages = 1 + new_alloc / page_size; 222 size_t new_pages = 1 + new_alloc / page_size;
@@ -356,19 +229,23 @@ static void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc )
356size_t return_fullscrape_for_tracker( char **reply ) { 229size_t return_fullscrape_for_tracker( char **reply ) {
357 size_t torrent_count = 0, j; 230 size_t torrent_count = 0, j;
358 size_t allocated, replysize; 231 size_t allocated, replysize;
359 int i; 232 ot_vector *torrents_list;
233 int bucket;
360 char *r; 234 char *r;
361 235
362 for( i=0; i<OT_BUCKET_COUNT; ++i ) 236 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
363 torrent_count += all_torrents[i].size; 237 ot_vector *torrents_list = mutex_bucket_lock( bucket );
238 torrent_count += torrents_list->size;
239 mutex_bucket_unlock( bucket );
240 }
364 241
365 /* one extra for pro- and epilogue */ 242 /* one extra for pro- and epilogue */
366 allocated = 100*(1+torrent_count); 243 allocated = 100*(1+torrent_count);
367 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0; 244 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0;
368 245
369 memmove( r, "d5:filesd", 9 ); r += 9; 246 memmove( r, "d5:filesd", 9 ); r += 9;
370 for( i=0; i<OT_BUCKET_COUNT; ++i ) { 247 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
371 ot_vector *torrents_list = all_torrents + i; 248 torrents_list = mutex_bucket_lock( bucket );
372 for( j=0; j<torrents_list->size; ++j ) { 249 for( j=0; j<torrents_list->size; ++j ) {
373 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; 250 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
374 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash; 251 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
@@ -378,6 +255,7 @@ size_t return_fullscrape_for_tracker( char **reply ) {
378 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 ); 255 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 );
379 } 256 }
380 } 257 }
258 mutex_bucket_unlock( bucket );
381 } 259 }
382 260
383 *r++='e'; *r++='e'; 261 *r++='e'; *r++='e';
@@ -388,45 +266,10 @@ size_t return_fullscrape_for_tracker( char **reply ) {
388 return replysize; 266 return replysize;
389} 267}
390 268
391size_t return_memstat_for_tracker( char **reply ) {
392 size_t torrent_count = 0, j;
393 size_t allocated, replysize;
394 int i, k;
395 char *r;
396
397 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
398 ot_vector *torrents_list = all_torrents + i;
399 torrent_count += torrents_list->size;
400 }
401
402 allocated = OT_BUCKET_COUNT*32 + (43+OT_POOLS_COUNT*32)*torrent_count;
403 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) ) return 0;
404
405 for( i=0; i<OT_BUCKET_COUNT; ++i )
406 r += sprintf( r, "%02X: %08X %08X\n", i, (unsigned int)all_torrents[i].size, (unsigned int)all_torrents[i].space );
407
408 for( i=0; i<OT_BUCKET_COUNT; ++i ) {
409 ot_vector *torrents_list = all_torrents + i;
410 char hex_out[42];
411 for( j=0; j<torrents_list->size; ++j ) {
412 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
413 ot_hash *hash =&( ((ot_torrent*)(torrents_list->data))[j] ).hash;
414 r += sprintf( r, "\n%s:\n", to_hex( hex_out, (ot_byte*)hash) );
415 for( k=0; k<OT_POOLS_COUNT; ++k )
416 r += sprintf( r, "\t%05X %05X\n", ((unsigned int)peer_list->peers[k].size), (unsigned int)peer_list->peers[k].space );
417 }
418 }
419
420 replysize = ( r - *reply );
421 fix_mmapallocation( *reply, allocated, replysize );
422
423 return replysize;
424}
425
426/* Fetches scrape info for a specific torrent */ 269/* Fetches scrape info for a specific torrent */
427size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) { 270size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
428 int exactmatch; 271 int exactmatch;
429 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 272 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
430 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 273 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
431 274
432 if( !exactmatch ) { 275 if( !exactmatch ) {
@@ -443,7 +286,7 @@ size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ) {
443 r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count ); 286 r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count );
444 } 287 }
445 } 288 }
446 unlock_bucket_by_hash( hash ); 289 mutex_bucket_unlock_by_hash( hash );
447 return 12; 290 return 12;
448} 291}
449 292
@@ -456,7 +299,7 @@ size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *repl
456 299
457 for( i=0; i<amount; ++i ) { 300 for( i=0; i<amount; ++i ) {
458 ot_hash *hash = hash_list + i; 301 ot_hash *hash = hash_list + i;
459 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 302 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
460 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 303 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
461 304
462 if( exactmatch ) { 305 if( exactmatch ) {
@@ -468,347 +311,22 @@ size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *repl
468 torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ) + 23; 311 torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ) + 23;
469 } 312 }
470 } 313 }
471 unlock_bucket_by_hash( hash ); 314 mutex_bucket_unlock_by_hash( hash );
472 }
473
474 *r++ = 'e'; *r++ = 'e';
475 return r - reply;
476}
477
478#ifdef WANT_TRACKER_SYNC
479/* Import Changeset from an external authority
480 format: d4:syncd[..]ee
481 [..]: ( 20:01234567890abcdefghij16:XXXXYYYY )+
482*/
483int add_changeset_to_tracker( ot_byte *data, size_t len ) {
484 ot_hash *hash;
485 ot_byte *end = data + len;
486 unsigned long peer_count;
487
488 /* We do know, that the string is \n terminated, so it cant
489 overflow */
490 if( byte_diff( data, 8, "d4:syncd" ) ) return -1;
491 data += 8;
492
493 while( 1 ) {
494 if( byte_diff( data, 3, "20:" ) ) {
495 if( byte_diff( data, 2, "ee" ) )
496 return -1;
497 return 0;
498 }
499 data += 3;
500 hash = (ot_hash*)data;
501 data += sizeof( ot_hash );
502
503 /* Scan string length indicator */
504 data += ( len = scan_ulong( (char*)data, &peer_count ) );
505
506 /* If no long was scanned, it is not divisible by 8, it is not
507 followed by a colon or claims to need to much memory, we fail */
508 if( !len || !peer_count || ( peer_count & 7 ) || ( *data++ != ':' ) || ( data + peer_count > end ) )
509 return -1;
510
511 while( peer_count > 0 ) {
512 add_peer_to_torrent( hash, (ot_peer*)data, 1 );
513 data += 8; peer_count -= 8;
514 }
515 }
516 return 0;
517}
518
519/* Proposed output format
520 d4:syncd20:<info_hash>8*N:(xxxxyyyy)*Nee
521*/
522size_t return_changeset_for_tracker( char **reply ) {
523 size_t allocated = 0, i, replysize;
524 int bucket;
525 char *r;
526
527 /* Maybe there is time to clean_all_torrents(); */
528
529 /* Determine space needed for whole changeset */
530 for( bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket ) {
531 ot_vector *torrents_list = all_torrents + bucket;
532 for( i=0; i<torrents_list->size; ++i ) {
533 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
534 allocated += sizeof( ot_hash ) + sizeof(ot_peer) * torrent->peer_list->changeset.size + 13;
535 }
536 } 315 }
537 316
538 /* add "d4:syncd" and "ee" */
539 allocated += 8 + 2;
540
541 if( !( r = *reply = mmap( NULL, allocated, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 ) ) )
542 return 0;
543
544 memmove( r, "d4:syncd", 8 ); r += 8;
545 for( bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket ) {
546 ot_vector *torrents_list = all_torrents + bucket;
547 for( i=0; i<torrents_list->size; ++i ) {
548 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
549 const size_t byte_count = sizeof(ot_peer) * torrent->peer_list->changeset.size;
550 *r++ = '2'; *r++ = '0'; *r++ = ':';
551 memmove( r, torrent->hash, sizeof( ot_hash ) ); r += sizeof( ot_hash );
552 r += sprintf( r, "%zd:", byte_count );
553 memmove( r, torrent->peer_list->changeset.data, byte_count ); r += byte_count;
554 }
555 }
556 *r++ = 'e'; *r++ = 'e'; 317 *r++ = 'e'; *r++ = 'e';
557
558 replysize = ( r - *reply );
559 fix_mmapallocation( *reply, allocated, replysize );
560
561 return replysize;
562}
563#endif
564
565/* Clean a single torrent
566 return 1 if torrent timed out
567*/
568static int clean_single_torrent( ot_torrent *torrent ) {
569 ot_peerlist *peer_list = torrent->peer_list;
570 size_t peers_count = 0, seeds_count;
571 time_t timedout = (int)( NOW - peer_list->base );
572 int i;
573#ifdef WANT_TRACKER_SYNC
574 char *new_peers;
575#endif
576
577 /* Torrent has idled out */
578 if( timedout > OT_TORRENT_TIMEOUT )
579 return 1;
580
581 /* Nothing to be cleaned here? Test if torrent is worth keeping */
582 if( timedout > OT_POOLS_COUNT ) {
583 if( !peer_list->peer_count )
584 return peer_list->down_count ? 0 : 1;
585 timedout = OT_POOLS_COUNT;
586 }
587
588 /* Release vectors that have timed out */
589 for( i = OT_POOLS_COUNT - timedout; i < OT_POOLS_COUNT; ++i )
590 free( peer_list->peers[i].data);
591
592 /* Shift vectors back by the amount of pools that were shifted out */
593 memmove( peer_list->peers + timedout, peer_list->peers, sizeof( ot_vector ) * ( OT_POOLS_COUNT - timedout ) );
594 byte_zero( peer_list->peers, sizeof( ot_vector ) * timedout );
595
596 /* Shift back seed counts as well */
597 memmove( peer_list->seed_counts + timedout, peer_list->seed_counts, sizeof( size_t ) * ( OT_POOLS_COUNT - timedout ) );
598 byte_zero( peer_list->seed_counts, sizeof( size_t ) * timedout );
599
600#ifdef WANT_TRACKER_SYNC
601 /* Save the block modified within last OT_POOLS_TIMEOUT */
602 if( peer_list->peers[1].size &&
603 ( new_peers = realloc( peer_list->changeset.data, sizeof( ot_peer ) * peer_list->peers[1].size ) ) )
604 {
605 memmove( new_peers, peer_list->peers[1].data, peer_list->peers[1].size );
606 peer_list->changeset.data = new_peers;
607 peer_list->changeset.size = sizeof( ot_peer ) * peer_list->peers[1].size;
608 } else {
609 free( peer_list->changeset.data );
610
611 memset( &peer_list->changeset, 0, sizeof( ot_vector ) );
612 }
613#endif
614
615 peers_count = seeds_count = 0;
616 for( i = 0; i < OT_POOLS_COUNT; ++i ) {
617 peers_count += peer_list->peers[i].size;
618 seeds_count += peer_list->seed_counts[i];
619 }
620 peer_list->seed_count = seeds_count;
621 peer_list->peer_count = peers_count;
622
623 if( peers_count )
624 peer_list->base = NOW;
625 else {
626 /* When we got here, the last time that torrent
627 has been touched is OT_POOLS_COUNT units before */
628 peer_list->base = NOW - OT_POOLS_COUNT;
629 }
630 return 0;
631}
632
633/* Clean up all peers in current bucket, remove timedout pools and
634 torrents */
635void clean_all_torrents( void ) {
636 ot_vector *torrents_list;
637 size_t i;
638 static int bucket;
639 ot_time time_now = NOW;
640
641 /* Search for an uncleaned bucked */
642 while( ( all_torrents_clean[bucket] == time_now ) && ( ++bucket < OT_BUCKET_COUNT ) );
643 if( bucket >= OT_BUCKET_COUNT ) {
644 bucket = 0; return;
645 }
646
647 all_torrents_clean[bucket] = time_now;
648
649 mutex_bucket_lock( bucket );
650 torrents_list = all_torrents + bucket;
651 for( i=0; i<torrents_list->size; ++i ) {
652 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + i;
653 if( clean_single_torrent( torrent ) ) {
654 vector_remove_torrent( torrents_list, torrent );
655 --i; continue;
656 }
657 }
658 mutex_bucket_unlock( bucket );
659}
660
661typedef struct { size_t val; ot_torrent * torrent; } ot_record;
662
663/* Fetches stats from tracker */
664size_t return_stats_for_tracker( char *reply, int mode ) {
665 size_t torrent_count = 0, peer_count = 0, seed_count = 0, j;
666 ot_record top5s[5], top5c[5];
667 char *r = reply;
668 int bucket;
669
670 byte_zero( top5s, sizeof( top5s ) );
671 byte_zero( top5c, sizeof( top5c ) );
672
673 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
674 ot_vector *torrents_list = all_torrents + bucket;
675 mutex_bucket_lock( bucket );
676 torrent_count += torrents_list->size;
677 for( j=0; j<torrents_list->size; ++j ) {
678 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
679 if( mode == STATS_TOP5 ) {
680 int idx = 4; while( (idx >= 0) && ( peer_list->peer_count > top5c[idx].val ) ) --idx;
681 if ( idx++ != 4 ) {
682 memmove( top5c + idx + 1, top5c + idx, ( 4 - idx ) * sizeof( ot_record ) );
683 top5c[idx].val = peer_list->peer_count;
684 top5c[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
685 }
686 idx = 4; while( (idx >= 0) && ( peer_list->seed_count > top5s[idx].val ) ) --idx;
687 if ( idx++ != 4 ) {
688 memmove( top5s + idx + 1, top5s + idx, ( 4 - idx ) * sizeof( ot_record ) );
689 top5s[idx].val = peer_list->seed_count;
690 top5s[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
691 }
692 }
693 peer_count += peer_list->peer_count; seed_count += peer_list->seed_count;
694 }
695 mutex_bucket_unlock( bucket );
696 }
697 if( mode == STATS_TOP5 ) {
698 char hex_out[42];
699 int idx;
700 r += sprintf( r, "Top5 torrents by peers:\n" );
701 for( idx=0; idx<5; ++idx )
702 if( top5c[idx].torrent )
703 r += sprintf( r, "\t%zd\t%s\n", top5c[idx].val, to_hex( hex_out, top5c[idx].torrent->hash) );
704 r += sprintf( r, "Top5 torrents by seeds:\n" );
705 for( idx=0; idx<5; ++idx )
706 if( top5s[idx].torrent )
707 r += sprintf( r, "\t%zd\t%s\n", top5s[idx].val, to_hex( hex_out, top5s[idx].torrent->hash) );
708 } else
709 r += sprintf( r, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", peer_count, seed_count, torrent_count );
710
711 return r - reply;
712}
713
714/* This function collects 4096 /24s in 4096 possible
715 malloc blocks
716*/
717size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh ) {
718
719#define NUM_TOPBITS 12
720#define NUM_LOWBITS (24-NUM_TOPBITS)
721#define NUM_BUFS (1<<NUM_TOPBITS)
722#define NUM_S24S (1<<NUM_LOWBITS)
723#define MSK_S24S (NUM_S24S-1)
724
725 ot_dword *counts[ NUM_BUFS ];
726 ot_dword slash24s[amount*2]; /* first dword amount, second dword subnet */
727 int bucket;
728 size_t i, j, k, l;
729 char *r = reply;
730
731 byte_zero( counts, sizeof( counts ) );
732 byte_zero( slash24s, amount * 2 * sizeof(ot_dword) );
733
734 r += sprintf( r, "Stats for all /24s with more than %u announced torrents:\n\n", thresh );
735
736 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
737 ot_vector *torrents_list = all_torrents + bucket;
738 mutex_bucket_lock( bucket );
739 for( j=0; j<torrents_list->size; ++j ) {
740 ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
741 for( k=0; k<OT_POOLS_COUNT; ++k ) {
742 ot_peer *peers = peer_list->peers[k].data;
743 size_t numpeers = peer_list->peers[k].size;
744 for( l=0; l<numpeers; ++l ) {
745 ot_dword s24 = ntohl(*(ot_dword*)(peers+l)) >> 8;
746 ot_dword *count = counts[ s24 >> NUM_LOWBITS ];
747 if( !count ) {
748 count = malloc( sizeof(ot_dword) * NUM_S24S );
749 if( !count )
750 goto bailout_cleanup;
751 byte_zero( count, sizeof( ot_dword ) * NUM_S24S );
752 counts[ s24 >> NUM_LOWBITS ] = count;
753 }
754 count[ s24 & MSK_S24S ]++;
755 }
756 }
757 }
758 mutex_bucket_unlock( bucket );
759 }
760
761 k = l = 0; /* Debug: count allocated bufs */
762 for( i=0; i < NUM_BUFS; ++i ) {
763 ot_dword *count = counts[i];
764 if( !counts[i] )
765 continue;
766 ++k; /* Debug: count allocated bufs */
767 for( j=0; j < NUM_S24S; ++j ) {
768 if( count[j] > thresh ) {
769 /* This subnet seems to announce more torrents than the last in our list */
770 int insert_pos = amount - 1;
771 while( ( insert_pos >= 0 ) && ( count[j] > slash24s[ 2 * insert_pos ] ) )
772 --insert_pos;
773 ++insert_pos;
774 memmove( slash24s + 2 * ( insert_pos + 1 ), slash24s + 2 * ( insert_pos ), 2 * sizeof( ot_dword ) * ( amount - insert_pos - 1 ) );
775 slash24s[ 2 * insert_pos ] = count[j];
776 slash24s[ 2 * insert_pos + 1 ] = ( i << NUM_TOPBITS ) + j;
777 if( slash24s[ 2 * amount - 2 ] > thresh )
778 thresh = slash24s[ 2 * amount - 2 ];
779 }
780 if( count[j] ) ++l;
781 }
782 free( count );
783 }
784
785 r += sprintf( r, "Allocated bufs: %zd, used s24s: %zd\n", k, l );
786
787 for( i=0; i < amount; ++i )
788 if( slash24s[ 2*i ] >= thresh ) {
789 ot_dword ip = slash24s[ 2*i +1 ];
790 r += sprintf( r, "% 10ld %d.%d.%d.0/24\n", (long)slash24s[ 2*i ], (int)(ip >> 16), (int)(255 & ( ip >> 8 )), (int)(ip & 255) );
791 }
792
793 return r - reply; 318 return r - reply;
794
795bailout_cleanup:
796
797 for( i=0; i < NUM_BUFS; ++i )
798 free( counts[i] );
799
800 return 0;
801} 319}
802 320
803size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp ) { 321size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp ) {
804 int exactmatch; 322 int exactmatch;
805 size_t index; 323 size_t index;
806 ot_vector *torrents_list = lock_bucket_by_hash( hash ); 324 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash );
807 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 325 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch );
808 ot_peerlist *peer_list; 326 ot_peerlist *peer_list;
809 327
810 if( !exactmatch ) { 328 if( !exactmatch ) {
811 unlock_bucket_by_hash( hash ); 329 mutex_bucket_unlock_by_hash( hash );
812 330
813 if( is_tcp ) 331 if( is_tcp )
814 return sprintf( reply, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 332 return sprintf( reply, "d8:completei0e10:incompletei0e8:intervali%ie5:peers0:e", OT_CLIENT_REQUEST_INTERVAL_RANDOM );
@@ -835,7 +353,7 @@ exit_loop:
835 353
836 if( is_tcp ) { 354 if( is_tcp ) {
837 size_t reply_size = sprintf( reply, "d8:completei%zde10:incompletei%zde8:intervali%ie5:peers0:e", peer_list->seed_count, peer_list->peer_count - peer_list->seed_count, OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 355 size_t reply_size = sprintf( reply, "d8:completei%zde10:incompletei%zde8:intervali%ie5:peers0:e", peer_list->seed_count, peer_list->peer_count - peer_list->seed_count, OT_CLIENT_REQUEST_INTERVAL_RANDOM );
838 unlock_bucket_by_hash( hash ); 356 mutex_bucket_unlock_by_hash( hash );
839 return reply_size; 357 return reply_size;
840 } 358 }
841 359
@@ -844,7 +362,7 @@ exit_loop:
844 ((ot_dword*)reply)[3] = peer_list->peer_count - peer_list->seed_count; 362 ((ot_dword*)reply)[3] = peer_list->peer_count - peer_list->seed_count;
845 ((ot_dword*)reply)[4] = peer_list->seed_count; 363 ((ot_dword*)reply)[4] = peer_list->seed_count;
846 364
847 unlock_bucket_by_hash( hash ); 365 mutex_bucket_unlock_by_hash( hash );
848 return (size_t)20; 366 return (size_t)20;
849} 367}
850 368
@@ -874,30 +392,28 @@ int trackerlogic_init( const char * const serverdir ) {
874 } 392 }
875 393
876 srandom( time(NULL) ); 394 srandom( time(NULL) );
877 395
878 /* Initialize control structures */ 396 clean_init( );
879 byte_zero( all_torrents, sizeof( all_torrents ) );
880
881 mutex_init( ); 397 mutex_init( );
882 398
883 return 0; 399 return 0;
884} 400}
885 401
886void trackerlogic_deinit( void ) { 402void trackerlogic_deinit( void ) {
887 int i; 403 int bucket;
888 size_t j; 404 size_t j;
889 405
890 /* Free all torrents... */ 406 /* Free all torrents... */
891 for(i=0; i<OT_BUCKET_COUNT; ++i ) { 407 for(bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
892 if( all_torrents[i].size ) { 408 ot_vector *torrents_list = mutex_bucket_lock( bucket );
893 ot_torrent *torrents_list = (ot_torrent*)all_torrents[i].data; 409 if( torrents_list->size ) {
894 for( j=0; j<all_torrents[i].size; ++j ) 410 for( j=0; j<torrents_list->size; ++j ) {
895 free_peerlist( torrents_list[j].peer_list ); 411 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + j;
896 free( all_torrents[i].data ); 412 free_peerlist( torrent->peer_list );
413 }
414 free( torrents_list->data );
897 } 415 }
898 } 416 }
899 byte_zero( all_torrents, sizeof (all_torrents));
900 byte_zero( all_torrents_clean, sizeof (all_torrents_clean));
901
902 mutex_deinit( ); 417 mutex_deinit( );
903} 418 clean_deinit( );
419} \ No newline at end of file
diff --git a/trackerlogic.h b/trackerlogic.h
index 5b03012..fd80f92 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -1,8 +1,8 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org> 1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */ 2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3 3
4#ifndef __TRACKERLOGIC_H__ 4#ifndef __OT_TRACKERLOGIC_H__
5#define __TRACKERLOGIC_H__ 5#define __OT_TRACKERLOGIC_H__
6 6
7#include <sys/types.h> 7#include <sys/types.h>
8#include <sys/time.h> 8#include <sys/time.h>
@@ -20,16 +20,6 @@ typedef ot_byte ot_hash[20];
20typedef ot_dword ot_ip; 20typedef ot_dword ot_ip;
21typedef time_t ot_time; 21typedef time_t ot_time;
22 22
23#define OT_VECTOR_MIN_MEMBERS 4
24#define OT_VECTOR_GROW_RATIO 8
25#define OT_VECTOR_SHRINK_THRESH 6
26#define OT_VECTOR_SHRINK_RATIO 4
27typedef struct {
28 void *data;
29 size_t size;
30 size_t space;
31} ot_vector;
32
33/* Some tracker behaviour tunable */ 23/* Some tracker behaviour tunable */
34#define OT_CLIENT_TIMEOUT 30 24#define OT_CLIENT_TIMEOUT 30
35#define OT_CLIENT_TIMEOUT_CHECKINTERVAL 10 25#define OT_CLIENT_TIMEOUT_CHECKINTERVAL 10
@@ -42,6 +32,10 @@ typedef struct {
42 32
43#define OT_CLIENT_REQUEST_INTERVAL_RANDOM ( OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION/2 + (int)( random( ) % OT_CLIENT_REQUEST_VARIATION ) ) 33#define OT_CLIENT_REQUEST_INTERVAL_RANDOM ( OT_CLIENT_REQUEST_INTERVAL - OT_CLIENT_REQUEST_VARIATION/2 + (int)( random( ) % OT_CLIENT_REQUEST_VARIATION ) )
44 34
35/* We maintain a list of 1024 pointers to sorted list of ot_torrent structs
36 Sort key is, of course, its hash */
37#define OT_BUCKET_COUNT 1024
38
45/* Number of tracker admin ip addresses allowed */ 39/* Number of tracker admin ip addresses allowed */
46#define OT_ADMINIP_MAX 64 40#define OT_ADMINIP_MAX 64
47#define OT_MAX_THREADS 16 41#define OT_MAX_THREADS 16
@@ -70,7 +64,16 @@ static const ot_byte PEER_FLAG_STOPPED = 0x20;
70#define OT_PEER_COMPARE_SIZE ((size_t)6) 64#define OT_PEER_COMPARE_SIZE ((size_t)6)
71#define OT_HASH_COMPARE_SIZE (sizeof(ot_hash)) 65#define OT_HASH_COMPARE_SIZE (sizeof(ot_hash))
72 66
67struct ot_peerlist;
68typedef struct ot_peerlist ot_peerlist;
73typedef struct { 69typedef struct {
70 ot_hash hash;
71 ot_peerlist *peer_list;
72} ot_torrent;
73
74#include "ot_vector.h"
75
76struct ot_peerlist {
74 ot_time base; 77 ot_time base;
75 size_t seed_count; 78 size_t seed_count;
76 size_t peer_count; 79 size_t peer_count;
@@ -80,12 +83,7 @@ typedef struct {
80#ifdef WANT_TRACKER_SYNC 83#ifdef WANT_TRACKER_SYNC
81 ot_vector changeset; 84 ot_vector changeset;
82#endif 85#endif
83} ot_peerlist; 86};
84
85typedef struct {
86 ot_hash hash;
87 ot_peerlist *peer_list;
88} ot_torrent;
89 87
90/* 88/*
91 Exported functions 89 Exported functions
@@ -100,27 +98,21 @@ typedef struct {
100int trackerlogic_init( const char * const serverdir ); 98int trackerlogic_init( const char * const serverdir );
101void trackerlogic_deinit( void ); 99void trackerlogic_deinit( void );
102 100
103enum { STATS_CONNS, STATS_PEERS, STATS_TOP5, STATS_DMEM, STATS_TCP, STATS_UDP, STATS_SLASH24S, SYNC_IN, SYNC_OUT, STATS_FULLSCRAPE };
104
105ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC_PARAM( int from_changeset ) ); 101ot_torrent *add_peer_to_torrent( ot_hash *hash, ot_peer *peer WANT_TRACKER_SYNC_PARAM( int from_changeset ) );
106size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp ); 102size_t remove_peer_from_torrent( ot_hash *hash, ot_peer *peer, char *reply, int is_tcp );
107size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int is_tcp ); 103size_t return_peers_for_torrent( ot_hash *hash, size_t amount, char *reply, int is_tcp );
108size_t return_fullscrape_for_tracker( char **reply ); 104size_t return_fullscrape_for_tracker( char **reply );
109size_t return_tcp_scrape_for_torrent( ot_hash *hash, int amount, char *reply ); 105size_t return_tcp_scrape_for_torrent( ot_hash *hash, int amount, char *reply );
110size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply ); 106size_t return_udp_scrape_for_torrent( ot_hash *hash, char *reply );
111size_t return_stats_for_tracker( char *reply, int mode );
112size_t return_stats_for_slash24s( char *reply, size_t amount, ot_dword thresh );
113size_t return_memstat_for_tracker( char **reply );
114void clean_all_torrents( void ); 107void clean_all_torrents( void );
115 108
116#ifdef WANT_TRACKER_SYNC
117size_t return_changeset_for_tracker( char **reply );
118int add_changeset_to_tracker( ot_byte *data, size_t len );
119#endif
120
121#if defined ( WANT_BLACKLISTING ) || defined ( WANT_CLOSED_TRACKER ) 109#if defined ( WANT_BLACKLISTING ) || defined ( WANT_CLOSED_TRACKER )
122int accesslist_addentry( ot_hash *hash ); 110int accesslist_addentry( ot_hash *hash );
123void accesslist_reset( void ); 111void accesslist_reset( void );
124#endif 112#endif
125 113
114/* Helper, before it moves to its own object */
115void fix_mmapallocation( void *buf, size_t old_alloc, size_t new_alloc );
116void free_peerlist( ot_peerlist *peer_list );
117
126#endif 118#endif