diff options
Diffstat (limited to 'opentracker.c')
| -rw-r--r-- | opentracker.c | 898 | 
1 files changed, 518 insertions, 380 deletions
| diff --git a/opentracker.c b/opentracker.c index 09010c7..14e9989 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -5,59 +5,59 @@ | |||
| 5 | $Id$ */ | 5 | $Id$ */ | 
| 6 | 6 | ||
| 7 | /* System */ | 7 | /* System */ | 
| 8 | #include <stdlib.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <arpa/inet.h> | 8 | #include <arpa/inet.h> | 
| 11 | #include <sys/socket.h> | 9 | #include <ctype.h> | 
| 12 | #include <unistd.h> | ||
| 13 | #include <errno.h> | 10 | #include <errno.h> | 
| 11 | #include <pthread.h> | ||
| 12 | #include <pwd.h> | ||
| 14 | #include <signal.h> | 13 | #include <signal.h> | 
| 15 | #include <stdio.h> | 14 | #include <stdio.h> | 
| 16 | #include <pwd.h> | 15 | #include <stdlib.h> | 
| 17 | #include <ctype.h> | 16 | #include <string.h> | 
| 18 | #include <pthread.h> | 17 | #include <sys/socket.h> | 
| 18 | #include <unistd.h> | ||
| 19 | #ifdef WANT_SYSLOGS | 19 | #ifdef WANT_SYSLOGS | 
| 20 | #include <syslog.h> | 20 | #include <syslog.h> | 
| 21 | #endif | 21 | #endif | 
| 22 | 22 | ||
| 23 | /* Libowfat */ | 23 | /* Libowfat */ | 
| 24 | #include "socket.h" | 24 | #include "byte.h" | 
| 25 | #include "io.h" | 25 | #include "io.h" | 
| 26 | #include "iob.h" | 26 | #include "iob.h" | 
| 27 | #include "byte.h" | ||
| 28 | #include "scan.h" | ||
| 29 | #include "ip6.h" | 27 | #include "ip6.h" | 
| 28 | #include "scan.h" | ||
| 29 | #include "socket.h" | ||
| 30 | 30 | ||
| 31 | /* Opentracker */ | 31 | /* Opentracker */ | 
| 32 | #include "trackerlogic.h" | ||
| 33 | #include "ot_mutex.h" | ||
| 34 | #include "ot_http.h" | ||
| 35 | #include "ot_udp.h" | ||
| 36 | #include "ot_accesslist.h" | 32 | #include "ot_accesslist.h" | 
| 37 | #include "ot_stats.h" | 33 | #include "ot_http.h" | 
| 38 | #include "ot_livesync.h" | 34 | #include "ot_livesync.h" | 
| 35 | #include "ot_mutex.h" | ||
| 36 | #include "ot_stats.h" | ||
| 37 | #include "ot_udp.h" | ||
| 38 | #include "trackerlogic.h" | ||
| 39 | 39 | ||
| 40 | /* Globals */ | 40 | /* Globals */ | 
| 41 | time_t g_now_seconds; | 41 | time_t g_now_seconds; | 
| 42 | char * g_redirecturl; | 42 | char *g_redirecturl; | 
| 43 | uint32_t g_tracker_id; | 43 | uint32_t g_tracker_id; | 
| 44 | volatile int g_opentracker_running = 1; | 44 | volatile int g_opentracker_running = 1; | 
| 45 | int g_self_pipe[2]; | 45 | int g_self_pipe[2]; | 
| 46 | 46 | ||
| 47 | static char * g_serverdir; | 47 | static char *g_serverdir; | 
| 48 | static char * g_serveruser; | 48 | static char *g_serveruser; | 
| 49 | static unsigned int g_udp_workers; | 49 | static unsigned int g_udp_workers; | 
| 50 | 50 | ||
| 51 | static void panic( const char *routing ) __attribute__ ((noreturn)); | 51 | static void panic(const char *routine) __attribute__((noreturn)); | 
| 52 | static void panic( const char *routine ) { | 52 | static void panic(const char *routine) { | 
| 53 | fprintf( stderr, "%s: %s\n", routine, strerror(errno) ); | 53 | fprintf(stderr, "%s: %s\n", routine, strerror(errno)); | 
| 54 | exit( 111 ); | 54 | exit(111); | 
| 55 | } | 55 | } | 
| 56 | 56 | ||
| 57 | static void signal_handler( int s ) { | 57 | static void signal_handler(int s) { | 
| 58 | if( s == SIGINT ) { | 58 | if (s == SIGINT) { | 
| 59 | /* Any new interrupt signal quits the application */ | 59 | /* Any new interrupt signal quits the application */ | 
| 60 | signal( SIGINT, SIG_DFL); | 60 | signal(SIGINT, SIG_DFL); | 
| 61 | 61 | ||
| 62 | /* Tell all other threads to not acquire any new lock on a bucket | 62 | /* Tell all other threads to not acquire any new lock on a bucket | 
| 63 | but cancel their operations and return */ | 63 | but cancel their operations and return */ | 
| @@ -69,483 +69,566 @@ static void signal_handler( int s ) { | |||
| 69 | closelog(); | 69 | closelog(); | 
| 70 | #endif | 70 | #endif | 
| 71 | 71 | ||
| 72 | exit( 0 ); | 72 | exit(0); | 
| 73 | } else if( s == SIGALRM ) { | ||
| 74 | /* Maintain our copy of the clock. time() on BSDs is very expensive. */ | ||
| 75 | g_now_seconds = time(NULL); | ||
| 76 | alarm(5); | ||
| 77 | } | 73 | } | 
| 78 | } | 74 | } | 
| 79 | 75 | ||
| 80 | static void defaul_signal_handlers( void ) { | 76 | static void defaul_signal_handlers(void) { | 
| 81 | sigset_t signal_mask; | 77 | sigset_t signal_mask; | 
| 82 | sigemptyset(&signal_mask); | 78 | sigemptyset(&signal_mask); | 
| 83 | sigaddset (&signal_mask, SIGPIPE); | 79 | sigaddset(&signal_mask, SIGPIPE); | 
| 84 | sigaddset (&signal_mask, SIGHUP); | 80 | sigaddset(&signal_mask, SIGHUP); | 
| 85 | sigaddset (&signal_mask, SIGINT); | 81 | sigaddset(&signal_mask, SIGINT); | 
| 86 | sigaddset (&signal_mask, SIGALRM); | 82 | sigaddset(&signal_mask, SIGALRM); | 
| 87 | pthread_sigmask (SIG_BLOCK, &signal_mask, NULL); | 83 | pthread_sigmask(SIG_BLOCK, &signal_mask, NULL); | 
| 88 | } | 84 | } | 
| 89 | 85 | ||
| 90 | static void install_signal_handlers( void ) { | 86 | static void install_signal_handlers(void) { | 
| 91 | struct sigaction sa; | 87 | struct sigaction sa; | 
| 92 | sigset_t signal_mask; | 88 | sigset_t signal_mask; | 
| 93 | sigemptyset(&signal_mask); | 89 | sigemptyset(&signal_mask); | 
| 94 | 90 | ||
| 95 | sa.sa_handler = signal_handler; | 91 | sa.sa_handler = signal_handler; | 
| 96 | sigemptyset(&sa.sa_mask); | 92 | sigemptyset(&sa.sa_mask); | 
| 97 | sa.sa_flags = SA_RESTART; | 93 | sa.sa_flags = SA_RESTART; | 
| 98 | if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1) ) | 94 | if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1)) | 
| 99 | panic( "install_signal_handlers" ); | 95 | panic("install_signal_handlers"); | 
| 100 | 96 | ||
| 101 | sigaddset (&signal_mask, SIGINT); | 97 | sigaddset(&signal_mask, SIGINT); | 
| 102 | sigaddset (&signal_mask, SIGALRM); | 98 | pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL); | 
| 103 | pthread_sigmask (SIG_UNBLOCK, &signal_mask, NULL); | ||
| 104 | } | 99 | } | 
| 105 | 100 | ||
| 106 | static void usage( char *name ) { | 101 | static void usage(char *name) { | 
| 107 | fprintf( stderr, "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip] [-f config] [-s livesyncport]" | 102 | fprintf(stderr, | 
| 103 | "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip[/bits]] [-f config] [-s livesyncport]" | ||
| 108 | #ifdef WANT_ACCESSLIST_BLACK | 104 | #ifdef WANT_ACCESSLIST_BLACK | 
| 109 | " [-b blacklistfile]" | 105 | " [-b blacklistfile]" | 
| 110 | #elif defined ( WANT_ACCESSLIST_WHITE ) | 106 | #elif defined(WANT_ACCESSLIST_WHITE) | 
| 111 | " [-w whitelistfile]" | 107 | " [-w whitelistfile]" | 
| 112 | #endif | 108 | #endif | 
| 113 | "\n", name ); | 109 | "\n", | 
| 110 | name); | ||
| 114 | } | 111 | } | 
| 115 | 112 | ||
| 116 | #define HELPLINE(opt,desc) fprintf(stderr, "\t%-10s%s\n",opt,desc) | 113 | #define HELPLINE(opt, desc) fprintf(stderr, "\t%-10s%s\n", opt, desc) | 
| 117 | static void help( char *name ) { | 114 | static void help(char *name) { | 
| 118 | usage( name ); | 115 | usage(name); | 
| 119 | 116 | ||
| 120 | HELPLINE("-f config","include and execute the config file"); | 117 | HELPLINE("-f config", "include and execute the config file"); | 
| 121 | HELPLINE("-i ip","specify ip to bind to (default: *, you may specify more than one)"); | 118 | HELPLINE("-i ip", "specify ip to bind to with next -[pP] (default: any, overrides preceeding ones)"); | 
| 122 | HELPLINE("-p port","specify tcp port to bind to (default: 6969, you may specify more than one)"); | 119 | HELPLINE("-p port", "do bind to tcp port (default: 6969, you may specify more than one)"); | 
| 123 | HELPLINE("-P port","specify udp port to bind to (default: 6969, you may specify more than one)"); | 120 | HELPLINE("-P port", "do bind to udp port (default: 6969, you may specify more than one)"); | 
| 124 | HELPLINE("-r redirecturl","specify url where / should be redirected to (default none)"); | 121 | HELPLINE("-r redirecturl", "specify url where / should be redirected to (default none)"); | 
| 125 | HELPLINE("-d dir","specify directory to try to chroot to (default: \".\")"); | 122 | HELPLINE("-d dir", "specify directory to try to chroot to (default: \".\")"); | 
| 126 | HELPLINE("-u user","specify user under whose priviliges opentracker should run (default: \"nobody\")"); | 123 | HELPLINE("-u user", "specify user under whose privileges opentracker should run (default: \"nobody\")"); | 
| 127 | HELPLINE("-A ip","bless an ip address as admin address (e.g. to allow syncs from this address)"); | 124 | HELPLINE("-A ip[/bits]", "bless an ip address or net as admin address (e.g. to allow syncs from this address)"); | 
| 128 | #ifdef WANT_ACCESSLIST_BLACK | 125 | #ifdef WANT_ACCESSLIST_BLACK | 
| 129 | HELPLINE("-b file","specify blacklist file."); | 126 | HELPLINE("-b file", "specify blacklist file."); | 
| 130 | #elif defined( WANT_ACCESSLIST_WHITE ) | 127 | #elif defined(WANT_ACCESSLIST_WHITE) | 
| 131 | HELPLINE("-w file","specify whitelist file."); | 128 | HELPLINE("-w file", "specify whitelist file."); | 
| 132 | #endif | 129 | #endif | 
| 133 | 130 | ||
| 134 | fprintf( stderr, "\nExample: ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -f ./opentracker.conf -i 10.1.1.23 -p 2710 -p 80\n" ); | 131 | fprintf(stderr, "\nExample: ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -f ./opentracker.conf -i 10.1.1.23 -p 2710 -p 80\n"); | 
| 132 | fprintf(stderr, " Here -i 127.0.0.1 selects the ip address for the next -p 6969 and -P 6969.\n"); | ||
| 133 | fprintf(stderr, " If no port is bound from config file or command line, the last address given\n"); | ||
| 134 | fprintf(stderr, " (or ::1 if none is set) will be used on port 6969.\n"); | ||
| 135 | } | 135 | } | 
| 136 | #undef HELPLINE | 136 | #undef HELPLINE | 
| 137 | 137 | ||
| 138 | static size_t header_complete( char * request, ssize_t byte_count ) { | 138 | static ssize_t header_complete(char *request, ssize_t byte_count) { | 
| 139 | int i = 0, state = 0; | 139 | ssize_t i = 0, state = 0; | 
| 140 | 140 | ||
| 141 | for( i=1; i < byte_count; i+=2 ) | 141 | for (i = 1; i < byte_count; i += 2) | 
| 142 | if( request[i] <= 13 ) { | 142 | if (request[i] <= 13) { | 
| 143 | i--; | 143 | i--; | 
| 144 | for( state = 0 ; i < byte_count; ++i ) { | 144 | for (state = 0; i < byte_count; ++i) { | 
| 145 | char c = request[i]; | 145 | char c = request[i]; | 
| 146 | if( c == '\r' || c == '\n' ) | 146 | if (c == '\r' || c == '\n') | 
| 147 | state = ( state >> 2 ) | ( ( c << 6 ) & 0xc0 ); | 147 | state = (state >> 2) | ((c << 6) & 0xc0); | 
| 148 | else | 148 | else | 
| 149 | break; | 149 | break; | 
| 150 | if( state >= 0xa0 || state == 0x99 ) return i + 1; | 150 | if (state >= 0xa0 || state == 0x99) | 
| 151 | return i + 1; | ||
| 151 | } | 152 | } | 
| 152 | } | 153 | } | 
| 153 | return 0; | 154 | return 0; | 
| 154 | } | 155 | } | 
| 155 | 156 | ||
| 156 | static void handle_dead( const int64 sock ) { | 157 | static void handle_dead(const int64 sock) { | 
| 157 | struct http_data* cookie=io_getcookie( sock ); | 158 | struct http_data *cookie = io_getcookie(sock); | 
| 158 | if( cookie ) { | 159 | if (cookie) { | 
| 159 | iob_reset( &cookie->batch ); | 160 | size_t i; | 
| 160 | array_reset( &cookie->request ); | 161 | for (i = 0; i < cookie->batches; ++i) | 
| 161 | if( cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK ) | 162 | iob_reset(cookie->batch + i); | 
| 162 | mutex_workqueue_canceltask( sock ); | 163 | free(cookie->batch); | 
| 163 | free( cookie ); | 164 | array_reset(&cookie->request); | 
| 165 | if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) | ||
| 166 | mutex_workqueue_canceltask(sock); | ||
| 167 | free(cookie); | ||
| 164 | } | 168 | } | 
| 165 | io_close( sock ); | 169 | io_close(sock); | 
| 166 | } | 170 | } | 
| 167 | 171 | ||
| 168 | static void handle_read( const int64 sock, struct ot_workstruct *ws ) { | 172 | static void handle_read(const int64 sock, struct ot_workstruct *ws) { | 
| 169 | struct http_data* cookie = io_getcookie( sock ); | 173 | struct http_data *cookie = io_getcookie(sock); | 
| 170 | ssize_t byte_count; | 174 | ssize_t byte_count = io_tryread(sock, ws->inbuf, G_INBUF_SIZE); | 
| 171 | 175 | ||
| 172 | if( ( byte_count = io_tryread( sock, ws->inbuf, G_INBUF_SIZE ) ) <= 0 ) { | 176 | if (byte_count == 0 || byte_count == -3) { | 
| 173 | handle_dead( sock ); | 177 | handle_dead(sock); | 
| 174 | return; | 178 | return; | 
| 175 | } | 179 | } | 
| 176 | 180 | ||
| 181 | if (byte_count == -1) | ||
| 182 | return; | ||
| 183 | |||
| 177 | /* If we get the whole request in one packet, handle it without copying */ | 184 | /* If we get the whole request in one packet, handle it without copying */ | 
| 178 | if( !array_start( &cookie->request ) ) { | 185 | if (!array_start(&cookie->request)) { | 
| 179 | if( ( ws->header_size = header_complete( ws->inbuf, byte_count ) ) ) { | 186 | if ((ws->header_size = header_complete(ws->inbuf, byte_count))) { | 
| 180 | ws->request = ws->inbuf; | 187 | ws->request = ws->inbuf; | 
| 181 | ws->request_size = byte_count; | 188 | ws->request_size = byte_count; | 
| 182 | http_handle_request( sock, ws ); | 189 | http_handle_request(sock, ws); | 
| 183 | } else | 190 | } else | 
| 184 | array_catb( &cookie->request, ws->inbuf, byte_count ); | 191 | array_catb(&cookie->request, ws->inbuf, (size_t)byte_count); | 
| 185 | return; | 192 | return; | 
| 186 | } | 193 | } | 
| 187 | 194 | ||
| 188 | array_catb( &cookie->request, ws->inbuf, byte_count ); | 195 | array_catb(&cookie->request, ws->inbuf, byte_count); | 
| 189 | if( array_failed( &cookie->request ) || array_bytes( &cookie->request ) > 8192 ) { | 196 | if (array_failed(&cookie->request) || array_bytes(&cookie->request) > 8192) { | 
| 190 | http_issue_error( sock, ws, CODE_HTTPERROR_500 ); | 197 | http_issue_error(sock, ws, CODE_HTTPERROR_500); | 
| 191 | return; | 198 | return; | 
| 192 | } | 199 | } | 
| 193 | 200 | ||
| 194 | while( ( ws->header_size = header_complete( array_start( &cookie->request ), array_bytes( &cookie->request ) ) ) ) { | 201 | while ((ws->header_size = header_complete(array_start(&cookie->request), array_bytes(&cookie->request)))) { | 
| 195 | ws->request = array_start( &cookie->request ); | 202 | ws->request = array_start(&cookie->request); | 
| 196 | ws->request_size = array_bytes( &cookie->request ); | 203 | ws->request_size = array_bytes(&cookie->request); | 
| 197 | http_handle_request( sock, ws ); | 204 | http_handle_request(sock, ws); | 
| 198 | #ifdef WANT_KEEPALIVE | 205 | #ifdef WANT_KEEPALIVE | 
| 199 | if( !ws->keep_alive ) | 206 | if (!ws->keep_alive) | 
| 200 | #endif | 207 | #endif | 
| 201 | return; | 208 | return; | 
| 202 | } | 209 | } | 
| 203 | } | 210 | } | 
| 204 | 211 | ||
| 205 | static void handle_write( const int64 sock ) { | 212 | static void handle_write(const int64 sock) { | 
| 206 | struct http_data* cookie=io_getcookie( sock ); | 213 | struct http_data *cookie = io_getcookie(sock); | 
| 207 | if( !cookie || ( iob_send( sock, &cookie->batch ) <= 0 ) ) | 214 | size_t i; | 
| 208 | handle_dead( sock ); | 215 | int chunked = 0; | 
| 216 | |||
| 217 | /* Look for the first io_batch still containing bytes to write */ | ||
| 218 | if (cookie) { | ||
| 219 | if (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) | ||
| 220 | chunked = 1; | ||
| 221 | |||
| 222 | for (i = 0; i < cookie->batches; ++i) { | ||
| 223 | if (cookie->batch[i].bytesleft) { | ||
| 224 | int64 res = iob_send(sock, cookie->batch + i); | ||
| 225 | |||
| 226 | if (res == -3) { | ||
| 227 | handle_dead(sock); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (!cookie->batch[i].bytesleft) | ||
| 232 | continue; | ||
| 233 | |||
| 234 | if (res == -1 || res > 0 || i < cookie->batches - 1) | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | /* In a chunked transfer after all batches accumulated have been sent, wait for the next one */ | ||
| 241 | if (chunked) | ||
| 242 | io_dontwantwrite(sock); | ||
| 243 | else | ||
| 244 | handle_dead(sock); | ||
| 209 | } | 245 | } | 
| 210 | 246 | ||
| 211 | static void handle_accept( const int64 serversocket ) { | 247 | static void handle_accept(const int64 serversocket) { | 
| 212 | struct http_data *cookie; | 248 | struct http_data *cookie; | 
| 213 | int64 sock; | 249 | int64 sock; | 
| 214 | ot_ip6 ip; | 250 | ot_ip6 ip; | 
| 215 | uint16 port; | 251 | uint16 port; | 
| 216 | tai6464 t; | 252 | tai6464 t; | 
| 217 | 253 | ||
| 218 | while( ( sock = socket_accept6( serversocket, ip, &port, NULL ) ) != -1 ) { | 254 | while ((sock = socket_accept6(serversocket, ip, &port, NULL)) != -1) { | 
| 219 | 255 | ||
| 220 | /* Put fd into a non-blocking mode */ | 256 | /* Put fd into a non-blocking mode */ | 
| 221 | io_nonblock( sock ); | 257 | io_nonblock(sock); | 
| 222 | 258 | ||
| 223 | if( !io_fd( sock ) || | 259 | if (!io_fd(sock) || !(cookie = (struct http_data *)malloc(sizeof(struct http_data)))) { | 
| 224 | !( cookie = (struct http_data*)malloc( sizeof(struct http_data) ) ) ) { | 260 | io_close(sock); | 
| 225 | io_close( sock ); | ||
| 226 | continue; | 261 | continue; | 
| 227 | } | 262 | } | 
| 228 | memset(cookie, 0, sizeof( struct http_data ) ); | 263 | memset(cookie, 0, sizeof(struct http_data)); | 
| 229 | memcpy(cookie->ip,ip,sizeof(ot_ip6)); | 264 | memcpy(cookie->ip, ip, sizeof(ot_ip6)); | 
| 230 | 265 | ||
| 231 | io_setcookie( sock, cookie ); | 266 | io_setcookie(sock, cookie); | 
| 232 | io_wantread( sock ); | 267 | io_wantread(sock); | 
| 233 | 268 | ||
| 234 | stats_issue_event( EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); | 269 | stats_issue_event(EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); | 
| 235 | 270 | ||
| 236 | /* That breaks taia encapsulation. But there is no way to take system | 271 | /* That breaks taia encapsulation. But there is no way to take system | 
| 237 | time this often in FreeBSD and libowfat does not allow to set unix time */ | 272 | time this often in FreeBSD and libowfat does not allow to set unix time */ | 
| 238 | taia_uint( &t, 0 ); /* Clear t */ | 273 | taia_uint(&t, 0); /* Clear t */ | 
| 239 | tai_unix( &(t.sec), (g_now_seconds + OT_CLIENT_TIMEOUT) ); | 274 | tai_unix(&(t.sec), (g_now_seconds + OT_CLIENT_TIMEOUT)); | 
| 240 | io_timeout( sock, t ); | 275 | io_timeout(sock, t); | 
| 241 | } | 276 | } | 
| 242 | io_eagain(serversocket); | 277 | io_eagain(serversocket); | 
| 243 | } | 278 | } | 
| 244 | 279 | ||
| 245 | static void * server_mainloop( void * args ) { | 280 | static void *server_mainloop(void *args) { | 
| 246 | struct ot_workstruct ws; | 281 | struct ot_workstruct ws; | 
| 247 | time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 282 | time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 
| 248 | struct iovec *iovector; | 283 | struct iovec *iovector; | 
| 249 | int iovec_entries; | 284 | int iovec_entries, is_partial; | 
| 250 | 285 | ||
| 251 | (void)args; | 286 | (void)args; | 
| 252 | 287 | ||
| 253 | /* Initialize our "thread local storage" */ | 288 | /* Initialize our "thread local storage" */ | 
| 254 | ws.inbuf = malloc( G_INBUF_SIZE ); | 289 | ws.inbuf = malloc(G_INBUF_SIZE); | 
| 255 | ws.outbuf = malloc( G_OUTBUF_SIZE ); | 290 | ws.outbuf = malloc(G_OUTBUF_SIZE); | 
| 256 | #ifdef _DEBUG_HTTPERROR | 291 | #ifdef _DEBUG_HTTPERROR | 
| 257 | ws.debugbuf= malloc( G_DEBUGBUF_SIZE ); | 292 | ws.debugbuf = malloc(G_DEBUGBUF_SIZE); | 
| 258 | #endif | 293 | #endif | 
| 259 | if( !ws.inbuf || !ws.outbuf ) | ||
| 260 | panic( "Initializing worker failed" ); | ||
| 261 | 294 | ||
| 262 | for( ; ; ) { | 295 | if (!ws.inbuf || !ws.outbuf) | 
| 296 | panic("Initializing worker failed"); | ||
| 297 | |||
| 298 | #ifdef WANT_ARC4RANDOM | ||
| 299 | arc4random_buf(&ws.rand48_state[0], 3 * sizeof(uint16_t)); | ||
| 300 | #else | ||
| 301 | ws.rand48_state[0] = (uint16_t)random(); | ||
| 302 | ws.rand48_state[1] = (uint16_t)random(); | ||
| 303 | ws.rand48_state[2] = (uint16_t)random(); | ||
| 304 | #endif | ||
| 305 | |||
| 306 | for (;;) { | ||
| 263 | int64 sock; | 307 | int64 sock; | 
| 264 | 308 | ||
| 265 | io_wait(); | 309 | io_wait(); | 
| 266 | 310 | ||
| 267 | while( ( sock = io_canread( ) ) != -1 ) { | 311 | while ((sock = io_canread()) != -1) { | 
| 268 | const void *cookie = io_getcookie( sock ); | 312 | const void *cookie = io_getcookie(sock); | 
| 269 | if( (intptr_t)cookie == FLAG_TCP ) | 313 | if ((intptr_t)cookie == FLAG_TCP) | 
| 270 | handle_accept( sock ); | 314 | handle_accept(sock); | 
| 271 | else if( (intptr_t)cookie == FLAG_UDP ) | 315 | else if ((intptr_t)cookie == FLAG_UDP) | 
| 272 | handle_udp6( sock, &ws ); | 316 | handle_udp6(sock, &ws); | 
| 273 | else if( (intptr_t)cookie == FLAG_SELFPIPE ) { | 317 | else if ((intptr_t)cookie == FLAG_SELFPIPE) | 
| 274 | io_tryread( sock, ws.inbuf, G_INBUF_SIZE ); | 318 | io_tryread(sock, ws.inbuf, G_INBUF_SIZE); | 
| 275 | fprintf(stderr, "pipe\n"); | 319 | else | 
| 276 | } else | 320 | handle_read(sock, &ws); | 
| 277 | handle_read( sock, &ws ); | ||
| 278 | } | 321 | } | 
| 279 | 322 | ||
| 280 | while( ( sock = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 ) | 323 | while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) | 
| 281 | http_sendiovecdata( sock, &ws, iovec_entries, iovector ); | 324 | http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); | 
| 282 | 325 | ||
| 283 | while( ( sock = io_canwrite( ) ) != -1 ) | 326 | while ((sock = io_canwrite()) != -1) | 
| 284 | handle_write( sock ); | 327 | handle_write(sock); | 
| 285 | 328 | ||
| 286 | if( g_now_seconds > next_timeout_check ) { | 329 | if (g_now_seconds > next_timeout_check) { | 
| 287 | while( ( sock = io_timeouted() ) != -1 ) | 330 | while ((sock = io_timeouted()) != -1) | 
| 288 | handle_dead( sock ); | 331 | handle_dead(sock); | 
| 289 | next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 332 | next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 
| 290 | } | 333 | } | 
| 291 | 334 | ||
| 292 | livesync_ticker(); | 335 | livesync_ticker(); | 
| 293 | |||
| 294 | /* Enforce setting the clock */ | ||
| 295 | signal_handler( SIGALRM ); | ||
| 296 | } | 336 | } | 
| 297 | return 0; | 337 | return 0; | 
| 298 | } | 338 | } | 
| 299 | 339 | ||
| 300 | static int64_t ot_try_bind( ot_ip6 ip, uint16_t port, PROTO_FLAG proto ) { | 340 | static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) { | 
| 301 | int64 sock = proto == FLAG_TCP ? socket_tcp6( ) : socket_udp6( ); | 341 | int64 sock = proto == FLAG_TCP ? socket_tcp6() : socket_udp6(); | 
| 302 | |||
| 303 | #ifndef WANT_V6 | ||
| 304 | if( !ip6_isv4mapped(ip) ) { | ||
| 305 | exerr( "V4 Tracker is V4 only!" ); | ||
| 306 | } | ||
| 307 | #else | ||
| 308 | if( ip6_isv4mapped(ip) ) { | ||
| 309 | exerr( "V6 Tracker is V6 only!" ); | ||
| 310 | } | ||
| 311 | #endif | ||
| 312 | 342 | ||
| 313 | #ifdef _DEBUG | 343 | #ifdef _DEBUG | 
| 314 | { | 344 | { | 
| 315 | char *protos[] = {"TCP","UDP","UDP mcast"}; | 345 | char *protos[] = {"TCP", "UDP", "UDP mcast"}; | 
| 316 | char _debug[512]; | 346 | char _debug[512]; | 
| 317 | int off = snprintf( _debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto] ); | 347 | int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]); | 
| 318 | off += fmt_ip6c( _debug+off, ip); | 348 | off += fmt_ip6c(_debug + off, ip); | 
| 319 | snprintf( _debug + off, sizeof(_debug)-off, "]:%d...", port); | 349 | snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port); | 
| 320 | fputs( _debug, stderr ); | 350 | fputs(_debug, stderr); | 
| 321 | } | 351 | } | 
| 322 | #endif | 352 | #endif | 
| 323 | 353 | ||
| 324 | if( socket_bind6_reuse( sock, ip, port, 0 ) == -1 ) | 354 | if (socket_bind6_reuse(sock, ip, port, 0) == -1) | 
| 325 | panic( "socket_bind6_reuse" ); | 355 | panic("socket_bind6_reuse"); | 
| 326 | 356 | ||
| 327 | if( ( proto == FLAG_TCP ) && ( socket_listen( sock, SOMAXCONN) == -1 ) ) | 357 | if ((proto == FLAG_TCP) && (socket_listen(sock, SOMAXCONN) == -1)) | 
| 328 | panic( "socket_listen" ); | 358 | panic("socket_listen"); | 
| 329 | 359 | ||
| 330 | if( !io_fd( sock ) ) | 360 | if (!io_fd(sock)) | 
| 331 | panic( "io_fd" ); | 361 | panic("io_fd"); | 
| 332 | 362 | ||
| 333 | io_setcookie( sock, (void*)proto ); | 363 | io_setcookie(sock, (void *)proto); | 
| 334 | 364 | ||
| 335 | if( (proto == FLAG_UDP) && g_udp_workers ) { | 365 | if ((proto == FLAG_UDP) && g_udp_workers) { | 
| 336 | io_block( sock ); | 366 | io_block(sock); | 
| 337 | udp_init( sock, g_udp_workers ); | 367 | udp_init(sock, g_udp_workers); | 
| 338 | } else | 368 | } else | 
| 339 | io_wantread( sock ); | 369 | io_wantread(sock); | 
| 340 | 370 | ||
| 341 | #ifdef _DEBUG | 371 | #ifdef _DEBUG | 
| 342 | fputs( " success.\n", stderr); | 372 | fputs(" success.\n", stderr); | 
| 343 | #endif | 373 | #endif | 
| 344 | 374 | ||
| 345 | return sock; | 375 | return sock; | 
| 346 | } | 376 | } | 
| 347 | 377 | ||
| 348 | char * set_config_option( char **option, char *value ) { | 378 | char *set_config_option(char **option, char *value) { | 
| 349 | #ifdef _DEBUG | 379 | #ifdef _DEBUG | 
| 350 | fprintf( stderr, "Setting config option: %s\n", value ); | 380 | fprintf(stderr, "Setting config option: %s\n", value); | 
| 351 | #endif | 381 | #endif | 
| 352 | while( isspace(*value) ) ++value; | 382 | while (isspace(*value)) | 
| 353 | free( *option ); | 383 | ++value; | 
| 354 | return *option = strdup( value ); | 384 | free(*option); | 
| 385 | return *option = strdup(value); | ||
| 355 | } | 386 | } | 
| 356 | 387 | ||
| 357 | static int scan_ip6_port( const char *src, ot_ip6 ip, uint16 *port ) { | 388 | static int scan_ip6_port(const char *src, ot_ip6 ip, uint16 *port) { | 
| 358 | const char *s = src; | 389 | const char *s = src; | 
| 359 | int off, bracket = 0; | 390 | int off, bracket = 0; | 
| 360 | while( isspace(*s) ) ++s; | 391 | while (isspace(*s)) | 
| 361 | if( *s == '[' ) ++s, ++bracket; /* for v6 style notation */ | 392 | ++s; | 
| 362 | if( !(off = scan_ip6( s, ip ) ) ) | 393 | if (*s == '[') | 
| 394 | ++s, ++bracket; /* for v6 style notation */ | ||
| 395 | if (!(off = scan_ip6(s, ip))) | ||
| 363 | return 0; | 396 | return 0; | 
| 364 | s += off; | 397 | s += off; | 
| 365 | if( bracket && *s == ']' ) ++s; | 398 | if (bracket && *s == ']') | 
| 366 | if( *s == 0 || isspace(*s)) return s-src; | 399 | ++s; | 
| 367 | if( !ip6_isv4mapped(ip)){ | 400 | if (*s == 0 || isspace(*s)) | 
| 368 | if( *s != ':' && *s != '.' ) return 0; | 401 | return s - src; | 
| 369 | if( !bracket && *(s) == ':' ) return 0; | 402 | if (!ip6_isv4mapped(ip)) { | 
| 403 | if (*s != ':' && *s != '.') | ||
| 404 | return 0; | ||
| 405 | if (!bracket && *(s) == ':') | ||
| 406 | return 0; | ||
| 370 | s++; | 407 | s++; | 
| 371 | } else { | 408 | } else { | 
| 372 | if( *(s++) != ':' ) return 0; | 409 | if (*(s++) != ':') | 
| 410 | return 0; | ||
| 411 | } | ||
| 412 | if (!(off = scan_ushort(s, port))) | ||
| 413 | return 0; | ||
| 414 | return off + s - src; | ||
| 415 | } | ||
| 416 | |||
| 417 | static int scan_ip6_net(const char *src, ot_net *net) { | ||
| 418 | const char *s = src; | ||
| 419 | int off; | ||
| 420 | while (isspace(*s)) | ||
| 421 | ++s; | ||
| 422 | if (!(off = scan_ip6(s, net->address))) | ||
| 423 | return 0; | ||
| 424 | s += off; | ||
| 425 | if (*s != '/') | ||
| 426 | net->bits = 128; | ||
| 427 | else { | ||
| 428 | s++; | ||
| 429 | if (!(off = scan_int(s, &net->bits))) | ||
| 430 | return 0; | ||
| 431 | if (ip6_isv4mapped(net->address)) | ||
| 432 | net->bits += 96; | ||
| 433 | if (net->bits > 128) | ||
| 434 | return 0; | ||
| 435 | s += off; | ||
| 373 | } | 436 | } | 
| 374 | if( !(off = scan_ushort (s, port ) ) ) | 437 | return off + s - src; | 
| 375 | return 0; | ||
| 376 | return off+s-src; | ||
| 377 | } | 438 | } | 
| 378 | 439 | ||
| 379 | int parse_configfile( char * config_filename ) { | 440 | int parse_configfile(char *config_filename) { | 
| 380 | FILE * accesslist_filehandle; | 441 | FILE *accesslist_filehandle; | 
| 381 | char inbuf[512]; | 442 | char inbuf[512]; | 
| 382 | ot_ip6 tmpip; | 443 | ot_ip6 tmpip; | 
| 383 | int bound = 0; | 444 | #if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE) | 
| 445 | ot_net tmpnet; | ||
| 446 | #endif | ||
| 447 | int bound = 0; | ||
| 384 | 448 | ||
| 385 | accesslist_filehandle = fopen( config_filename, "r" ); | 449 | accesslist_filehandle = fopen(config_filename, "r"); | 
| 386 | 450 | ||
| 387 | if( accesslist_filehandle == NULL ) { | 451 | if (accesslist_filehandle == NULL) { | 
| 388 | fprintf( stderr, "Warning: Can't open config file: %s.", config_filename ); | 452 | fprintf(stderr, "Warning: Can't open config file: %s.", config_filename); | 
| 389 | return 0; | 453 | return 0; | 
| 390 | } | 454 | } | 
| 391 | 455 | ||
| 392 | while( fgets( inbuf, sizeof(inbuf), accesslist_filehandle ) ) { | 456 | while (fgets(inbuf, sizeof(inbuf), accesslist_filehandle)) { | 
| 393 | char *p = inbuf; | 457 | char *p = inbuf; | 
| 394 | size_t strl; | 458 | size_t strl; | 
| 395 | 459 | ||
| 396 | /* Skip white spaces */ | 460 | /* Skip white spaces */ | 
| 397 | while(isspace(*p)) ++p; | 461 | while (isspace(*p)) | 
| 462 | ++p; | ||
| 398 | 463 | ||
| 399 | /* Ignore comments and empty lines */ | 464 | /* Ignore comments and empty lines */ | 
| 400 | if((*p=='#')||(*p=='\n')||(*p==0)) continue; | 465 | if ((*p == '#') || (*p == '\n') || (*p == 0)) | 
| 466 | continue; | ||
| 401 | 467 | ||
| 402 | /* consume trailing new lines and spaces */ | 468 | /* consume trailing new lines and spaces */ | 
| 403 | strl = strlen(p); | 469 | strl = strlen(p); | 
| 404 | while( strl && isspace(p[strl-1])) | 470 | while (strl && isspace(p[strl - 1])) | 
| 405 | p[--strl] = 0; | 471 | p[--strl] = 0; | 
| 406 | 472 | ||
| 407 | /* Scan for commands */ | 473 | /* Scan for commands */ | 
| 408 | if(!byte_diff(p,15,"tracker.rootdir" ) && isspace(p[15])) { | 474 | if (!byte_diff(p, 15, "tracker.rootdir") && isspace(p[15])) { | 
| 409 | set_config_option( &g_serverdir, p+16 ); | 475 | set_config_option(&g_serverdir, p + 16); | 
| 410 | } else if(!byte_diff(p,12,"tracker.user" ) && isspace(p[12])) { | 476 | } else if (!byte_diff(p, 12, "tracker.user") && isspace(p[12])) { | 
| 411 | set_config_option( &g_serveruser, p+13 ); | 477 | set_config_option(&g_serveruser, p + 13); | 
| 412 | } else if(!byte_diff(p,14,"listen.tcp_udp" ) && isspace(p[14])) { | 478 | } else if (!byte_diff(p, 14, "listen.tcp_udp") && isspace(p[14])) { | 
| 413 | uint16_t tmpport = 6969; | 479 | uint16_t tmpport = 6969; | 
| 414 | if( !scan_ip6_port( p+15, tmpip, &tmpport )) goto parse_error; | 480 | if (!scan_ip6_port(p + 15, tmpip, &tmpport)) | 
| 415 | ot_try_bind( tmpip, tmpport, FLAG_TCP ); ++bound; | 481 | goto parse_error; | 
| 416 | ot_try_bind( tmpip, tmpport, FLAG_UDP ); ++bound; | 482 | ot_try_bind(tmpip, tmpport, FLAG_TCP); | 
| 417 | } else if(!byte_diff(p,10,"listen.tcp" ) && isspace(p[10])) { | 483 | ++bound; | 
| 484 | ot_try_bind(tmpip, tmpport, FLAG_UDP); | ||
| 485 | ++bound; | ||
| 486 | } else if (!byte_diff(p, 10, "listen.tcp") && isspace(p[10])) { | ||
| 418 | uint16_t tmpport = 6969; | 487 | uint16_t tmpport = 6969; | 
| 419 | if( !scan_ip6_port( p+11, tmpip, &tmpport )) goto parse_error; | 488 | if (!scan_ip6_port(p + 11, tmpip, &tmpport)) | 
| 420 | ot_try_bind( tmpip, tmpport, FLAG_TCP ); | 489 | goto parse_error; | 
| 490 | ot_try_bind(tmpip, tmpport, FLAG_TCP); | ||
| 421 | ++bound; | 491 | ++bound; | 
| 422 | } else if(!byte_diff(p, 10, "listen.udp" ) && isspace(p[10])) { | 492 | } else if (!byte_diff(p, 10, "listen.udp") && isspace(p[10])) { | 
| 423 | uint16_t tmpport = 6969; | 493 | uint16_t tmpport = 6969; | 
| 424 | if( !scan_ip6_port( p+11, tmpip, &tmpport )) goto parse_error; | 494 | if (!scan_ip6_port(p + 11, tmpip, &tmpport)) | 
| 425 | ot_try_bind( tmpip, tmpport, FLAG_UDP ); | 495 | goto parse_error; | 
| 496 | ot_try_bind(tmpip, tmpport, FLAG_UDP); | ||
| 426 | ++bound; | 497 | ++bound; | 
| 427 | } else if(!byte_diff(p,18,"listen.udp.workers" ) && isspace(p[18])) { | 498 | } else if (!byte_diff(p, 18, "listen.udp.workers") && isspace(p[18])) { | 
| 428 | char *value = p + 18; | 499 | char *value = p + 18; | 
| 429 | while( isspace(*value) ) ++value; | 500 | while (isspace(*value)) | 
| 430 | scan_uint( value, &g_udp_workers ); | 501 | ++value; | 
| 502 | scan_uint(value, &g_udp_workers); | ||
| 431 | #ifdef WANT_ACCESSLIST_WHITE | 503 | #ifdef WANT_ACCESSLIST_WHITE | 
| 432 | } else if(!byte_diff(p, 16, "access.whitelist" ) && isspace(p[16])) { | 504 | } else if (!byte_diff(p, 16, "access.whitelist") && isspace(p[16])) { | 
| 433 | set_config_option( &g_accesslist_filename, p+17 ); | 505 | set_config_option(&g_accesslist_filename, p + 17); | 
| 434 | #elif defined( WANT_ACCESSLIST_BLACK ) | 506 | #elif defined(WANT_ACCESSLIST_BLACK) | 
| 435 | } else if(!byte_diff(p, 16, "access.blacklist" ) && isspace(p[16])) { | 507 | } else if (!byte_diff(p, 16, "access.blacklist") && isspace(p[16])) { | 
| 436 | set_config_option( &g_accesslist_filename, p+17 ); | 508 | set_config_option(&g_accesslist_filename, p + 17); | 
| 509 | #endif | ||
| 510 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 511 | } else if (!byte_diff(p, 15, "access.fifo_add") && isspace(p[15])) { | ||
| 512 | set_config_option(&g_accesslist_pipe_add, p + 16); | ||
| 513 | } else if (!byte_diff(p, 18, "access.fifo_delete") && isspace(p[18])) { | ||
| 514 | set_config_option(&g_accesslist_pipe_delete, p + 19); | ||
| 437 | #endif | 515 | #endif | 
| 438 | #ifdef WANT_RESTRICT_STATS | 516 | #ifdef WANT_RESTRICT_STATS | 
| 439 | } else if(!byte_diff(p, 12, "access.stats" ) && isspace(p[12])) { | 517 | } else if (!byte_diff(p, 12, "access.stats") && isspace(p[12])) { | 
| 440 | if( !scan_ip6( p+13, tmpip )) goto parse_error; | 518 | if (!scan_ip6_net(p + 13, &tmpnet)) | 
| 441 | accesslist_blessip( tmpip, OT_PERMISSION_MAY_STAT ); | 519 | goto parse_error; | 
| 520 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_STAT); | ||
| 442 | #endif | 521 | #endif | 
| 443 | } else if(!byte_diff(p, 17, "access.stats_path" ) && isspace(p[17])) { | 522 | } else if (!byte_diff(p, 17, "access.stats_path") && isspace(p[17])) { | 
| 444 | set_config_option( &g_stats_path, p+18 ); | 523 | set_config_option(&g_stats_path, p + 18); | 
| 445 | #ifdef WANT_IP_FROM_PROXY | 524 | #ifdef WANT_IP_FROM_PROXY | 
| 446 | } else if(!byte_diff(p, 12, "access.proxy" ) && isspace(p[12])) { | 525 | } else if (!byte_diff(p, 12, "access.proxy") && isspace(p[12])) { | 
| 447 | if( !scan_ip6( p+13, tmpip )) goto parse_error; | 526 | if (!scan_ip6_net(p + 13, &tmpnet)) | 
| 448 | accesslist_blessip( tmpip, OT_PERMISSION_MAY_PROXY ); | 527 | goto parse_error; | 
| 528 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_PROXY); | ||
| 449 | #endif | 529 | #endif | 
| 450 | } else if(!byte_diff(p, 20, "tracker.redirect_url" ) && isspace(p[20])) { | 530 | } else if (!byte_diff(p, 20, "tracker.redirect_url") && isspace(p[20])) { | 
| 451 | set_config_option( &g_redirecturl, p+21 ); | 531 | set_config_option(&g_redirecturl, p + 21); | 
| 452 | #ifdef WANT_SYNC_LIVE | 532 | #ifdef WANT_SYNC_LIVE | 
| 453 | } else if(!byte_diff(p, 24, "livesync.cluster.node_ip" ) && isspace(p[24])) { | 533 | } else if (!byte_diff(p, 24, "livesync.cluster.node_ip") && isspace(p[24])) { | 
| 454 | if( !scan_ip6( p+25, tmpip )) goto parse_error; | 534 | if (!scan_ip6_net(p + 25, &tmpnet)) | 
| 455 | accesslist_blessip( tmpip, OT_PERMISSION_MAY_LIVESYNC ); | 535 | goto parse_error; | 
| 456 | } else if(!byte_diff(p, 23, "livesync.cluster.listen" ) && isspace(p[23])) { | 536 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_LIVESYNC); | 
| 537 | } else if (!byte_diff(p, 23, "livesync.cluster.listen") && isspace(p[23])) { | ||
| 457 | uint16_t tmpport = LIVESYNC_PORT; | 538 | uint16_t tmpport = LIVESYNC_PORT; | 
| 458 | if( !scan_ip6_port( p+24, tmpip, &tmpport )) goto parse_error; | 539 | if (!scan_ip6_port(p + 24, tmpip, &tmpport)) | 
| 459 | livesync_bind_mcast( tmpip, tmpport ); | 540 | goto parse_error; | 
| 541 | livesync_bind_mcast(tmpip, tmpport); | ||
| 460 | #endif | 542 | #endif | 
| 461 | } else | 543 | } else | 
| 462 | fprintf( stderr, "Unhandled line in config file: %s\n", inbuf ); | 544 | fprintf(stderr, "Unhandled line in config file: %s\n", inbuf); | 
| 463 | continue; | 545 | continue; | 
| 464 | parse_error: | 546 | parse_error: | 
| 465 | fprintf( stderr, "Parse error in config file: %s\n", inbuf); | 547 | fprintf(stderr, "Parse error in config file: %s\n", inbuf); | 
| 466 | } | 548 | } | 
| 467 | fclose( accesslist_filehandle ); | 549 | fclose(accesslist_filehandle); | 
| 468 | return bound; | 550 | return bound; | 
| 469 | } | 551 | } | 
| 470 | 552 | ||
| 471 | void load_state(const char * const state_filename ) { | 553 | void load_state(const char *const state_filename) { | 
| 472 | FILE * state_filehandle; | 554 | FILE *state_filehandle; | 
| 473 | char inbuf[512]; | 555 | char inbuf[512]; | 
| 474 | ot_hash infohash; | 556 | ot_hash infohash; | 
| 475 | unsigned long long base, downcount; | 557 | unsigned long long base, downcount; | 
| 476 | int consumed; | 558 | int consumed; | 
| 477 | 559 | ||
| 478 | state_filehandle = fopen( state_filename, "r" ); | 560 | state_filehandle = fopen(state_filename, "r"); | 
| 479 | 561 | ||
| 480 | if( state_filehandle == NULL ) { | 562 | if (state_filehandle == NULL) { | 
| 481 | fprintf( stderr, "Warning: Can't open config file: %s.", state_filename ); | 563 | fprintf(stderr, "Warning: Can't open config file: %s.", state_filename); | 
| 482 | return; | 564 | return; | 
| 483 | } | 565 | } | 
| 484 | 566 | ||
| 485 | /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */ | 567 | /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */ | 
| 486 | while( fgets( inbuf, sizeof(inbuf), state_filehandle ) ) { | 568 | while (fgets(inbuf, sizeof(inbuf), state_filehandle)) { | 
| 487 | int i; | 569 | int i; | 
| 488 | for( i=0; i<(int)sizeof(ot_hash); ++i ) { | 570 | for (i = 0; i < (int)sizeof(ot_hash); ++i) { | 
| 489 | int eger = 16 * scan_fromhex( inbuf[ 2*i ] ) + scan_fromhex( inbuf[ 1 + 2*i ] ); | 571 | int eger = 16 * scan_fromhex(inbuf[2 * i]) + scan_fromhex(inbuf[1 + 2 * i]); | 
| 490 | if( eger < 0 ) | 572 | if (eger < 0) | 
| 491 | continue; | 573 | continue; | 
| 492 | infohash[i] = eger; | 574 | infohash[i] = eger; | 
| 493 | } | 575 | } | 
| 494 | 576 | ||
| 495 | if( i != (int)sizeof(ot_hash) ) continue; | 577 | if (i != (int)sizeof(ot_hash)) | 
| 578 | continue; | ||
| 496 | i *= 2; | 579 | i *= 2; | 
| 497 | 580 | ||
| 498 | if( inbuf[ i++ ] != ':' || !( consumed = scan_ulonglong( inbuf+i, &base ) ) ) continue; | 581 | if (inbuf[i++] != ':' || !(consumed = scan_ulonglong(inbuf + i, &base))) | 
| 582 | continue; | ||
| 499 | i += consumed; | 583 | i += consumed; | 
| 500 | if( inbuf[ i++ ] != ':' || !( consumed = scan_ulonglong( inbuf+i, &downcount ) ) ) continue; | 584 | if (inbuf[i++] != ':' || !(consumed = scan_ulonglong(inbuf + i, &downcount))) | 
| 501 | add_torrent_from_saved_state( infohash, base, downcount ); | 585 | continue; | 
| 586 | add_torrent_from_saved_state(infohash, base, downcount); | ||
| 502 | } | 587 | } | 
| 503 | 588 | ||
| 504 | fclose( state_filehandle ); | 589 | fclose(state_filehandle); | 
| 505 | } | 590 | } | 
| 506 | 591 | ||
| 507 | int drop_privileges ( const char * const serveruser, const char * const serverdir ) { | 592 | int drop_privileges(const char *const serveruser, const char *const serverdir) { | 
| 508 | struct passwd *pws = NULL; | 593 | struct passwd *pws = NULL; | 
| 509 | 594 | ||
| 510 | #ifdef _DEBUG | 595 | #ifdef _DEBUG | 
| 511 | if( !geteuid() ) | 596 | if (!geteuid()) | 
| 512 | fprintf( stderr, "Dropping to user %s.\n", serveruser ); | 597 | fprintf(stderr, "Dropping to user %s.\n", serveruser); | 
| 513 | if( serverdir ) | 598 | if (serverdir) | 
| 514 | fprintf( stderr, "ch%s'ing to directory %s.\n", geteuid() ? "dir" : "root", serverdir ); | 599 | fprintf(stderr, "ch%s'ing to directory %s.\n", geteuid() ? "dir" : "root", serverdir); | 
| 515 | #endif | 600 | #endif | 
| 516 | 601 | ||
| 517 | /* Grab pws entry before chrooting */ | 602 | /* Grab pws entry before chrooting */ | 
| 518 | pws = getpwnam( serveruser ); | 603 | pws = getpwnam(serveruser); | 
| 519 | endpwent(); | 604 | endpwent(); | 
| 520 | 605 | ||
| 521 | if( geteuid() == 0 ) { | 606 | if (geteuid() == 0) { | 
| 522 | /* Running as root: chroot and drop privileges */ | 607 | /* Running as root: chroot and drop privileges */ | 
| 523 | if( serverdir && chroot( serverdir ) ) { | 608 | if (serverdir && chroot(serverdir)) { | 
| 524 | fprintf( stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno) ); | 609 | fprintf(stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno)); | 
| 525 | return -1; | 610 | return -1; | 
| 526 | } | 611 | } | 
| 527 | 612 | ||
| 528 | if(chdir("/")) | 613 | if (chdir("/")) | 
| 529 | panic("chdir() failed after chrooting: "); | 614 | panic("chdir() failed after chrooting: "); | 
| 530 | 615 | ||
| 531 | /* If we can't find server user, revert to nobody's default uid */ | 616 | /* If we can't find server user, revert to nobody's default uid */ | 
| 532 | if( !pws ) { | 617 | if (!pws) { | 
| 533 | fprintf( stderr, "Warning: Could not get password entry for %s. Reverting to uid -2.\n", serveruser ); | 618 | fprintf(stderr, "Warning: Could not get password entry for %s. Reverting to uid -2.\n", serveruser); | 
| 534 | setegid( (gid_t)-2 ); setgid( (gid_t)-2 ); | 619 | if (setegid((gid_t)-2) || setgid((gid_t)-2) || setuid((uid_t)-2) || seteuid((uid_t)-2)) | 
| 535 | setuid( (uid_t)-2 ); seteuid( (uid_t)-2 ); | 620 | panic("Could not set uid to value -2"); | 
| 536 | } | 621 | } else { | 
| 537 | else { | 622 | if (setegid(pws->pw_gid) || setgid(pws->pw_gid) || setuid(pws->pw_uid) || seteuid(pws->pw_uid)) | 
| 538 | setegid( pws->pw_gid ); setgid( pws->pw_gid ); | 623 | panic("Could not set uid to specified value"); | 
| 539 | setuid( pws->pw_uid ); seteuid( pws->pw_uid ); | ||
| 540 | } | 624 | } | 
| 541 | 625 | ||
| 542 | if( geteuid() == 0 || getegid() == 0 ) | 626 | if (geteuid() == 0 || getegid() == 0) | 
| 543 | panic("Still running with root privileges?!"); | 627 | panic("Still running with root privileges?!"); | 
| 544 | } | 628 | } else { | 
| 545 | else { | ||
| 546 | /* Normal user, just chdir() */ | 629 | /* Normal user, just chdir() */ | 
| 547 | if( serverdir && chdir( serverdir ) ) { | 630 | if (serverdir && chdir(serverdir)) { | 
| 548 | fprintf( stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno) ); | 631 | fprintf(stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno)); | 
| 549 | return -1; | 632 | return -1; | 
| 550 | } | 633 | } | 
| 551 | } | 634 | } | 
| @@ -553,118 +636,173 @@ int drop_privileges ( const char * const serveruser, const char * const serverdi | |||
| 553 | return 0; | 636 | return 0; | 
| 554 | } | 637 | } | 
| 555 | 638 | ||
| 556 | int main( int argc, char **argv ) { | 639 | /* Maintain our copy of the clock. time() on BSDs is very expensive. */ | 
| 557 | ot_ip6 serverip, tmpip; | 640 | static void *time_caching_worker(void *args) { | 
| 558 | int bound = 0, scanon = 1; | 641 | (void)args; | 
| 559 | uint16_t tmpport; | 642 | while (1) { | 
| 560 | char * statefile = 0; | 643 | g_now_seconds = time(NULL); | 
| 644 | sleep(5); | ||
| 645 | } | ||
| 646 | return NULL; | ||
| 647 | } | ||
| 561 | 648 | ||
| 562 | memset( serverip, 0, sizeof(ot_ip6) ); | 649 | int main(int argc, char **argv) { | 
| 563 | #ifndef WANT_V6 | 650 | ot_ip6 serverip; | 
| 564 | serverip[10]=serverip[11]=-1; | 651 | ot_net tmpnet; | 
| 565 | noipv6=1; | 652 | int bound = 0, scanon = 1; | 
| 653 | uint16_t tmpport; | ||
| 654 | char *statefile = 0; | ||
| 655 | pthread_t thread_id; /* time cacher */ | ||
| 656 | |||
| 657 | memset(serverip, 0, sizeof(ot_ip6)); | ||
| 658 | #ifdef WANT_V4_ONLY | ||
| 659 | serverip[10] = serverip[11] = -1; | ||
| 566 | #endif | 660 | #endif | 
| 567 | 661 | ||
| 568 | #ifdef WANT_DEV_RANDOM | 662 | #ifdef WANT_DEV_RANDOM | 
| 569 | srandomdev(); | 663 | srandomdev(); | 
| 570 | #else | 664 | #else | 
| 571 | srandom( time(NULL) ); | 665 | srandom(time(NULL)); | 
| 572 | #endif | 666 | #endif | 
| 573 | 667 | ||
| 574 | while( scanon ) { | 668 | while (scanon) { | 
| 575 | switch( getopt( argc, argv, ":i:p:A:P:d:u:r:s:f:l:v" | 669 | switch (getopt(argc, argv, | 
| 670 | ":i:p:A:P:d:u:r:s:f:l:v" | ||
| 576 | #ifdef WANT_ACCESSLIST_BLACK | 671 | #ifdef WANT_ACCESSLIST_BLACK | 
| 577 | "b:" | 672 | "b:" | 
| 578 | #elif defined( WANT_ACCESSLIST_WHITE ) | 673 | #elif defined(WANT_ACCESSLIST_WHITE) | 
| 579 | "w:" | 674 | "w:" | 
| 580 | #endif | 675 | #endif | 
| 581 | "h" ) ) { | 676 | "h")) { | 
| 582 | case -1 : scanon = 0; break; | 677 | case -1: | 
| 583 | case 'i': | 678 | scanon = 0; | 
| 584 | if( !scan_ip6( optarg, serverip )) { usage( argv[0] ); exit( 1 ); } | 679 | break; | 
| 585 | break; | 680 | case 'i': | 
| 681 | if (!scan_ip6(optarg, serverip)) { | ||
| 682 | usage(argv[0]); | ||
| 683 | exit(1); | ||
| 684 | } | ||
| 685 | break; | ||
| 586 | #ifdef WANT_ACCESSLIST_BLACK | 686 | #ifdef WANT_ACCESSLIST_BLACK | 
| 587 | case 'b': set_config_option( &g_accesslist_filename, optarg); break; | 687 | case 'b': | 
| 588 | #elif defined( WANT_ACCESSLIST_WHITE ) | 688 | set_config_option(&g_accesslist_filename, optarg); | 
| 589 | case 'w': set_config_option( &g_accesslist_filename, optarg); break; | 689 | break; | 
| 690 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 691 | case 'w': | ||
| 692 | set_config_option(&g_accesslist_filename, optarg); | ||
| 693 | break; | ||
| 590 | #endif | 694 | #endif | 
| 591 | case 'p': | 695 | case 'p': | 
| 592 | if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); } | 696 | if (!scan_ushort(optarg, &tmpport)) { | 
| 593 | ot_try_bind( serverip, tmpport, FLAG_TCP ); bound++; break; | 697 | usage(argv[0]); | 
| 594 | case 'P': | 698 | exit(1); | 
| 595 | if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); } | 699 | } | 
| 596 | ot_try_bind( serverip, tmpport, FLAG_UDP ); bound++; break; | 700 | ot_try_bind(serverip, tmpport, FLAG_TCP); | 
| 701 | bound++; | ||
| 702 | break; | ||
| 703 | case 'P': | ||
| 704 | if (!scan_ushort(optarg, &tmpport)) { | ||
| 705 | usage(argv[0]); | ||
| 706 | exit(1); | ||
| 707 | } | ||
| 708 | ot_try_bind(serverip, tmpport, FLAG_UDP); | ||
| 709 | bound++; | ||
| 710 | break; | ||
| 597 | #ifdef WANT_SYNC_LIVE | 711 | #ifdef WANT_SYNC_LIVE | 
| 598 | case 's': | 712 | case 's': | 
| 599 | if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); } | 713 | if (!scan_ushort(optarg, &tmpport)) { | 
| 600 | livesync_bind_mcast( serverip, tmpport); break; | 714 | usage(argv[0]); | 
| 715 | exit(1); | ||
| 716 | } | ||
| 717 | livesync_bind_mcast(serverip, tmpport); | ||
| 718 | break; | ||
| 601 | #endif | 719 | #endif | 
| 602 | case 'd': set_config_option( &g_serverdir, optarg ); break; | 720 | case 'd': | 
| 603 | case 'u': set_config_option( &g_serveruser, optarg ); break; | 721 | set_config_option(&g_serverdir, optarg); | 
| 604 | case 'r': set_config_option( &g_redirecturl, optarg ); break; | 722 | break; | 
| 605 | case 'l': statefile = optarg; break; | 723 | case 'u': | 
| 606 | case 'A': | 724 | set_config_option(&g_serveruser, optarg); | 
| 607 | if( !scan_ip6( optarg, tmpip )) { usage( argv[0] ); exit( 1 ); } | 725 | break; | 
| 608 | accesslist_blessip( tmpip, 0xffff ); /* Allow everything for now */ | 726 | case 'r': | 
| 609 | break; | 727 | set_config_option(&g_redirecturl, optarg); | 
| 610 | case 'f': bound += parse_configfile( optarg ); break; | 728 | break; | 
| 611 | case 'h': help( argv[0] ); exit( 0 ); | 729 | case 'l': | 
| 612 | case 'v': { | 730 | statefile = optarg; | 
| 613 | char buffer[8192]; | 731 | break; | 
| 614 | stats_return_tracker_version( buffer ); | 732 | case 'A': | 
| 615 | fputs( buffer, stderr ); | 733 | if (!scan_ip6_net(optarg, &tmpnet)) { | 
| 616 | exit( 0 ); | 734 | usage(argv[0]); | 
| 735 | exit(1); | ||
| 617 | } | 736 | } | 
| 618 | default: | 737 | accesslist_bless_net(&tmpnet, 0xffff); /* Allow everything for now */ | 
| 619 | case '?': usage( argv[0] ); exit( 1 ); | 738 | break; | 
| 739 | case 'f': | ||
| 740 | bound += parse_configfile(optarg); | ||
| 741 | break; | ||
| 742 | case 'h': | ||
| 743 | help(argv[0]); | ||
| 744 | exit(0); | ||
| 745 | case 'v': { | ||
| 746 | char buffer[8192]; | ||
| 747 | stats_return_tracker_version(buffer); | ||
| 748 | fputs(buffer, stderr); | ||
| 749 | exit(0); | ||
| 750 | } | ||
| 751 | default: | ||
| 752 | case '?': | ||
| 753 | usage(argv[0]); | ||
| 754 | exit(1); | ||
| 620 | } | 755 | } | 
| 621 | } | 756 | } | 
| 622 | 757 | ||
| 623 | /* Bind to our default tcp/udp ports */ | 758 | /* Bind to our default tcp/udp ports */ | 
| 624 | if( !bound) { | 759 | if (!bound) { | 
| 625 | ot_try_bind( serverip, 6969, FLAG_TCP ); | 760 | ot_try_bind(serverip, 6969, FLAG_TCP); | 
| 626 | ot_try_bind( serverip, 6969, FLAG_UDP ); | 761 | ot_try_bind(serverip, 6969, FLAG_UDP); | 
| 627 | } | 762 | } | 
| 628 | 763 | ||
| 764 | defaul_signal_handlers(); | ||
| 765 | |||
| 629 | #ifdef WANT_SYSLOGS | 766 | #ifdef WANT_SYSLOGS | 
| 630 | openlog( "opentracker", 0, LOG_USER ); | 767 | openlog("opentracker", 0, LOG_USER); | 
| 631 | setlogmask(LOG_UPTO(LOG_INFO)); | 768 | setlogmask(LOG_UPTO(LOG_INFO)); | 
| 632 | #endif | 769 | #endif | 
| 633 | 770 | ||
| 634 | if( drop_privileges( g_serveruser ? g_serveruser : "nobody", g_serverdir ) == -1 ) | 771 | if (drop_privileges(g_serveruser ? g_serveruser : "nobody", g_serverdir) == -1) | 
| 635 | panic( "drop_privileges failed, exiting. Last error"); | 772 | panic("drop_privileges failed, exiting. Last error"); | 
| 636 | 773 | ||
| 637 | g_now_seconds = time( NULL ); | 774 | g_now_seconds = time(NULL); | 
| 775 | pthread_create(&thread_id, NULL, time_caching_worker, NULL); | ||
| 638 | 776 | ||
| 639 | /* Create our self pipe which allows us to interrupt mainloops | 777 | /* Create our self pipe which allows us to interrupt mainloops | 
| 640 | io_wait in case some data is available to send out */ | 778 | io_wait in case some data is available to send out */ | 
| 641 | if( pipe( g_self_pipe ) == -1 ) | 779 | if (pipe(g_self_pipe) == -1) | 
| 642 | panic( "selfpipe failed: " ); | 780 | panic("selfpipe failed: "); | 
| 643 | if( !io_fd( g_self_pipe[0] ) ) | 781 | if (!io_fd(g_self_pipe[0])) | 
| 644 | panic( "selfpipe io_fd failed: " ); | 782 | panic("selfpipe io_fd failed: "); | 
| 645 | if( !io_fd( g_self_pipe[1] ) ) | 783 | if (!io_fd(g_self_pipe[1])) | 
| 646 | panic( "selfpipe io_fd failed: " ); | 784 | panic("selfpipe io_fd failed: "); | 
| 647 | io_setcookie( g_self_pipe[0], (void*)FLAG_SELFPIPE ); | 785 | io_setcookie(g_self_pipe[0], (void *)FLAG_SELFPIPE); | 
| 648 | io_wantread( g_self_pipe[0] ); | 786 | io_wantread(g_self_pipe[0]); | 
| 649 | 787 | ||
| 650 | defaul_signal_handlers( ); | ||
| 651 | /* Init all sub systems. This call may fail with an exit() */ | 788 | /* Init all sub systems. This call may fail with an exit() */ | 
| 652 | trackerlogic_init( ); | 789 | trackerlogic_init(); | 
| 653 | 790 | ||
| 654 | if( statefile ) | 791 | #ifdef _DEBUG_RANDOMTORRENTS | 
| 655 | load_state( statefile ); | 792 | fprintf(stderr, "DEBUG: Generating %d random peers on random torrents. This may take a while. (Setting RANDOMTORRENTS in trackerlogic.h)\n", RANDOMTORRENTS); | 
| 793 | trackerlogic_add_random_torrents(RANDOMTORRENTS); | ||
| 794 | fprintf(stderr, "... done.\n"); | ||
| 795 | #endif | ||
| 656 | 796 | ||
| 657 | install_signal_handlers( ); | 797 | if (statefile) | 
| 798 | load_state(statefile); | ||
| 658 | 799 | ||
| 659 | if( !g_udp_workers ) | 800 | install_signal_handlers(); | 
| 660 | udp_init( -1, 0 ); | ||
| 661 | 801 | ||
| 662 | /* Kick off our initial clock setting alarm */ | 802 | if (!g_udp_workers) | 
| 663 | alarm(5); | 803 | udp_init(-1, 0); | 
| 664 | 804 | ||
| 665 | server_mainloop( 0 ); | 805 | server_mainloop(0); | 
| 666 | 806 | ||
| 667 | return 0; | 807 | return 0; | 
| 668 | } | 808 | } | 
| 669 | |||
| 670 | const char *g_version_opentracker_c = "$Source$: $Revision$\n"; | ||
