diff options
| author | erdgeist <> | 2006-12-05 12:56:56 +0000 |
|---|---|---|
| committer | erdgeist <> | 2006-12-05 12:56:56 +0000 |
| commit | ad472597c569568bad0526d116b601dfcb36638c (patch) | |
| tree | 932f75b100baefdffa6899a76ff2a526dfb63d49 | |
Kickoff
| -rw-r--r-- | Makefile | 11 | ||||
| -rw-r--r-- | opentracker.c | 288 | ||||
| -rw-r--r-- | trackerlogic.c | 273 |
3 files changed, 572 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..22ddea9 --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | CC?=gcc | ||
| 2 | CFLAGS+=-I../libowfat -Wall -O2 -pipe | ||
| 3 | LDFLAGS+=-L../libowfat/ -lowfat -s | ||
| 4 | |||
| 5 | SOURCES=opentracker.c trackerlogic.c | ||
| 6 | |||
| 7 | opentracker: $(SOURCES) | ||
| 8 | $(CC) $(SOURCES) -o opentracker $(CFLAGS) $(LDFLAGS) | ||
| 9 | |||
| 10 | clean: | ||
| 11 | rm -rf opentracker | ||
diff --git a/opentracker.c b/opentracker.c new file mode 100644 index 0000000..bd47a24 --- /dev/null +++ b/opentracker.c | |||
| @@ -0,0 +1,288 @@ | |||
| 1 | #include "socket.h" | ||
| 2 | #include "io.h" | ||
| 3 | #include "buffer.h" | ||
| 4 | #include "ip6.h" | ||
| 5 | #include "array.h" | ||
| 6 | #include "case.h" | ||
| 7 | #include "fmt.h" | ||
| 8 | #include "iob.h" | ||
| 9 | #include "str.h" | ||
| 10 | #include <sys/types.h> | ||
| 11 | #include <sys/stat.h> | ||
| 12 | #include <unistd.h> | ||
| 13 | #include <stdlib.h> | ||
| 14 | #include <errno.h> | ||
| 15 | |||
| 16 | static void carp(const char* routine) { | ||
| 17 | buffer_puts(buffer_2,routine); | ||
| 18 | buffer_puts(buffer_2,": "); | ||
| 19 | buffer_puterror(buffer_2); | ||
| 20 | buffer_putnlflush(buffer_2); | ||
| 21 | } | ||
| 22 | |||
| 23 | static void panic(const char* routine) { | ||
| 24 | carp(routine); | ||
| 25 | exit(111); | ||
| 26 | } | ||
| 27 | |||
| 28 | struct http_data { | ||
| 29 | array r; | ||
| 30 | io_batch iob; | ||
| 31 | char* hdrbuf; | ||
| 32 | int hlen; | ||
| 33 | int keepalive; | ||
| 34 | }; | ||
| 35 | |||
| 36 | int header_complete(struct http_data* r) { | ||
| 37 | long i; | ||
| 38 | long l=array_bytes(&r->r); | ||
| 39 | const char* c=array_start(&r->r); | ||
| 40 | for (i=0; i+1<l; ++i) { | ||
| 41 | if (c[i]=='\n' && c[i+1]=='\n') | ||
| 42 | return i+2; | ||
| 43 | if (i+3<l && | ||
| 44 | c[i]=='\r' && c[i+1]=='\n' && | ||
| 45 | c[i+2]=='\r' && c[i+3]=='\n') | ||
| 46 | return i+4; | ||
| 47 | } | ||
| 48 | return 0; | ||
| 49 | } | ||
| 50 | |||
| 51 | void httperror(struct http_data* r,const char* title,const char* message) { | ||
| 52 | char* c; | ||
| 53 | c=r->hdrbuf=(char*)malloc(strlen(message)+strlen(title)+200); | ||
| 54 | if (!c) { | ||
| 55 | r->hdrbuf="HTTP/1.0 500 internal error\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nout of memory\n"; | ||
| 56 | r->hlen=strlen(r->hdrbuf); | ||
| 57 | } else { | ||
| 58 | c+=fmt_str(c,"HTTP/1.0 "); | ||
| 59 | c+=fmt_str(c,title); | ||
| 60 | c+=fmt_str(c,"\r\nContent-Type: text/html\r\nConnection: "); | ||
| 61 | c+=fmt_str(c,r->keepalive?"keep-alive":"close"); | ||
| 62 | c+=fmt_str(c,"\r\nContent-Length: "); | ||
| 63 | c+=fmt_ulong(c,strlen(message)+strlen(title)+16-4); | ||
| 64 | c+=fmt_str(c,"\r\n\r\n<title>"); | ||
| 65 | c+=fmt_str(c,title+4); | ||
| 66 | c+=fmt_str(c,"</title>\n"); | ||
| 67 | r->hlen=c - r->hdrbuf; | ||
| 68 | } | ||
| 69 | iob_addbuf(&r->iob,r->hdrbuf,r->hlen); | ||
| 70 | } | ||
| 71 | |||
| 72 | static struct mimeentry { const char* name, *type; } mimetab[] = { | ||
| 73 | { "html", "text/html" }, | ||
| 74 | { "css", "text/css" }, | ||
| 75 | { "dvi", "application/x-dvi" }, | ||
| 76 | { "ps", "application/postscript" }, | ||
| 77 | { "pdf", "application/pdf" }, | ||
| 78 | { "gif", "image/gif" }, | ||
| 79 | { "png", "image/png" }, | ||
| 80 | { "jpeg", "image/jpeg" }, | ||
| 81 | { "jpg", "image/jpeg" }, | ||
| 82 | { "mpeg", "video/mpeg" }, | ||
| 83 | { "mpg", "video/mpeg" }, | ||
| 84 | { "avi", "video/x-msvideo" }, | ||
| 85 | { "mov", "video/quicktime" }, | ||
| 86 | { "qt", "video/quicktime" }, | ||
| 87 | { "mp3", "audio/mpeg" }, | ||
| 88 | { "ogg", "audio/x-oggvorbis" }, | ||
| 89 | { "wav", "audio/x-wav" }, | ||
| 90 | { "pac", "application/x-ns-proxy-autoconfig" }, | ||
| 91 | { "sig", "application/pgp-signature" }, | ||
| 92 | { "torrent", "application/x-bittorrent" }, | ||
| 93 | { "class", "application/octet-stream" }, | ||
| 94 | { "js", "application/x-javascript" }, | ||
| 95 | { "tar", "application/x-tar" }, | ||
| 96 | { "zip", "application/zip" }, | ||
| 97 | { "dtd", "text/xml" }, | ||
| 98 | { "xml", "text/xml" }, | ||
| 99 | { "xbm", "image/x-xbitmap" }, | ||
| 100 | { "xpm", "image/x-xpixmap" }, | ||
| 101 | { "xwd", "image/x-xwindowdump" }, | ||
| 102 | { 0,0 } }; | ||
| 103 | |||
| 104 | const char* mimetype(const char* filename) { | ||
| 105 | int i,e=str_rchr(filename,'.'); | ||
| 106 | if (filename[e]==0) return "text/plain"; | ||
| 107 | ++e; | ||
| 108 | for (i=0; mimetab[i].name; ++i) | ||
| 109 | if (str_equal(mimetab[i].name,filename+e)) | ||
| 110 | return mimetab[i].type; | ||
| 111 | return "application/octet-stream"; | ||
| 112 | } | ||
| 113 | |||
| 114 | const char* http_header(struct http_data* r,const char* h) { | ||
| 115 | long i; | ||
| 116 | long l=array_bytes(&r->r); | ||
| 117 | long sl=strlen(h); | ||
| 118 | const char* c=array_start(&r->r); | ||
| 119 | for (i=0; i+sl+2<l; ++i) | ||
| 120 | if (c[i]=='\n' && case_equalb(c+i+1,sl,h) && c[i+sl+1]==':') { | ||
| 121 | c+=i+sl+1; | ||
| 122 | if (*c==' ' || *c=='\t') ++c; | ||
| 123 | return c; | ||
| 124 | } | ||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | void httpresponse(struct http_data* h,int64 s) { | ||
| 129 | char* c; | ||
| 130 | const char* m; | ||
| 131 | array_cat0(&h->r); | ||
| 132 | c=array_start(&h->r); | ||
| 133 | if (byte_diff(c,4,"GET ")) { | ||
| 134 | e400: | ||
| 135 | httperror(h,"400 Invalid Request","This server only understands GET."); | ||
| 136 | } else { | ||
| 137 | char *d; | ||
| 138 | int64 fd; | ||
| 139 | struct stat s; | ||
| 140 | c+=4; | ||
| 141 | for (d=c; *d!=' '&&*d!='\t'&&*d!='\n'&&*d!='\r'; ++d) ; | ||
| 142 | if (*d!=' ') goto e400; | ||
| 143 | *d=0; | ||
| 144 | if (c[0]!='/') goto e404; | ||
| 145 | while (c[1]=='/') ++c; | ||
| 146 | if (!io_readfile(&fd,c+1)) { | ||
| 147 | e404: | ||
| 148 | httperror(h,"404 Not Found","No such file or directory."); | ||
| 149 | } else { | ||
| 150 | if (fstat(fd,&s)==-1) { | ||
| 151 | io_close(fd); | ||
| 152 | goto e404; | ||
| 153 | } | ||
| 154 | if ((m=http_header(h,"Connection"))) { | ||
| 155 | if (str_equal(m,"keep-alive")) | ||
| 156 | h->keepalive=1; | ||
| 157 | else | ||
| 158 | h->keepalive=0; | ||
| 159 | } else { | ||
| 160 | if (byte_equal(d+1,8,"HTTP/1.0")) | ||
| 161 | h->keepalive=0; | ||
| 162 | else | ||
| 163 | h->keepalive=1; | ||
| 164 | } | ||
| 165 | m=mimetype(c); | ||
| 166 | c=h->hdrbuf=(char*)malloc(500); | ||
| 167 | c+=fmt_str(c,"HTTP/1.1 Coming Up\r\nContent-Type: "); | ||
| 168 | c+=fmt_str(c,m); | ||
| 169 | c+=fmt_str(c,"\r\nContent-Length: "); | ||
| 170 | c+=fmt_ulonglong(c,s.st_size); | ||
| 171 | c+=fmt_str(c,"\r\nLast-Modified: "); | ||
| 172 | c+=fmt_httpdate(c,s.st_mtime); | ||
| 173 | c+=fmt_str(c,"\r\nConnection: "); | ||
| 174 | c+=fmt_str(c,h->keepalive?"keep-alive":"close"); | ||
| 175 | c+=fmt_str(c,"\r\n\r\n"); | ||
| 176 | iob_addbuf(&h->iob,h->hdrbuf,c - h->hdrbuf); | ||
| 177 | iob_addfile(&h->iob,fd,0,s.st_size); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | io_dontwantread(s); | ||
| 181 | io_wantwrite(s); | ||
| 182 | } | ||
| 183 | |||
| 184 | int main() { | ||
| 185 | int s=socket_tcp6b(); | ||
| 186 | uint32 scope_id; | ||
| 187 | char ip[16]; | ||
| 188 | uint16 port; | ||
| 189 | if (socket_bind6_reuse(s,V6any,8000,0)==-1) | ||
| 190 | panic("socket_bind6_reuse"); | ||
| 191 | if (socket_listen(s,16)==-1) | ||
| 192 | panic("socket_listen"); | ||
| 193 | if (!io_fd(s)) | ||
| 194 | panic("io_fd"); | ||
| 195 | io_wantread(s); | ||
| 196 | for (;;) { | ||
| 197 | int64 i; | ||
| 198 | io_wait(); | ||
| 199 | while ((i=io_canread())!=-1) { | ||
| 200 | if (i==s) { | ||
| 201 | int n; | ||
| 202 | while ((n=socket_accept6(s,ip,&port,&scope_id))!=-1) { | ||
| 203 | char buf[IP6_FMT]; | ||
| 204 | buffer_puts(buffer_2,"accepted new connection from "); | ||
| 205 | buffer_put(buffer_2,buf,fmt_ip6(buf,ip)); | ||
| 206 | buffer_puts(buffer_2,":"); | ||
| 207 | buffer_putulong(buffer_2,port); | ||
| 208 | buffer_puts(buffer_2," (fd "); | ||
| 209 | buffer_putulong(buffer_2,n); | ||
| 210 | buffer_puts(buffer_2,")"); | ||
| 211 | if (io_fd(n)) { | ||
| 212 | struct http_data* h=(struct http_data*)malloc(sizeof(struct http_data)); | ||
| 213 | io_wantread(n); | ||
| 214 | if (h) { | ||
| 215 | byte_zero(h,sizeof(struct http_data)); | ||
| 216 | io_setcookie(n,h); | ||
| 217 | } else | ||
| 218 | io_close(n); | ||
| 219 | } else { | ||
| 220 | buffer_puts(buffer_2,", but io_fd failed."); | ||
| 221 | io_close(n); | ||
| 222 | } | ||
| 223 | buffer_putnlflush(buffer_2); | ||
| 224 | } | ||
| 225 | if (errno==EAGAIN) | ||
| 226 | io_eagain(s); | ||
| 227 | else | ||
| 228 | carp("socket_accept6"); | ||
| 229 | } else { | ||
| 230 | char buf[8192]; | ||
| 231 | struct http_data* h=io_getcookie(i); | ||
| 232 | int l=io_tryread(i,buf,sizeof buf); | ||
| 233 | if (l==-3) { | ||
| 234 | if (h) { | ||
| 235 | array_reset(&h->r); | ||
| 236 | iob_reset(&h->iob); | ||
| 237 | free(h->hdrbuf); h->hdrbuf=0; | ||
| 238 | } | ||
| 239 | buffer_puts(buffer_2,"io_tryread("); | ||
| 240 | buffer_putulong(buffer_2,i); | ||
| 241 | buffer_puts(buffer_2,"): "); | ||
| 242 | buffer_puterror(buffer_2); | ||
| 243 | buffer_putnlflush(buffer_2); | ||
| 244 | io_close(i); | ||
| 245 | } else if (l==0) { | ||
| 246 | if (h) { | ||
| 247 | array_reset(&h->r); | ||
| 248 | iob_reset(&h->iob); | ||
| 249 | free(h->hdrbuf); h->hdrbuf=0; | ||
| 250 | } | ||
| 251 | buffer_puts(buffer_2,"eof on fd #"); | ||
| 252 | buffer_putulong(buffer_2,i); | ||
| 253 | buffer_putnlflush(buffer_2); | ||
| 254 | io_close(i); | ||
| 255 | } else if (l>0) { | ||
| 256 | array_catb(&h->r,buf,l); | ||
| 257 | if (array_failed(&h->r)) { | ||
| 258 | httperror(h,"500 Server Error","request too long."); | ||
| 259 | emerge: | ||
| 260 | io_dontwantread(i); | ||
| 261 | io_wantwrite(i); | ||
| 262 | } else if (array_bytes(&h->r)>8192) { | ||
| 263 | httperror(h,"500 request too long","You sent too much headers"); | ||
| 264 | goto emerge; | ||
| 265 | } else if ((l=header_complete(h))) | ||
| 266 | httpresponse(h,i); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | } | ||
| 270 | while ((i=io_canwrite())!=-1) { | ||
| 271 | struct http_data* h=io_getcookie(i); | ||
| 272 | int64 r=iob_send(i,&h->iob); | ||
| 273 | /* printf("iob_send returned %lld\n",r); */ | ||
| 274 | if (r==-1) io_eagain(i); else | ||
| 275 | if (r<=0) { | ||
| 276 | array_trunc(&h->r); | ||
| 277 | iob_reset(&h->iob); | ||
| 278 | free(h->hdrbuf); h->hdrbuf=0; | ||
| 279 | if (h->keepalive) { | ||
| 280 | io_dontwantwrite(i); | ||
| 281 | io_wantread(i); | ||
| 282 | } else | ||
| 283 | io_close(i); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | } | ||
| 287 | return 0; | ||
| 288 | } | ||
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 | } | ||
