diff options
Diffstat (limited to 'trackerlogic.c')
-rw-r--r-- | trackerlogic.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/trackerlogic.c b/trackerlogic.c new file mode 100644 index 0000000..d21fc01 --- /dev/null +++ b/trackerlogic.c | |||
@@ -0,0 +1,273 @@ | |||
1 | // THIS REALLY BELONGS INTO A HEADER FILE | ||
2 | // | ||
3 | // | ||
4 | #include <string.h> | ||
5 | #include <stdio.h> | ||
6 | #include <fcntl.h> | ||
7 | #include <sys/types.h> | ||
8 | #include <sys/mman.h> | ||
9 | |||
10 | typedef unsigned char ot_hash[20]; | ||
11 | typedef unsigned char ot_ip[ 4/*0*/ ]; | ||
12 | typedef unsigned long ot_time; | ||
13 | // tunables | ||
14 | const unsigned long OT_TIMEOUT = 2700; | ||
15 | const unsigned long OT_HUGE_FILESIZE = 1024*1024*256; // Thats 256MB per file, enough for 204800 peers of 128 bytes | ||
16 | |||
17 | #define OT_COMPACT_ONLY | ||
18 | |||
19 | #define MEMMOVE memmove | ||
20 | #define BZERO bzero | ||
21 | #define FORMAT_FIXED_STRING sprintf | ||
22 | #define FORMAT_FORMAT_STRING sprintf | ||
23 | #define BINARY_FIND binary_search | ||
24 | |||
25 | typedef struct { | ||
26 | #ifndef OT_COMPACT_ONLY | ||
27 | ot_hash id; | ||
28 | ot_hash key; | ||
29 | #endif | ||
30 | ot_ip ip; | ||
31 | unsigned short port; | ||
32 | ot_time death; | ||
33 | unsigned char flags; | ||
34 | } ot_peer; | ||
35 | unsigned char PEER_FLAG_SEEDING = 0x80; | ||
36 | unsigned char PEER_IP_LENGTH_MASK = 0x3f; | ||
37 | |||
38 | typedef struct { | ||
39 | ot_hash hash; | ||
40 | ot_peer *peer_list; | ||
41 | unsigned long peer_count; | ||
42 | unsigned long seed_count; | ||
43 | } ot_torrent; | ||
44 | |||
45 | void *map_file( char *file_name ); | ||
46 | |||
47 | // This behaves quite like bsearch but allows to find | ||
48 | // the insertion point for inserts after unsuccessful searches | ||
49 | // in this case exactmatch is 0 on exit | ||
50 | // | ||
51 | void *binary_search( const void *key, const void *base, | ||
52 | const unsigned long member_count, const unsigned long member_size, | ||
53 | int (*compar) (const void *, const void *), | ||
54 | int *exactmatch ); | ||
55 | |||
56 | int compare_hash( const void *hash1, const void *hash2 ) { return memcmp( hash1, hash2, sizeof( ot_hash )); } | ||
57 | int compare_ip_port( const void *peer1, const void *peer2 ) { return memcmp( peer1, peer2, 6); } | ||
58 | |||
59 | // | ||
60 | // | ||
61 | // END OF STUFF THAT BELONGS INTO A HEADER FILE | ||
62 | |||
63 | ot_torrent *torrents_pointer = 0; | ||
64 | unsigned long torrents_count = 0; | ||
65 | unsigned char *scratchspace; | ||
66 | |||
67 | ot_torrent *add_peer_to_torrent( ot_hash hash, ot_peer *peer ) { | ||
68 | ot_torrent *torrent; | ||
69 | ot_peer *peer_dest; | ||
70 | int exactmatch; | ||
71 | |||
72 | torrent = BINARY_FIND( hash, torrents_pointer, torrents_count, sizeof( ot_torrent ), compare_hash, &exactmatch ); | ||
73 | if( !exactmatch ) { | ||
74 | // Assume, OS will provide us with space, after all, this is file backed | ||
75 | MEMMOVE( torrent + 1, torrent, ( torrents_pointer + torrents_count ) - torrent ); | ||
76 | |||
77 | // Create a new torrent entry, then | ||
78 | MEMMOVE( &torrent->hash, hash, sizeof( ot_hash ) ); | ||
79 | torrent->peer_list = map_file( hash ); | ||
80 | torrent->peer_count = 0; | ||
81 | torrent->seed_count = 0; | ||
82 | } | ||
83 | |||
84 | peer_dest = BINARY_FIND( peer, torrent->peer_list, torrent->peer_count, sizeof( ot_peer ), compare_ip_port, &exactmatch ); | ||
85 | if( exactmatch ) { | ||
86 | // If peer was a seeder but isn't anymore, decrease seeder count | ||
87 | if( ( peer_dest->flags & PEER_FLAG_SEEDING ) && !( peer->flags & PEER_FLAG_SEEDING ) ) | ||
88 | torrent->seed_count--; | ||
89 | if( !( peer_dest->flags & PEER_FLAG_SEEDING ) && ( peer->flags & PEER_FLAG_SEEDING ) ) | ||
90 | torrent->seed_count++; | ||
91 | } else { | ||
92 | // Assume, OS will provide us with space, after all, this is file backed | ||
93 | MEMMOVE( peer_dest + 1, peer_dest, ( torrent->peer_list + torrent->peer_count ) - peer_dest ); | ||
94 | |||
95 | // Create a new peer entry, then | ||
96 | MEMMOVE( peer_dest, peer, sizeof( ot_peer ) ); | ||
97 | |||
98 | torrent->peer_count++; | ||
99 | torrent->seed_count+= ( peer->flags & PEER_FLAG_SEEDING ) ? 1 : 0; | ||
100 | } | ||
101 | |||
102 | // Set new time out time | ||
103 | peer_dest->death = now() + OT_TIMEOUT; | ||
104 | |||
105 | return torrent; | ||
106 | } | ||
107 | |||
108 | #define SETINVALID( i ) (scratchspace[index] = 3); | ||
109 | #define SETSELECTED( i ) (scratchspace[index] = 1); | ||
110 | #define TESTSELECTED( i ) (scratchspace[index] == 1 ) | ||
111 | #define TESTSET( i ) (scratchspace[index]) | ||
112 | #define RANDOM random() | ||
113 | |||
114 | inline int TESTVALIDPEER( ot_peer *p ) { return p->death > now(); } | ||
115 | |||
116 | // Compiles a list of random peers for a torrent | ||
117 | // * scratch space keeps track of death or already selected peers | ||
118 | // * reply must have enough space to hold 1+(1+16+2+1)*amount+1 bytes | ||
119 | // * Selector function can be anything, maybe test for seeds, etc. | ||
120 | // * that RANDOM may return huge values | ||
121 | // * does not yet check not to return self | ||
122 | // * it is not guaranteed to see all peers, so no assumptions on active seeders/peers may be done | ||
123 | // * since compact format cannot handle v6 addresses, it must be enabled by OT_COMPACT_ONLY | ||
124 | // | ||
125 | void return_peers_for_torrent( ot_torrent *torrent, unsigned long amount, char *reply ) { | ||
126 | register ot_peer *peer_base = torrent->peer_list; | ||
127 | unsigned long peer_count = torrent->peer_count; | ||
128 | unsigned long selected_count = 0, invalid_count = 0; | ||
129 | unsigned long index = 0; | ||
130 | |||
131 | // optimize later ;) | ||
132 | BZERO( scratchspace, peer_count ); | ||
133 | |||
134 | while( ( selected_count < amount ) && ( selected_count + invalid_count < peer_count ) ) { | ||
135 | // skip to first non-flagged peer | ||
136 | while( TESTSET(index) ) index = ( index + 1 ) % peer_count; | ||
137 | |||
138 | if( TESTVALIDPEER( peer_base + index ) ) { | ||
139 | SETINVALID(index); invalid_count++; | ||
140 | } else { | ||
141 | SETSELECTED(index); selected_count++; | ||
142 | index = ( index + RANDOM ) % peer_count; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | // Now our scratchspace contains a list of selected_count valid peers | ||
147 | // Collect them into a reply string | ||
148 | index = 0; | ||
149 | |||
150 | #ifndef OT_COMPACT_ONLY | ||
151 | reply += FORMAT_FIXED_STRING( reply, "d5:peersl" ); | ||
152 | #else | ||
153 | reply += FORMAT_FORMAT_STRING( reply, "d5:peers%i:",6*selected_count ); | ||
154 | #endif | ||
155 | |||
156 | while( selected_count-- ) { | ||
157 | ot_peer *peer; | ||
158 | while( !TESTSELECTED( index ) ) ++index; | ||
159 | peer = peer_base + index; | ||
160 | #ifdef OT_COMPACT_ONLY | ||
161 | MEMMOVE( reply, &peer->ip, 4 ); | ||
162 | MEMMOVE( reply+4, &peer->port, 2 ); | ||
163 | reply += 6; | ||
164 | #else | ||
165 | reply += FORMAT_FORMAT_STRING( reply, "d2:ip%d:%s7:peer id20:%20c4:porti%ie", | ||
166 | peer->flags & PEER_IP_LENGTH_MASK, | ||
167 | peer->ip, | ||
168 | peer->id, | ||
169 | peer->port ); | ||
170 | #endif | ||
171 | } | ||
172 | #ifndef OT_COMPACT_ONLY | ||
173 | reply += FORMAT_FIXED_STRING( reply, "ee" ); | ||
174 | #else | ||
175 | reply += FORMAT_FIXED_STRING( reply, "e" ); | ||
176 | #endif | ||
177 | } | ||
178 | |||
179 | // Compacts a torrents peer list | ||
180 | // * torrents older than OT_TIMEOUT are being kicked | ||
181 | // * is rather expansive | ||
182 | // * if this fails, torrent file is invalid, should add flag | ||
183 | // | ||
184 | void heal_torrent( ot_torrent *torrent ) { | ||
185 | unsigned long index = 0, base = 0, end, seed_count = 0; | ||
186 | |||
187 | // Initialize base to first dead peer. | ||
188 | while( ( base < torrent->peer_count ) && torrent->peer_list[base].death <= now() ) { | ||
189 | seed_count += ( torrent->peer_list[base].flags & PEER_FLAG_SEEDING ) ? 1 : 0; | ||
190 | base++; | ||
191 | } | ||
192 | |||
193 | // No dead peers? Home. | ||
194 | if( base == torrent->peer_count ) return; | ||
195 | |||
196 | // From now index always looks to the next living peer while base keeps track of | ||
197 | // the dead peer that marks the beginning of insert space. | ||
198 | index = base + 1; | ||
199 | |||
200 | while( 1 ) { | ||
201 | // Let index search for next living peer | ||
202 | while( ( index < torrent->peer_count ) && torrent->peer_list[index].death > now() ) index++; | ||
203 | |||
204 | // No further living peers found - base is our new peer count | ||
205 | if( index == torrent->peer_count ) { | ||
206 | torrent->peer_count = base; | ||
207 | torrent->seed_count = seed_count; | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | end = index + 1; | ||
212 | |||
213 | // Let end search for next dead peer (end of living peers) | ||
214 | while( ( end < torrent->peer_count ) && torrent->peer_list[end].death <= now() ) { | ||
215 | seed_count += ( torrent->peer_list[end].flags & PEER_FLAG_SEEDING ) ? 1 : 0; | ||
216 | end++; | ||
217 | } | ||
218 | |||
219 | // We either hit a dead peer or the end of our peers | ||
220 | // In both cases: move block towards base | ||
221 | MEMMOVE( torrent->peer_list + base, torrent->peer_list + index, ( end - index ) * sizeof( ot_peer ) ); | ||
222 | base += end - index; | ||
223 | |||
224 | index = end; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | void *binary_search( const void *key, const void *base, | ||
229 | unsigned long member_count, const unsigned long member_size, | ||
230 | int (*compar) (const void *, const void *), | ||
231 | int *exactmatch ) { | ||
232 | unsigned char *lookat = ((unsigned char*)base) + member_size * (member_count >> 1); | ||
233 | *exactmatch = 1; | ||
234 | |||
235 | while( member_count ) { | ||
236 | int cmp = compar((void*)lookat, key); | ||
237 | if (cmp == 0) return (void *)lookat; | ||
238 | if (cmp < 0) { | ||
239 | base = (void*)(lookat + member_size); | ||
240 | --member_count; | ||
241 | } | ||
242 | member_count >>= 1; | ||
243 | lookat = ((unsigned char*)base) + member_size * (member_count >> 1); | ||
244 | } | ||
245 | *exactmatch = 0; | ||
246 | return (void*)lookat; | ||
247 | |||
248 | } | ||
249 | |||
250 | // This function maps a "huge" file into process space | ||
251 | // * I guess, we should be checking for more errors... | ||
252 | void *map_file( char *file_name ) { | ||
253 | char *map; | ||
254 | int file_desc=open(file_name,O_RDWR|O_CREAT|O_NDELAY,0644); | ||
255 | |||
256 | if( file_desc < 0) return 0; | ||
257 | |||
258 | map=mmap(0,OT_HUGE_FILESIZE,PROT_READ|PROT_WRITE,MAP_SHARED,file_desc,0); | ||
259 | close(file_desc); | ||
260 | |||
261 | return (map == (char*)-1) ? 0 : map; | ||
262 | } | ||
263 | |||
264 | int init_logic( ) { | ||
265 | unlink( "./opentracker_map_index.idx" ); | ||
266 | torrents_pointer = map_file( "./opentracker_map_index.idx" ); | ||
267 | torrents_count = 0; | ||
268 | scratchspace = map_file( "./scratchspace" ); | ||
269 | } | ||
270 | |||
271 | void deinit_logic( ) { | ||
272 | unmap_file( torrents_pointer ); | ||
273 | } | ||