summaryrefslogtreecommitdiff
path: root/trackerlogic.c
diff options
context:
space:
mode:
Diffstat (limited to 'trackerlogic.c')
-rw-r--r--trackerlogic.c635
1 files changed, 388 insertions, 247 deletions
diff --git a/trackerlogic.c b/trackerlogic.c
index ea4b489..04df544 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -4,454 +4,595 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <stdlib.h>
8#include <string.h>
9#include <stdio.h>
10#include <arpa/inet.h> 7#include <arpa/inet.h>
11#include <unistd.h>
12#include <errno.h> 8#include <errno.h>
13#include <stdint.h> 9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14 14
15/* Libowfat */ 15/* Libowfat */
16#include "array.h"
16#include "byte.h" 17#include "byte.h"
17#include "io.h" 18#include "io.h"
18#include "iob.h" 19#include "iob.h"
19#include "array.h" 20#include "ip6.h"
20 21
21/* Opentracker */ 22/* Opentracker */
22#include "trackerlogic.h"
23#include "ot_mutex.h"
24#include "ot_stats.h"
25#include "ot_clean.h"
26#include "ot_http.h"
27#include "ot_accesslist.h" 23#include "ot_accesslist.h"
24#include "ot_clean.h"
28#include "ot_fullscrape.h" 25#include "ot_fullscrape.h"
26#include "ot_http.h"
29#include "ot_livesync.h" 27#include "ot_livesync.h"
28#include "ot_mutex.h"
29#include "ot_stats.h"
30#include "ot_vector.h"
31#include "trackerlogic.h"
30 32
31/* Forward declaration */ 33/* Forward declaration */
32size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto ); 34size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto);
33 35
34void free_peerlist( ot_peerlist *peer_list ) { 36void free_peerlist(ot_peerlist *peer_list) {
35 if( peer_list->peers.data ) { 37 if (peer_list->peers.data) {
36 if( OT_PEERLIST_HASBUCKETS( peer_list ) ) { 38 if (OT_PEERLIST_HASBUCKETS(peer_list))
37 ot_vector *bucket_list = (ot_vector*)(peer_list->peers.data); 39 vector_clean_list((ot_vector *)peer_list->peers.data, peer_list->peers.size);
38 40 else
39 while( peer_list->peers.size-- ) 41 free(peer_list->peers.data);
40 free( bucket_list++->data );
41 }
42 free( peer_list->peers.data );
43 } 42 }
44 free( peer_list ); 43 free(peer_list);
45} 44}
46 45
47void add_torrent_from_saved_state( ot_hash hash, ot_time base, size_t down_count ) { 46void add_torrent_from_saved_state(ot_hash const hash, ot_time base, size_t down_count) {
48 int exactmatch; 47 int exactmatch;
49 ot_torrent *torrent; 48 ot_torrent *torrent;
50 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ); 49 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
51 50
52 if( !accesslist_hashisvalid( hash ) ) 51 if (!accesslist_hashisvalid(hash))
53 return mutex_bucket_unlock_by_hash( hash, 0 ); 52 return mutex_bucket_unlock_by_hash(hash, 0);
54 53
55 torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 54 torrent = vector_find_or_insert(torrents_list, (void *)hash, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
56 if( !torrent || exactmatch ) 55 if (!torrent || exactmatch)
57 return mutex_bucket_unlock_by_hash( hash, 0 ); 56 return mutex_bucket_unlock_by_hash(hash, 0);
58 57
59 /* Create a new torrent entry, then */ 58 /* Create a new torrent entry, then */
60 memcpy( torrent->hash, hash, sizeof(ot_hash) ); 59 byte_zero(torrent, sizeof(ot_torrent));
60 memcpy(torrent->hash, hash, sizeof(ot_hash));
61 61
62 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { 62 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
63 vector_remove_torrent( torrents_list, torrent ); 63 vector_remove_torrent(torrents_list, torrent);
64 return mutex_bucket_unlock_by_hash( hash, 0 ); 64 return mutex_bucket_unlock_by_hash(hash, 0);
65 } 65 }
66 66
67 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 67 byte_zero(torrent->peer_list6, sizeof(ot_peerlist));
68 torrent->peer_list->base = base; 68 byte_zero(torrent->peer_list4, sizeof(ot_peerlist));
69 torrent->peer_list->down_count = down_count; 69 torrent->peer_list6->base = base;
70 torrent->peer_list4->base = base;
71 torrent->peer_list6->down_count = down_count;
72 torrent->peer_list4->down_count = down_count;
70 73
71 return mutex_bucket_unlock_by_hash( hash, 1 ); 74 return mutex_bucket_unlock_by_hash(hash, 1);
72} 75}
73 76
74size_t add_peer_to_torrent_and_return_peers( PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount ) { 77size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) {
75 int exactmatch, delta_torrentcount = 0; 78 int exactmatch, delta_torrentcount = 0;
76 ot_torrent *torrent; 79 ot_torrent *torrent;
77 ot_peer *peer_dest; 80 ot_peer *peer_dest;
78 ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash ); 81 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
79 82 ot_peerlist *peer_list;
80 if( !accesslist_hashisvalid( *ws->hash ) ) { 83 size_t peer_size; /* initialized in next line */
81 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 84 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);
82 if( proto == FLAG_TCP ) { 85
86 if (!accesslist_hashisvalid(*ws->hash)) {
87 mutex_bucket_unlock_by_hash(*ws->hash, 0);
88 if (proto == FLAG_TCP) {
83 const char invalid_hash[] = "d14:failure reason63:Requested download is not authorized for use with this tracker.e"; 89 const char invalid_hash[] = "d14:failure reason63:Requested download is not authorized for use with this tracker.e";
84 memcpy( ws->reply, invalid_hash, strlen( invalid_hash ) ); 90 memcpy(ws->reply, invalid_hash, strlen(invalid_hash));
85 return strlen( invalid_hash ); 91 return strlen(invalid_hash);
86 } 92 }
87 return 0; 93 return 0;
88 } 94 }
89 95
90 torrent = vector_find_or_insert( torrents_list, (void*)ws->hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 96 torrent = vector_find_or_insert(torrents_list, (void *)ws->hash, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
91 if( !torrent ) { 97 if (!torrent) {
92 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 98 mutex_bucket_unlock_by_hash(*ws->hash, 0);
93 return 0; 99 return 0;
94 } 100 }
95 101
96 if( !exactmatch ) { 102 if (!exactmatch) {
97 /* Create a new torrent entry, then */ 103 /* Create a new torrent entry, then */
98 memcpy( torrent->hash, *ws->hash, sizeof(ot_hash) ); 104 byte_zero(torrent, sizeof(ot_torrent));
105 memcpy(torrent->hash, *ws->hash, sizeof(ot_hash));
99 106
100 if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { 107 if (!(torrent->peer_list6 = malloc(sizeof(ot_peerlist))) || !(torrent->peer_list4 = malloc(sizeof(ot_peerlist)))) {
101 vector_remove_torrent( torrents_list, torrent ); 108 vector_remove_torrent(torrents_list, torrent);
102 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 109 mutex_bucket_unlock_by_hash(*ws->hash, 0);
103 return 0; 110 return 0;
104 } 111 }
105 112
106 byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); 113 byte_zero(torrent->peer_list6, sizeof(ot_peerlist));
114 byte_zero(torrent->peer_list4, sizeof(ot_peerlist));
107 delta_torrentcount = 1; 115 delta_torrentcount = 1;
108 } else 116 } else
109 clean_single_torrent( torrent ); 117 clean_single_torrent(torrent);
110 118
111 torrent->peer_list->base = g_now_minutes; 119 torrent->peer_list6->base = g_now_minutes;
120 torrent->peer_list4->base = g_now_minutes;
121
122 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
112 123
113 /* Check for peer in torrent */ 124 /* Check for peer in torrent */
114 peer_dest = vector_find_or_insert_peer( &(torrent->peer_list->peers), &ws->peer, &exactmatch ); 125 peer_dest = vector_find_or_insert_peer(&(peer_list->peers), peer_src, peer_size, &exactmatch);
115 if( !peer_dest ) { 126 if (!peer_dest) {
116 mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); 127 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
117 return 0; 128 return 0;
118 } 129 }
119 130
120 /* Tell peer that it's fresh */ 131 /* Tell peer that it's fresh */
121 OT_PEERTIME( &ws->peer ) = 0; 132 OT_PEERTIME(ws->peer, OT_PEER_SIZE6) = 0;
122 133
123 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */ 134 /* Sanitize flags: Whoever claims to have completed download, must be a seeder */
124 if( ( OT_PEERFLAG( &ws->peer ) & ( PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING ) ) == PEER_FLAG_COMPLETED ) 135 if ((OT_PEERFLAG(ws->peer) & (PEER_FLAG_COMPLETED | PEER_FLAG_SEEDING)) == PEER_FLAG_COMPLETED)
125 OT_PEERFLAG( &ws->peer ) ^= PEER_FLAG_COMPLETED; 136 OT_PEERFLAG(ws->peer) ^= PEER_FLAG_COMPLETED;
126 137
127 /* If we hadn't had a match create peer there */ 138 /* If we hadn't had a match create peer there */
128 if( !exactmatch ) { 139 if (!exactmatch) {
129 140
130#ifdef WANT_SYNC_LIVE 141#ifdef WANT_SYNC_LIVE
131 if( proto == FLAG_MCA ) 142 if (proto == FLAG_MCA)
132 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_FROM_SYNC; 143 OT_PEERFLAG(ws->peer) |= PEER_FLAG_FROM_SYNC;
133 else 144 else
134 livesync_tell( ws ); 145 livesync_tell(ws);
135#endif 146#endif
136 147
137 torrent->peer_list->peer_count++; 148 peer_list->peer_count++;
138 if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) { 149 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) {
139 torrent->peer_list->down_count++; 150 peer_list->down_count++;
140 stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws ); 151 stats_issue_event(EVENT_COMPLETED, 0, (uintptr_t)ws);
141 } 152 }
142 if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) 153 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING)
143 torrent->peer_list->seed_count++; 154 peer_list->seed_count++;
144 155
145 } else { 156 } else {
146 stats_issue_event( EVENT_RENEW, 0, OT_PEERTIME( peer_dest ) ); 157 stats_issue_event(EVENT_RENEW, 0, OT_PEERTIME(peer_dest, peer_size));
147#ifdef WANT_SPOT_WOODPECKER 158#ifdef WANT_SPOT_WOODPECKER
148 if( ( OT_PEERTIME(peer_dest) > 0 ) && ( OT_PEERTIME(peer_dest) < 20 ) ) 159 if ((OT_PEERTIME(peer_dest, peer_size) > 0) && (OT_PEERTIME(peer_dest, peer_size) < 20))
149 stats_issue_event( EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer ); 160 stats_issue_event(EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer);
150#endif 161#endif
151#ifdef WANT_SYNC_LIVE 162#ifdef WANT_SYNC_LIVE
152 /* Won't live sync peers that come back too fast. Only exception: 163 /* Won't live sync peers that come back too fast. Only exception:
153 fresh "completed" reports */ 164 fresh "completed" reports */
154 if( proto != FLAG_MCA ) { 165 if (proto != FLAG_MCA) {
155 if( OT_PEERTIME( peer_dest ) > OT_CLIENT_SYNC_RENEW_BOUNDARY || 166 if (OT_PEERTIME(peer_dest, peer_size) > OT_CLIENT_SYNC_RENEW_BOUNDARY ||
156 ( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) ) ) 167 (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)))
157 livesync_tell( ws ); 168 livesync_tell(ws);
158 } 169 }
159#endif 170#endif
160 171
161 if( (OT_PEERFLAG(peer_dest) & PEER_FLAG_SEEDING ) && !(OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) ) 172 if ((OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && !(OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING))
162 torrent->peer_list->seed_count--; 173 peer_list->seed_count--;
163 if( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_SEEDING ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_SEEDING ) ) 174 if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING))
164 torrent->peer_list->seed_count++; 175 peer_list->seed_count++;
165 if( !(OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) && (OT_PEERFLAG(&ws->peer) & PEER_FLAG_COMPLETED ) ) { 176 if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) {
166 torrent->peer_list->down_count++; 177 peer_list->down_count++;
167 stats_issue_event( EVENT_COMPLETED, 0, (uintptr_t)ws ); 178 stats_issue_event(EVENT_COMPLETED, 0, (uintptr_t)ws);
168 } 179 }
169 if( OT_PEERFLAG(peer_dest) & PEER_FLAG_COMPLETED ) 180 if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED)
170 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; 181 OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED;
171 } 182 }
172 183
173 memcpy( peer_dest, &ws->peer, sizeof(ot_peer) ); 184 memcpy(peer_dest, peer_src, peer_size);
174#ifdef WANT_SYNC 185#ifdef WANT_SYNC
175 if( proto == FLAG_MCA ) { 186 if (proto == FLAG_MCA) {
176 mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); 187 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
177 return 0; 188 return 0;
178 } 189 }
179#endif 190#endif
180 191
181 ws->reply_size = return_peers_for_torrent( ws, torrent, amount, ws->reply, proto ); 192 ws->reply_size = return_peers_for_torrent(ws, torrent, amount, ws->reply, proto);
182 mutex_bucket_unlock_by_hash( *ws->hash, delta_torrentcount ); 193 mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount);
183 return ws->reply_size; 194 return ws->reply_size;
184} 195}
185 196
186static size_t return_peers_all( ot_peerlist *peer_list, char *reply ) { 197static size_t return_peers_all(ot_peerlist *peer_list, size_t peer_size, char *reply) {
187 unsigned int bucket, num_buckets = 1; 198 unsigned int bucket, num_buckets = 1;
188 ot_vector * bucket_list = &peer_list->peers; 199 ot_vector *bucket_list = &peer_list->peers;
189 size_t result = OT_PEER_COMPARE_SIZE * peer_list->peer_count; 200 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
190 char * r_end = reply + result; 201 size_t result = compare_size * peer_list->peer_count;
202 char *r_end = reply + result;
191 203
192 if( OT_PEERLIST_HASBUCKETS(peer_list) ) { 204 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
193 num_buckets = bucket_list->size; 205 num_buckets = bucket_list->size;
194 bucket_list = (ot_vector *)bucket_list->data; 206 bucket_list = (ot_vector *)bucket_list->data;
195 } 207 }
196 208
197 for( bucket = 0; bucket<num_buckets; ++bucket ) { 209 for (bucket = 0; bucket < num_buckets; ++bucket) {
198 ot_peer * peers = (ot_peer*)bucket_list[bucket].data; 210 ot_peer *peers = bucket_list[bucket].data;
199 size_t peer_count = bucket_list[bucket].size; 211 size_t peer_count = bucket_list[bucket].size;
200 while( peer_count-- ) { 212 while (peer_count--) {
201 if( OT_PEERFLAG(peers) & PEER_FLAG_SEEDING ) { 213 if (OT_PEERFLAG_D(peers, peer_size) & PEER_FLAG_SEEDING) {
202 r_end-=OT_PEER_COMPARE_SIZE; 214 r_end -= compare_size;
203 memcpy(r_end,peers++,OT_PEER_COMPARE_SIZE); 215 memcpy(r_end, peers, compare_size);
204 } else { 216 } else {
205 memcpy(reply,peers++,OT_PEER_COMPARE_SIZE); 217 memcpy(reply, peers, compare_size);
206 reply+=OT_PEER_COMPARE_SIZE; 218 reply += compare_size;
207 } 219 }
220 peers += peer_size;
208 } 221 }
209 } 222 }
210 return result; 223 return result;
211} 224}
212 225
213static size_t return_peers_selection( struct ot_workstruct *ws, ot_peerlist *peer_list, size_t amount, char *reply ) { 226static size_t return_peers_selection(struct ot_workstruct *ws, ot_peerlist *peer_list, size_t peer_size, size_t amount, char *reply) {
214 unsigned int bucket_offset, bucket_index = 0, num_buckets = 1; 227 unsigned int bucket_offset, bucket_index = 0, num_buckets = 1;
215 ot_vector * bucket_list = &peer_list->peers; 228 ot_vector *bucket_list = &peer_list->peers;
216 unsigned int shifted_pc = peer_list->peer_count; 229 unsigned int shifted_pc = peer_list->peer_count;
217 unsigned int shifted_step = 0; 230 unsigned int shifted_step = 0;
218 unsigned int shift = 0; 231 unsigned int shift = 0;
219 size_t result = OT_PEER_COMPARE_SIZE * amount; 232 size_t compare_size = OT_PEER_COMPARE_SIZE_FROM_PEER_SIZE(peer_size);
220 char * r_end = reply + result; 233 size_t result = compare_size * amount;
234 char *r_end = reply + result;
221 235
222 if( OT_PEERLIST_HASBUCKETS(peer_list) ) { 236 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
223 num_buckets = bucket_list->size; 237 num_buckets = bucket_list->size;
224 bucket_list = (ot_vector *)bucket_list->data; 238 bucket_list = (ot_vector *)bucket_list->data;
225 } 239 }
226 240
227 /* Make fixpoint arithmetic as exact as possible */ 241 /* Make fixpoint arithmetic as exact as possible */
228#define MAXPRECBIT (1<<(8*sizeof(int)-3)) 242#define MAXPRECBIT (1 << (8 * sizeof(int) - 3))
229 while( !(shifted_pc & MAXPRECBIT ) ) { shifted_pc <<= 1; shift++; } 243 while (!(shifted_pc & MAXPRECBIT)) {
230 shifted_step = shifted_pc/amount; 244 shifted_pc <<= 1;
245 shift++;
246 }
247 shifted_step = shifted_pc / amount;
231#undef MAXPRECBIT 248#undef MAXPRECBIT
232 249
233 /* Initialize somewhere in the middle of peers so that 250 /* Initialize somewhere in the middle of peers so that
234 fixpoint's aliasing doesn't alway miss the same peers */ 251 fixpoint's aliasing doesn't alway miss the same peers */
235 bucket_offset = nrand48(ws->rand48_state) % peer_list->peer_count; 252 bucket_offset = nrand48(ws->rand48_state) % peer_list->peer_count;
236 253
237 while( amount-- ) { 254 while (amount--) {
238 ot_peer * peer; 255 ot_peer *peer;
239 256
240 /* This is the aliased, non shifted range, next value may fall into */ 257 /* This is the aliased, non shifted range, next value may fall into */
241 unsigned int diff = ( ( ( amount + 1 ) * shifted_step ) >> shift ) - 258 unsigned int diff = (((amount + 1) * shifted_step) >> shift) - ((amount * shifted_step) >> shift);
242 ( ( amount * shifted_step ) >> shift ); 259 bucket_offset += 1 + nrand48(ws->rand48_state) % diff;
243 bucket_offset += 1 + nrand48(ws->rand48_state) % diff;
244 260
245 while( bucket_offset >= bucket_list[bucket_index].size ) { 261 while (bucket_offset >= bucket_list[bucket_index].size) {
246 bucket_offset -= bucket_list[bucket_index].size; 262 bucket_offset -= bucket_list[bucket_index].size;
247 bucket_index = ( bucket_index + 1 ) % num_buckets; 263 bucket_index = (bucket_index + 1) % num_buckets;
248 } 264 }
249 peer = ((ot_peer*)bucket_list[bucket_index].data) + bucket_offset; 265 peer = bucket_list[bucket_index].data + peer_size * bucket_offset;
250 if( OT_PEERFLAG(peer) & PEER_FLAG_SEEDING ) { 266 if (OT_PEERFLAG_D(peer, peer_size) & PEER_FLAG_SEEDING) {
251 r_end-=OT_PEER_COMPARE_SIZE; 267 r_end -= compare_size;
252 memcpy(r_end,peer,OT_PEER_COMPARE_SIZE); 268 memcpy(r_end, peer, compare_size);
253 } else { 269 } else {
254 memcpy(reply,peer,OT_PEER_COMPARE_SIZE); 270 memcpy(reply, peer, compare_size);
255 reply+=OT_PEER_COMPARE_SIZE; 271 reply += compare_size;
256 } 272 }
257 } 273 }
258 return result; 274 return result;
259} 275}
260 276
261/* Compiles a list of random peers for a torrent 277static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) {
262 * reply must have enough space to hold 92+6*amount bytes 278 char *r = reply;
263 * does not yet check not to return self 279 size_t peer_size = peer_size_from_peer6(&ws->peer);
264*/ 280 ot_peerlist *peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
265size_t return_peers_for_torrent( struct ot_workstruct * ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto ) { 281 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
266 ot_peerlist *peer_list = torrent->peer_list; 282 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
267 char *r = reply; 283
268 284 if (amount > peer_list->peer_count)
269 if( amount > peer_list->peer_count )
270 amount = peer_list->peer_count; 285 amount = peer_list->peer_count;
271 286
272 if( proto == FLAG_TCP ) { 287 *(uint32_t *)(r + 0) = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM);
273 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; 288 *(uint32_t *)(r + 4) = htonl(peer_count - seed_count);
274 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 ); 289 *(uint32_t *)(r + 8) = htonl(seed_count);
275 } else { 290 r += 12;
276 *(uint32_t*)(r+0) = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 291
277 *(uint32_t*)(r+4) = htonl( peer_list->peer_count - peer_list->seed_count ); 292 if (amount) {
278 *(uint32_t*)(r+8) = htonl( peer_list->seed_count ); 293 if (amount == peer_list->peer_count)
279 r += 12; 294 r += return_peers_all(peer_list, peer_size, r);
295 else
296 r += return_peers_selection(ws, peer_list, peer_size, amount, r);
297 }
298 return r - reply;
299}
300
301static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply) {
302 char *r = reply;
303 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
304 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
305 size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count;
306 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - seed_count;
307
308 /* Simple case: amount of peers in both lists is less than requested, here we return all results */
309 size_t amount_v4 = torrent->peer_list4->peer_count;
310 size_t amount_v6 = torrent->peer_list6->peer_count;
311
312 /* Complex case: both lists have more than enough entries and we need to split between v4 and v6 clients */
313 if (amount_v4 + amount_v6 > amount) {
314 size_t amount_left, percent_v6 = 0, percent_v4 = 0, left_v6, left_v4;
315 const size_t SCALE = 1024;
316
317 /* If possible, fill at least a quarter of peer from each family */
318 if (amount / 4 <= amount_v4)
319 amount_v4 = amount / 4;
320 if (amount / 4 <= amount_v6)
321 amount_v6 = amount / 4;
322
323 /* Fill the rest according to which family's pool provides more peers */
324 amount_left = amount - (amount_v4 + amount_v6);
325
326 left_v4 = torrent->peer_list4->peer_count - amount_v4;
327 left_v6 = torrent->peer_list6->peer_count - amount_v6;
328
329 if (left_v4 + left_v6) {
330 percent_v4 = (SCALE * left_v4) / (left_v4 + left_v6);
331 percent_v6 = (SCALE * left_v6) / (left_v4 + left_v6);
332 }
333
334 amount_v4 += (amount_left * percent_v4) / SCALE;
335 amount_v6 += (amount_left * percent_v6) / SCALE;
336
337 /* Integer division rounding can leave out a peer */
338 if (amount_v4 + amount_v6 < amount && amount_v6 < torrent->peer_list6->peer_count)
339 ++amount_v6;
340 if (amount_v4 + amount_v6 < amount && amount_v4 < torrent->peer_list4->peer_count)
341 ++amount_v4;
280 } 342 }
281 343
282 if( amount ) { 344 r +=
283 if( amount == peer_list->peer_count ) 345 sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zde8:intervali%ie12:min intervali%ie", seed_count, down_count, peer_count, erval, erval / 2);
284 r += return_peers_all( peer_list, r ); 346
347 if (amount_v4) {
348 r += sprintf(r, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4);
349 if (amount_v4 == torrent->peer_list4->peer_count)
350 r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r);
351 else
352 r += return_peers_selection(ws, torrent->peer_list4, OT_PEER_SIZE4, amount_v4, r);
353 }
354
355 if (amount_v6) {
356 r += sprintf(r, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6);
357 if (amount_v6 == torrent->peer_list6->peer_count)
358 r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r);
285 else 359 else
286 r += return_peers_selection( ws, peer_list, amount, r ); 360 r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r);
287 } 361 }
288 362
289 if( proto == FLAG_TCP ) 363 *r++ = 'e';
290 *r++ = 'e';
291 364
292 return r - reply; 365 return r - reply;
293} 366}
294 367
368/* Compiles a list of random peers for a torrent
369 * Reply must have enough space to hold:
370 * 92 + 6 * amount bytes for TCP/IPv4
371 * 92 + 18 * amount bytes for TCP/IPv6
372 * 12 + 6 * amount bytes for UDP/IPv4
373 * 12 + 18 * amount bytes for UDP/IPv6
374 * Does not yet check not to return self
375 */
376size_t return_peers_for_torrent(struct ot_workstruct *ws, ot_torrent *torrent, size_t amount, char *reply, PROTO_FLAG proto) {
377 return proto == FLAG_TCP ? return_peers_for_torrent_tcp(ws, torrent, amount, reply) : return_peers_for_torrent_udp(ws, torrent, amount, reply);
378}
379
295/* Fetches scrape info for a specific torrent */ 380/* Fetches scrape info for a specific torrent */
296size_t return_udp_scrape_for_torrent( ot_hash hash, char *reply ) { 381size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) {
297 int exactmatch, delta_torrentcount = 0; 382 int exactmatch, delta_torrentcount = 0;
298 ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ); 383 ot_vector *torrents_list = mutex_bucket_lock_by_hash(hash);
299 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 384 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
300 385
301 if( !exactmatch ) { 386 if (!exactmatch) {
302 memset( reply, 0, 12); 387 memset(reply, 0, 12);
303 } else { 388 } else {
304 uint32_t *r = (uint32_t*) reply; 389 uint32_t *r = (uint32_t *)reply;
305 390
306 if( clean_single_torrent( torrent ) ) { 391 if (clean_single_torrent(torrent)) {
307 vector_remove_torrent( torrents_list, torrent ); 392 vector_remove_torrent(torrents_list, torrent);
308 memset( reply, 0, 12); 393 memset(reply, 0, 12);
309 delta_torrentcount = -1; 394 delta_torrentcount = -1;
310 } else { 395 } else {
311 r[0] = htonl( torrent->peer_list->seed_count ); 396 r[0] = htonl(torrent->peer_list6->seed_count + torrent->peer_list4->seed_count);
312 r[1] = htonl( torrent->peer_list->down_count ); 397 r[1] = htonl(torrent->peer_list6->down_count + torrent->peer_list4->down_count);
313 r[2] = htonl( torrent->peer_list->peer_count-torrent->peer_list->seed_count ); 398 r[2] = htonl(torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
314 } 399 }
315 } 400 }
316 mutex_bucket_unlock_by_hash( hash, delta_torrentcount ); 401 mutex_bucket_unlock_by_hash(hash, delta_torrentcount);
317 return 12; 402 return 12;
318} 403}
319 404
320/* Fetches scrape info for a specific torrent */ 405/* Fetches scrape info for a specific torrent */
321size_t return_tcp_scrape_for_torrent( ot_hash *hash_list, int amount, char *reply ) { 406size_t return_tcp_scrape_for_torrent(ot_hash const *hash_list, int amount, char *reply) {
322 char *r = reply; 407 char *r = reply;
323 int exactmatch, i; 408 int exactmatch, i;
324 409
325 r += sprintf( r, "d5:filesd" ); 410 r += sprintf(r, "d5:filesd");
326 411
327 for( i=0; i<amount; ++i ) { 412 for (i = 0; i < amount; ++i) {
328 int delta_torrentcount = 0; 413 int delta_torrentcount = 0;
329 ot_hash *hash = hash_list + i; 414 ot_hash const *hash = hash_list + i;
330 ot_vector *torrents_list = mutex_bucket_lock_by_hash( *hash ); 415 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*hash);
331 ot_torrent *torrent = binary_search( hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 416 ot_torrent *torrent = binary_search(hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
332 417
333 if( exactmatch ) { 418 if (exactmatch) {
334 if( clean_single_torrent( torrent ) ) { 419 if (clean_single_torrent(torrent)) {
335 vector_remove_torrent( torrents_list, torrent ); 420 vector_remove_torrent(torrents_list, torrent);
336 delta_torrentcount = -1; 421 delta_torrentcount = -1;
337 } else { 422 } else {
338 *r++='2';*r++='0';*r++=':'; 423 *r++ = '2';
339 memcpy( r, hash, sizeof(ot_hash) ); r+=sizeof(ot_hash); 424 *r++ = '0';
340 r += sprintf( r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", 425 *r++ = ':';
341 torrent->peer_list->seed_count, torrent->peer_list->down_count, torrent->peer_list->peer_count-torrent->peer_list->seed_count ); 426 memcpy(r, hash, sizeof(ot_hash));
427 r += sizeof(ot_hash);
428 r += sprintf(r, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", torrent->peer_list6->seed_count + torrent->peer_list4->seed_count,
429 torrent->peer_list6->down_count + torrent->peer_list4->down_count,
430 torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count);
342 } 431 }
343 } 432 }
344 mutex_bucket_unlock_by_hash( *hash, delta_torrentcount ); 433 mutex_bucket_unlock_by_hash(*hash, delta_torrentcount);
345 } 434 }
346 435
347 *r++ = 'e'; *r++ = 'e'; 436 *r++ = 'e';
437 *r++ = 'e';
348 return r - reply; 438 return r - reply;
349} 439}
350 440
351static ot_peerlist dummy_list; 441static ot_peerlist dummy_list;
352size_t remove_peer_from_torrent( PROTO_FLAG proto, struct ot_workstruct *ws ) { 442size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) {
353 int exactmatch; 443 int exactmatch;
354 ot_vector *torrents_list = mutex_bucket_lock_by_hash( *ws->hash ); 444 ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash);
355 ot_torrent *torrent = binary_search( ws->hash, torrents_list->data, torrents_list->size, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); 445 ot_torrent *torrent = binary_search(ws->hash, torrents_list->data, torrents_list->size, sizeof(ot_torrent), OT_HASH_COMPARE_SIZE, &exactmatch);
356 ot_peerlist *peer_list = &dummy_list; 446 ot_peerlist *peer_list = &dummy_list;
447 size_t peer_size; /* initialized in next line */
448 ot_peer const *peer_src = peer_from_peer6(&ws->peer, &peer_size);
449 size_t peer_count = 0, seed_count = 0;
357 450
358#ifdef WANT_SYNC_LIVE 451#ifdef WANT_SYNC_LIVE
359 if( proto != FLAG_MCA ) { 452 if (proto != FLAG_MCA) {
360 OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; 453 OT_PEERFLAG(ws->peer) |= PEER_FLAG_STOPPED;
361 livesync_tell( ws ); 454 livesync_tell(ws);
362 } 455 }
363#endif 456#endif
364 457
365 if( exactmatch ) { 458 if (exactmatch) {
366 peer_list = torrent->peer_list; 459 peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4;
367 switch( vector_remove_peer( &peer_list->peers, &ws->peer ) ) { 460 switch (vector_remove_peer(&peer_list->peers, peer_src, peer_size)) {
368 case 2: peer_list->seed_count--; /* Fall throughs intended */ 461 case 2:
369 case 1: peer_list->peer_count--; /* Fall throughs intended */ 462 peer_list->seed_count--; /* Intentional fallthrough */
370 default: break; 463 case 1:
464 peer_list->peer_count--; /* Intentional fallthrough */
465 default:
466 break;
371 } 467 }
468
469 peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
470 seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
372 } 471 }
373 472
374 if( proto == FLAG_TCP ) { 473 if (proto == FLAG_TCP) {
375 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; 474 int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM;
376 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 ); 475 ws->reply_size = sprintf(ws->reply, "d8:completei%zde10:incompletei%zde8:intervali%ie12:min intervali%ie%s0:e", seed_count, peer_count - seed_count, erval,
476 erval / 2, peer_size == OT_PEER_SIZE6 ? PEERS_BENCODED6 : PEERS_BENCODED4);
377 } 477 }
378 478
379 /* Handle UDP reply */ 479 /* Handle UDP reply */
380 if( proto == FLAG_UDP ) { 480 if (proto == FLAG_UDP) {
381 ((uint32_t*)ws->reply)[2] = htonl( OT_CLIENT_REQUEST_INTERVAL_RANDOM ); 481 ((uint32_t *)ws->reply)[2] = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM);
382 ((uint32_t*)ws->reply)[3] = htonl( peer_list->peer_count - peer_list->seed_count ); 482 ((uint32_t *)ws->reply)[3] = htonl(peer_count - seed_count);
383 ((uint32_t*)ws->reply)[4] = htonl( peer_list->seed_count); 483 ((uint32_t *)ws->reply)[4] = htonl(seed_count);
384 ws->reply_size = 20; 484 ws->reply_size = 20;
385 } 485 }
386 486
387 mutex_bucket_unlock_by_hash( *ws->hash, 0 ); 487 mutex_bucket_unlock_by_hash(*ws->hash, 0);
388 return ws->reply_size; 488 return ws->reply_size;
389} 489}
390 490
391void iterate_all_torrents( int (*for_each)( ot_torrent* torrent, uintptr_t data ), uintptr_t data ) { 491void iterate_all_torrents(int (*for_each)(ot_torrent *torrent, uintptr_t data), uintptr_t data) {
392 int bucket; 492 int bucket;
393 size_t j; 493 size_t j;
394 494
395 for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 495 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
396 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 496 ot_vector *torrents_list = mutex_bucket_lock(bucket);
397 ot_torrent *torrents = (ot_torrent*)(torrents_list->data); 497 ot_torrent *torrents = (ot_torrent *)(torrents_list->data);
398 498
399 for( j=0; j<torrents_list->size; ++j ) 499 for (j = 0; j < torrents_list->size; ++j)
400 if( for_each( torrents + j, data ) ) 500 if (for_each(torrents + j, data))
401 break; 501 break;
402 502
403 mutex_bucket_unlock( bucket, 0 ); 503 mutex_bucket_unlock(bucket, 0);
404 if( !g_opentracker_running ) return; 504 if (!g_opentracker_running)
505 return;
506 }
507}
508
509ot_peer *peer_from_peer6(ot_peer6 *peer, size_t *peer_size) {
510 ot_ip6 *ip = (ot_ip6 *)peer;
511 if (!ip6_isv4mapped(ip)) {
512 *peer_size = OT_PEER_SIZE6;
513 return (ot_peer *)peer;
514 }
515 *peer_size = OT_PEER_SIZE4;
516 return (ot_peer *)(((uint8_t *)peer) + 12);
517}
518
519size_t peer_size_from_peer6(ot_peer6 *peer) {
520 ot_ip6 *ip = (ot_ip6 *)peer;
521 if (!ip6_isv4mapped(ip))
522 return OT_PEER_SIZE6;
523 return OT_PEER_SIZE4;
524}
525
526#ifdef _DEBUG_RANDOMTORRENTS
527void trackerlogic_add_random_torrents(size_t amount) {
528 struct ot_workstruct ws;
529 memset(&ws, 0, sizeof(ws));
530
531 ws.inbuf = malloc(G_INBUF_SIZE);
532 ws.outbuf = malloc(G_OUTBUF_SIZE);
533 ws.reply = ws.outbuf;
534 ws.hash = (ot_hash *)ws.inbuf;
535
536 while (amount--) {
537 arc4random_buf(ws.hash, sizeof(ot_hash));
538 arc4random_buf(&ws.peer, sizeof(ws.peer));
539
540 OT_PEERFLAG(ws.peer) &= PEER_FLAG_SEEDING | PEER_FLAG_COMPLETED | PEER_FLAG_STOPPED;
541
542 add_peer_to_torrent_and_return_peers(FLAG_TCP, &ws, 1);
405 } 543 }
544
545 free(ws.inbuf);
546 free(ws.outbuf);
406} 547}
548#endif
407 549
408void exerr( char * message ) { 550void exerr(char *message) {
409 fprintf( stderr, "%s\n", message ); 551 fprintf(stderr, "%s\n", message);
410 exit( 111 ); 552 exit(111);
411} 553}
412 554
413void trackerlogic_init( ) { 555void trackerlogic_init() {
414 g_tracker_id = random(); 556 g_tracker_id = random();
415 557
416 if( !g_stats_path ) 558 if (!g_stats_path)
417 g_stats_path = "stats"; 559 g_stats_path = "stats";
418 g_stats_path_len = strlen( g_stats_path ); 560 g_stats_path_len = strlen(g_stats_path);
419 561
420 /* Initialise background worker threads */ 562 /* Initialise background worker threads */
421 mutex_init( ); 563 mutex_init();
422 clean_init( ); 564 clean_init();
423 fullscrape_init( ); 565 fullscrape_init();
424 accesslist_init( ); 566 accesslist_init();
425 livesync_init( ); 567 livesync_init();
426 stats_init( ); 568 stats_init();
427} 569}
428 570
429void trackerlogic_deinit( void ) { 571void trackerlogic_deinit(void) {
430 int bucket, delta_torrentcount = 0; 572 int bucket, delta_torrentcount = 0;
431 size_t j; 573 size_t j;
432 574
433 /* Free all torrents... */ 575 /* Free all torrents... */
434 for(bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { 576 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
435 ot_vector *torrents_list = mutex_bucket_lock( bucket ); 577 ot_vector *torrents_list = mutex_bucket_lock(bucket);
436 if( torrents_list->size ) { 578 if (torrents_list->size) {
437 for( j=0; j<torrents_list->size; ++j ) { 579 for (j = 0; j < torrents_list->size; ++j) {
438 ot_torrent *torrent = ((ot_torrent*)(torrents_list->data)) + j; 580 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + j;
439 free_peerlist( torrent->peer_list ); 581 free_peerlist(torrent->peer_list6);
582 free_peerlist(torrent->peer_list4);
440 delta_torrentcount -= 1; 583 delta_torrentcount -= 1;
441 } 584 }
442 free( torrents_list->data ); 585 free(torrents_list->data);
443 } 586 }
444 mutex_bucket_unlock( bucket, delta_torrentcount ); 587 mutex_bucket_unlock(bucket, delta_torrentcount);
445 } 588 }
446 589
447 /* Deinitialise background worker threads */ 590 /* Deinitialise background worker threads */
448 stats_deinit( ); 591 stats_deinit();
449 livesync_deinit( ); 592 livesync_deinit();
450 accesslist_deinit( ); 593 accesslist_deinit();
451 fullscrape_deinit( ); 594 fullscrape_deinit();
452 clean_deinit( ); 595 clean_deinit();
453 /* Release mutexes */ 596 /* Release mutexes */
454 mutex_deinit( ); 597 mutex_deinit();
455} 598}
456
457const char *g_version_trackerlogic_c = "$Source$: $Revision$\n";