/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
It is considered beerware . Prost . Skol . Cheers or whatever .
Some of the stuff below is stolen from Fefes example libowfat httpd .
$ Id $ */
/* System */
# include <stdlib.h>
# include <string.h>
# include <arpa/inet.h>
# include <sys/socket.h>
# include <unistd.h>
# include <errno.h>
# include <signal.h>
# include <stdio.h>
# include <pwd.h>
# include <ctype.h>
# include <pthread.h>
# ifdef WANT_SYSLOGS
# include <syslog.h>
# endif
/* Libowfat */
# include "socket.h"
# include "io.h"
# include "iob.h"
# include "byte.h"
# include "scan.h"
# include "ip6.h"
/* Opentracker */
# include "trackerlogic.h"
# include "ot_mutex.h"
# include "ot_http.h"
# include "ot_udp.h"
# include "ot_accesslist.h"
# include "ot_stats.h"
# include "ot_livesync.h"
/* Globals */
time_t g_now_seconds ;
char * g_redirecturl ;
uint32_t g_tracker_id ;
volatile int g_opentracker_running = 1 ;
int g_self_pipe [ 2 ] ;
static char * g_serverdir ;
static char * g_serveruser ;
static unsigned int g_udp_workers ;
static void panic ( const char * routine ) __attribute__ ( ( noreturn ) ) ;
static void panic ( const char * routine ) {
fprintf ( stderr , " %s: %s \n " , routine , strerror ( errno ) ) ;
exit ( 111 ) ;
}
static void signal_handler ( int s ) {
if ( s = = SIGINT ) {
/* Any new interrupt signal quits the application */
signal ( SIGINT , SIG_DFL ) ;
/* Tell all other threads to not acquire any new lock on a bucket
but cancel their operations and return */
g_opentracker_running = 0 ;
trackerlogic_deinit ( ) ;
# ifdef WANT_SYSLOGS
closelog ( ) ;
# endif
exit ( 0 ) ;
}
}
static void defaul_signal_handlers ( void ) {
sigset_t signal_mask ;
sigemptyset ( & signal_mask ) ;
sigaddset ( & signal_mask , SIGPIPE ) ;
sigaddset ( & signal_mask , SIGHUP ) ;
sigaddset ( & signal_mask , SIGINT ) ;
sigaddset ( & signal_mask , SIGALRM ) ;
pthread_sigmask ( SIG_BLOCK , & signal_mask , NULL ) ;
}
static void install_signal_handlers ( void ) {
struct sigaction sa ;
sigset_t signal_mask ;
sigemptyset ( & signal_mask ) ;
sa . sa_handler = signal_handler ;
sigemptyset ( & sa . sa_mask ) ;
sa . sa_flags = SA_RESTART ;
if ( ( sigaction ( SIGINT , & sa , NULL ) = = - 1 ) | | ( sigaction ( SIGALRM , & sa , NULL ) = = - 1 ) )
panic ( " install_signal_handlers " ) ;
sigaddset ( & signal_mask , SIGINT ) ;
pthread_sigmask ( SIG_UNBLOCK , & signal_mask , NULL ) ;
}
static void usage ( char * name ) {
fprintf ( stderr , " Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip[/bits]] [-f config] [-s livesyncport] "
# ifdef WANT_ACCESSLIST_BLACK
" [-b blacklistfile] "
# elif defined ( WANT_ACCESSLIST_WHITE )
" [-w whitelistfile] "
# endif
" \n " , name ) ;
}
# define HELPLINE(opt,desc) fprintf(stderr, "\t%-10s%s\n",opt,desc)
static void help ( char * name ) {
usage ( name ) ;
HELPLINE ( " -f config " , " include and execute the config file " ) ;
HELPLINE ( " -i ip " , " specify ip to bind to with next -[pP] (default: any, overrides preceeding ones) " ) ;
HELPLINE ( " -p port " , " do bind to tcp port (default: 6969, you may specify more than one) " ) ;
HELPLINE ( " -P port " , " do bind to udp port (default: 6969, you may specify more than one) " ) ;
HELPLINE ( " -r redirecturl " , " specify url where / should be redirected to (default none) " ) ;
HELPLINE ( " -d dir " , " specify directory to try to chroot to (default: \" . \" ) " ) ;
HELPLINE ( " -u user " , " specify user under whose privileges opentracker should run (default: \" nobody \" ) " ) ;
HELPLINE ( " -A ip[/bits] " , " bless an ip address or net as admin address (e.g. to allow syncs from this address) " ) ;
# ifdef WANT_ACCESSLIST_BLACK
HELPLINE ( " -b file " , " specify blacklist file. " ) ;
# elif defined( WANT_ACCESSLIST_WHITE )
HELPLINE ( " -w file " , " specify whitelist file. " ) ;
# endif
fprintf ( stderr , " \n Example: ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -f ./opentracker.conf -i 10.1.1.23 -p 2710 -p 80 \n " ) ;
fprintf ( stderr , " Here -i 127.0.0.1 selects the ip address for the next -p 6969 and -P 6969. \n " ) ;
fprintf ( stderr , " If no port is bound from config file or command line, the last address given \n " ) ;
fprintf ( stderr , " (or ::1 if none is set) will be used on port 6969. \n " ) ;
}
# undef HELPLINE
static ssize_t header_complete ( char * request , ssize_t byte_count ) {
ssize_t i = 0 , state = 0 ;
for ( i = 1 ; i < byte_count ; i + = 2 )
if ( request [ i ] < = 13 ) {
i - - ;
for ( state = 0 ; i < byte_count ; + + i ) {
char c = request [ i ] ;
if ( c = = ' \r ' | | c = = ' \n ' )
state = ( state > > 2 ) | ( ( c < < 6 ) & 0xc0 ) ;
else
break ;
if ( state > = 0xa0 | | state = = 0x99 ) return i + 1 ;
}
}
return 0 ;
}
static void handle_dead ( const int64 sock ) {
struct http_data * cookie = io_getcookie ( sock ) ;
if ( cookie ) {
size_t i ;
for ( i = 0 ; i < cookie - > batches ; + + i )
iob_reset ( cookie - > batch + i ) ;
free ( cookie - > batch ) ;
array_reset ( & cookie - > request ) ;
if ( cookie - > flag & ( STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER ) )
mutex_workqueue_canceltask ( sock ) ;
free ( cookie ) ;
}
io_close ( sock ) ;
}
static void handle_read ( const int64 sock , struct ot_workstruct * ws ) {
struct http_data * cookie = io_getcookie ( sock ) ;
ssize_t byte_count = io_tryread ( sock , ws - > inbuf , G_INBUF_SIZE ) ;
if ( byte_count = = 0 | | byte_count = = - 3 ) {
handle_dead ( sock ) ;
return ;
}
if ( byte_count = = - 1 )
return ;
/* If we get the whole request in one packet, handle it without copying */
if ( ! array_start ( & cookie - > request ) ) {
if ( ( ws - > header_size = header_complete ( ws - > inbuf , byte_count ) ) ) {
ws - > request = ws - > inbuf ;
ws - > request_size = byte_count ;
http_handle_request ( sock , ws ) ;
} else
array_catb ( & cookie - > request , ws - > inbuf , ( size_t ) byte_count ) ;
return ;
}
array_catb ( & cookie - > request , ws - > inbuf , byte_count ) ;
if ( array_failed ( & cookie - > request ) | | array_bytes ( & cookie - > request ) > 8192 ) {
http_issue_error ( sock , ws , CODE_HTTPERROR_500 ) ;
return ;
}
while ( ( ws - > header_size = header_complete ( array_start ( & cookie - > request ) , array_bytes ( & cookie - > request ) ) ) ) {
ws - > request = array_start ( & cookie - > request ) ;
ws - > request_size = array_bytes ( & cookie - > request ) ;
http_handle_request ( sock , ws ) ;
# ifdef WANT_KEEPALIVE
if ( ! ws - > keep_alive )
# endif
return ;
}
}
static void handle_write ( const int64 sock ) {
struct http_data * cookie = io_getcookie ( sock ) ;
size_t i ;
int chunked = 0 ;
/* Look for the first io_batch still containing bytes to write */
if ( cookie ) {
if ( cookie - > flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER )
chunked = 1 ;
for ( i = 0 ; i < cookie - > batches ; + + i ) {
if ( cookie - > batch [ i ] . bytesleft ) {
int64 res = iob_send ( sock , cookie - > batch + i ) ;
if ( res = = - 3 ) {
handle_dead ( sock ) ;
return ;
}
if ( ! cookie - > batch [ i ] . bytesleft )
continue ;
if ( res = = - 1 | | res > 0 | | i < cookie - > batches - 1 )
return ;
}
}
}
/* In a chunked transfer after all batches accumulated have been sent, wait for the next one */
if ( chunked )
io_dontwantwrite ( sock ) ;
else
handle_dead ( sock ) ;
}
static void handle_accept ( const int64 serversocket ) {
struct http_data * cookie ;
int64 sock ;
ot_ip6 ip ;
uint16 port ;
tai6464 t ;
while ( ( sock = socket_accept6 ( serversocket , ip , & port , NULL ) ) ! = - 1 ) {
/* Put fd into a non-blocking mode */
io_nonblock ( sock ) ;
if ( ! io_fd ( sock ) | |
! ( cookie = ( struct http_data * ) malloc ( sizeof ( struct http_data ) ) ) ) {
io_close ( sock ) ;
continue ;
}
memset ( cookie , 0 , sizeof ( struct http_data ) ) ;
memcpy ( cookie - > ip , ip , sizeof ( ot_ip6 ) ) ;
io_setcookie ( sock , cookie ) ;
io_wantread ( sock ) ;
stats_issue_event ( EVENT_ACCEPT , FLAG_TCP , ( uintptr_t ) ip ) ;
/* That breaks taia encapsulation. But there is no way to take system
time this often in FreeBSD and libowfat does not allow to set unix time */
taia_uint ( & t , 0 ) ; /* Clear t */
tai_unix ( & ( t . sec ) , ( g_now_seconds + OT_CLIENT_TIMEOUT ) ) ;
io_timeout ( sock , t ) ;
}
io_eagain ( serversocket ) ;
}
static void * server_mainloop ( void * args ) {
struct ot_workstruct ws ;
time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL ;
struct iovec * iovector ;
int iovec_entries , is_partial ;
( void ) args ;
/* Initialize our "thread local storage" */
ws . inbuf = malloc ( G_INBUF_SIZE ) ;
ws . outbuf = malloc ( G_OUTBUF_SIZE ) ;
# ifdef _DEBUG_HTTPERROR
ws . debugbuf = malloc ( G_DEBUGBUF_SIZE ) ;
# endif
if ( ! ws . inbuf | | ! ws . outbuf )
panic ( " Initializing worker failed " ) ;
# ifdef WANT_ARC4RANDOM
arc4random_buf ( & ws . rand48_state [ 0 ] , 3 * sizeof ( uint16_t ) ) ;
# else
ws . rand48_state [ 0 ] = ( uint16_t ) random ( ) ;
ws . rand48_state [ 1 ] = ( uint16_t ) random ( ) ;
ws . rand48_state [ 2 ] = ( uint16_t ) random ( ) ;
# endif
for ( ; ; ) {
int64 sock ;
io_wait ( ) ;
while ( ( sock = io_canread ( ) ) ! = - 1 ) {
const void * cookie = io_getcookie ( sock ) ;
if ( ( intptr_t ) cookie = = FLAG_TCP )
handle_accept ( sock ) ;
else if ( ( intptr_t ) cookie = = FLAG_UDP )
handle_udp6 ( sock , & ws ) ;
else if ( ( intptr_t ) cookie = = FLAG_SELFPIPE )
io_tryread ( sock , ws . inbuf , G_INBUF_SIZE ) ;
else
handle_read ( sock , & ws ) ;
}
while ( ( sock = mutex_workqueue_popresult ( & iovec_entries , & iovector , & is_partial ) ) ! = - 1 )
http_sendiovecdata ( sock , & ws , iovec_entries , iovector , is_partial ) ;
while ( ( sock = io_canwrite ( ) ) ! = - 1 )
handle_write ( sock ) ;
if ( g_now_seconds > next_timeout_check ) {
while ( ( sock = io_timeouted ( ) ) ! = - 1 )
handle_dead ( sock ) ;
next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL ;
}
livesync_ticker ( ) ;
}
return 0 ;
}
static int64_t ot_try_bind ( ot_ip6 ip , uint16_t port , PROTO_FLAG proto ) {
int64 sock = proto = = FLAG_TCP ? socket_tcp6 ( ) : socket_udp6 ( ) ;
# ifdef _DEBUG
{
char * protos [ ] = { " TCP " , " UDP " , " UDP mcast " } ;
char _debug [ 512 ] ;
int off = snprintf ( _debug , sizeof ( _debug ) , " Binding socket type %s to address [ " , protos [ proto ] ) ;
off + = fmt_ip6c ( _debug + off , ip ) ;
snprintf ( _debug + off , sizeof ( _debug ) - off , " ]:%d... " , port ) ;
fputs ( _debug , stderr ) ;
}
# endif
if ( socket_bind6_reuse ( sock , ip , port , 0 ) = = - 1 )
panic ( " socket_bind6_reuse " ) ;
if ( ( proto = = FLAG_TCP ) & & ( socket_listen ( sock , SOMAXCONN ) = = - 1 ) )
panic ( " socket_listen " ) ;
if ( ! io_fd ( sock ) )
panic ( " io_fd " ) ;
io_setcookie ( sock , ( void * ) proto ) ;
if ( ( proto = = FLAG_UDP ) & & g_udp_workers ) {
io_block ( sock ) ;
udp_init ( sock , g_udp_workers ) ;
} else
io_wantread ( sock ) ;
# ifdef _DEBUG
fputs ( " success. \n " , stderr ) ;
# endif
return sock ;
}
char * set_config_option ( char * * option , char * value ) {
# ifdef _DEBUG
fprintf ( stderr , " Setting config option: %s \n " , value ) ;
# endif
while ( isspace ( * value ) ) + + value ;
free ( * option ) ;
return * option = strdup ( value ) ;
}
static int scan_ip6_port ( const char * src , ot_ip6 ip , uint16 * port ) {
const char * s = src ;
int off , bracket = 0 ;
while ( isspace ( * s ) ) + + s ;
if ( * s = = ' [ ' ) + + s , + + bracket ; /* for v6 style notation */
if ( ! ( off = scan_ip6 ( s , ip ) ) )
return 0 ;
s + = off ;
if ( bracket & & * s = = ' ] ' ) + + s ;
if ( * s = = 0 | | isspace ( * s ) ) return s - src ;
if ( ! ip6_isv4mapped ( ip ) ) {
if ( * s ! = ' : ' & & * s ! = ' . ' ) return 0 ;
if ( ! bracket & & * ( s ) = = ' : ' ) return 0 ;
s + + ;
} else {
if ( * ( s + + ) ! = ' : ' ) return 0 ;
}
if ( ! ( off = scan_ushort ( s , port ) ) )
return 0 ;
return off + s - src ;
}
static int scan_ip6_net ( const char * src , ot_net * net ) {
const char * s = src ;
int off ;
while ( isspace ( * s ) ) + + s ;
if ( ! ( off = scan_ip6 ( s , net - > address ) ) )
return 0 ;
s + = off ;
if ( * s ! = ' / ' )
net - > bits = 128 ;
else {
s + + ;
if ( ! ( off = scan_int ( s , & net - > bits ) ) )
return 0 ;
if ( ip6_isv4mapped ( net - > address ) )
net - > bits + = 96 ;
if ( net - > bits > 128 )
return 0 ;
s + = off ;
}
return off + s - src ;
}
int parse_configfile ( char * config_filename ) {
FILE * accesslist_filehandle ;
char inbuf [ 512 ] ;
ot_ip6 tmpip ;
# if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE)
ot_net tmpnet ;
# endif
int bound = 0 ;
accesslist_filehandle = fopen ( config_filename , " r " ) ;
if ( accesslist_filehandle = = NULL ) {
fprintf ( stderr , " Warning: Can't open config file: %s. " , config_filename ) ;
return 0 ;
}
while ( fgets ( inbuf , sizeof ( inbuf ) , accesslist_filehandle ) ) {
char * p = inbuf ;
size_t strl ;
/* Skip white spaces */
while ( isspace ( * p ) ) + + p ;
/* Ignore comments and empty lines */
if ( ( * p = = ' # ' ) | | ( * p = = ' \n ' ) | | ( * p = = 0 ) ) continue ;
/* consume trailing new lines and spaces */
strl = strlen ( p ) ;
while ( strl & & isspace ( p [ strl - 1 ] ) )
p [ - - strl ] = 0 ;
/* Scan for commands */
if ( ! byte_diff ( p , 15 , " tracker.rootdir " ) & & isspace ( p [ 15 ] ) ) {
set_config_option ( & g_serverdir , p + 16 ) ;
} else if ( ! byte_diff ( p , 12 , " tracker.user " ) & & isspace ( p [ 12 ] ) ) {
set_config_option ( & g_serveruser , p + 13 ) ;
} else if ( ! byte_diff ( p , 14 , " listen.tcp_udp " ) & & isspace ( p [ 14 ] ) ) {
uint16_t tmpport = 6969 ;
if ( ! scan_ip6_port ( p + 15 , tmpip , & tmpport ) ) goto parse_error ;
ot_try_bind ( tmpip , tmpport , FLAG_TCP ) ; + + bound ;
ot_try_bind ( tmpip , tmpport , FLAG_UDP ) ; + + bound ;
} else if ( ! byte_diff ( p , 10 , " listen.tcp " ) & & isspace ( p [ 10 ] ) ) {
uint16_t tmpport = 6969 ;
if ( ! scan_ip6_port ( p + 11 , tmpip , & tmpport ) ) goto parse_error ;
ot_try_bind ( tmpip , tmpport , FLAG_TCP ) ;
+ + bound ;
} else if ( ! byte_diff ( p , 10 , " listen.udp " ) & & isspace ( p [ 10 ] ) ) {
uint16_t tmpport = 6969 ;
if ( ! scan_ip6_port ( p + 11 , tmpip , & tmpport ) ) goto parse_error ;
ot_try_bind ( tmpip , tmpport , FLAG_UDP ) ;
+ + bound ;
} else if ( ! byte_diff ( p , 18 , " listen.udp.workers " ) & & isspace ( p [ 18 ] ) ) {
char * value = p + 18 ;
while ( isspace ( * value ) ) + + value ;
scan_uint ( value , & g_udp_workers ) ;
# ifdef WANT_ACCESSLIST_WHITE
} else if ( ! byte_diff ( p , 16 , " access.whitelist " ) & & isspace ( p [ 16 ] ) ) {
set_config_option ( & g_accesslist_filename , p + 17 ) ;
# elif defined( WANT_ACCESSLIST_BLACK )
} else if ( ! byte_diff ( p , 16 , " access.blacklist " ) & & isspace ( p [ 16 ] ) ) {
set_config_option ( & g_accesslist_filename , p + 17 ) ;
# endif
# ifdef WANT_DYNAMIC_ACCESSLIST
} else if ( ! byte_diff ( p , 15 , " access.fifo_add " ) & & isspace ( p [ 15 ] ) ) {
set_config_option ( & g_accesslist_pipe_add , p + 16 ) ;
} else if ( ! byte_diff ( p , 18 , " access.fifo_delete " ) & & isspace ( p [ 18 ] ) ) {
set_config_option ( & g_accesslist_pipe_delete , p + 19 ) ;
# endif
# ifdef WANT_RESTRICT_STATS
} else if ( ! byte_diff ( p , 12 , " access.stats " ) & & isspace ( p [ 12 ] ) ) {
if ( ! scan_ip6_net ( p + 13 , & tmpnet ) ) goto parse_error ;
accesslist_bless_net ( & tmpnet , OT_PERMISSION_MAY_STAT ) ;
# endif
} else if ( ! byte_diff ( p , 17 , " access.stats_path " ) & & isspace ( p [ 17 ] ) ) {
set_config_option ( & g_stats_path , p + 18 ) ;
# ifdef WANT_IP_FROM_PROXY
} else if ( ! byte_diff ( p , 12 , " access.proxy " ) & & isspace ( p [ 12 ] ) ) {
if ( ! scan_ip6_net ( p + 13 , & tmpnet ) ) goto parse_error ;
accesslist_bless_net ( & tmpnet , OT_PERMISSION_MAY_PROXY ) ;
# endif
} else if ( ! byte_diff ( p , 20 , " tracker.redirect_url " ) & & isspace ( p [ 20 ] ) ) {
set_config_option ( & g_redirecturl , p + 21 ) ;
# ifdef WANT_SYNC_LIVE
} else if ( ! byte_diff ( p , 24 , " livesync.cluster.node_ip " ) & & isspace ( p [ 24 ] ) ) {
if ( ! scan_ip6_net ( p + 25 , & tmpnet ) ) goto parse_error ;
accesslist_bless_net ( & tmpnet , OT_PERMISSION_MAY_LIVESYNC ) ;
} else if ( ! byte_diff ( p , 23 , " livesync.cluster.listen " ) & & isspace ( p [ 23 ] ) ) {
uint16_t tmpport = LIVESYNC_PORT ;
if ( ! scan_ip6_port ( p + 24 , tmpip , & tmpport ) ) goto parse_error ;
livesync_bind_mcast ( tmpip , tmpport ) ;
# endif
} else
fprintf ( stderr , " Unhandled line in config file: %s \n " , inbuf ) ;
continue ;
parse_error :
fprintf ( stderr , " Parse error in config file: %s \n " , inbuf ) ;
}
fclose ( accesslist_filehandle ) ;
return bound ;
}
void load_state ( const char * const state_filename ) {
FILE * state_filehandle ;
char inbuf [ 512 ] ;
ot_hash infohash ;
unsigned long long base , downcount ;
int consumed ;
state_filehandle = fopen ( state_filename , " r " ) ;
if ( state_filehandle = = NULL ) {
fprintf ( stderr , " Warning: Can't open config file: %s. " , state_filename ) ;
return ;
}
/* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */
while ( fgets ( inbuf , sizeof ( inbuf ) , state_filehandle ) ) {
int i ;
for ( i = 0 ; i < ( int ) sizeof ( ot_hash ) ; + + i ) {
int eger = 16 * scan_fromhex ( inbuf [ 2 * i ] ) + scan_fromhex ( inbuf [ 1 + 2 * i ] ) ;
if ( eger < 0 )
continue ;
infohash [ i ] = eger ;
}
if ( i ! = ( int ) sizeof ( ot_hash ) ) continue ;
i * = 2 ;
if ( inbuf [ i + + ] ! = ' : ' | | ! ( consumed = scan_ulonglong ( inbuf + i , & base ) ) ) continue ;
i + = consumed ;
if ( inbuf [ i + + ] ! = ' : ' | | ! ( consumed = scan_ulonglong ( inbuf + i , & downcount ) ) ) continue ;
add_torrent_from_saved_state ( infohash , base , downcount ) ;
}
fclose ( state_filehandle ) ;
}
int drop_privileges ( const char * const serveruser , const char * const serverdir ) {
struct passwd * pws = NULL ;
# ifdef _DEBUG
if ( ! geteuid ( ) )
fprintf ( stderr , " Dropping to user %s. \n " , serveruser ) ;
if ( serverdir )
fprintf ( stderr , " ch%s'ing to directory %s. \n " , geteuid ( ) ? " dir " : " root " , serverdir ) ;
# endif
/* Grab pws entry before chrooting */
pws = getpwnam ( serveruser ) ;
endpwent ( ) ;
if ( geteuid ( ) = = 0 ) {
/* Running as root: chroot and drop privileges */
if ( serverdir & & chroot ( serverdir ) ) {
fprintf ( stderr , " Could not chroot to %s, because: %s \n " , serverdir , strerror ( errno ) ) ;
return - 1 ;
}
if ( chdir ( " / " ) )
panic ( " chdir() failed after chrooting: " ) ;
/* If we can't find server user, revert to nobody's default uid */
if ( ! pws ) {
fprintf ( stderr , " Warning: Could not get password entry for %s. Reverting to uid -2. \n " , serveruser ) ;
if ( setegid ( ( gid_t ) - 2 ) | | setgid ( ( gid_t ) - 2 ) | | setuid ( ( uid_t ) - 2 ) | | seteuid ( ( uid_t ) - 2 ) )
panic ( " Could not set uid to value -2 " ) ;
}
else {
if ( setegid ( pws - > pw_gid ) | | setgid ( pws - > pw_gid ) | | setuid ( pws - > pw_uid ) | | seteuid ( pws - > pw_uid ) )
panic ( " Could not set uid to specified value " ) ;
}
if ( geteuid ( ) = = 0 | | getegid ( ) = = 0 )
panic ( " Still running with root privileges?! " ) ;
}
else {
/* Normal user, just chdir() */
if ( serverdir & & chdir ( serverdir ) ) {
fprintf ( stderr , " Could not chroot to %s, because: %s \n " , serverdir , strerror ( errno ) ) ;
return - 1 ;
}
}
return 0 ;
}
/* Maintain our copy of the clock. time() on BSDs is very expensive. */
static void * time_caching_worker ( void * args ) {
( void ) args ;
while ( 1 ) {
g_now_seconds = time ( NULL ) ;
sleep ( 5 ) ;
}
}
int main ( int argc , char * * argv ) {
ot_ip6 serverip ;
ot_net tmpnet ;
int bound = 0 , scanon = 1 ;
uint16_t tmpport ;
char * statefile = 0 ;
pthread_t thread_id ; /* time cacher */
memset ( serverip , 0 , sizeof ( ot_ip6 ) ) ;
# ifdef WANT_V4_ONLY
serverip [ 10 ] = serverip [ 11 ] = - 1 ;
# endif
# ifdef WANT_DEV_RANDOM
srandomdev ( ) ;
# else
srandom ( time ( NULL ) ) ;
# endif
while ( scanon ) {
switch ( getopt ( argc , argv , " :i:p:A:P:d:u:r:s:f:l:v "
# ifdef WANT_ACCESSLIST_BLACK
" b: "
# elif defined( WANT_ACCESSLIST_WHITE )
" w: "
# endif
" h " ) ) {
case - 1 : scanon = 0 ; break ;
case ' i ' :
if ( ! scan_ip6 ( optarg , serverip ) ) { usage ( argv [ 0 ] ) ; exit ( 1 ) ; }
break ;
# ifdef WANT_ACCESSLIST_BLACK
case ' b ' : set_config_option ( & g_accesslist_filename , optarg ) ; break ;
# elif defined( WANT_ACCESSLIST_WHITE )
case ' w ' : set_config_option ( & g_accesslist_filename , optarg ) ; break ;
# endif
case ' p ' :
if ( ! scan_ushort ( optarg , & tmpport ) ) { usage ( argv [ 0 ] ) ; exit ( 1 ) ; }
ot_try_bind ( serverip , tmpport , FLAG_TCP ) ; bound + + ; break ;
case ' P ' :
if ( ! scan_ushort ( optarg , & tmpport ) ) { usage ( argv [ 0 ] ) ; exit ( 1 ) ; }
ot_try_bind ( serverip , tmpport , FLAG_UDP ) ; bound + + ; break ;
# ifdef WANT_SYNC_LIVE
case ' s ' :
if ( ! scan_ushort ( optarg , & tmpport ) ) { usage ( argv [ 0 ] ) ; exit ( 1 ) ; }
livesync_bind_mcast ( serverip , tmpport ) ; break ;
# endif
case ' d ' : set_config_option ( & g_serverdir , optarg ) ; break ;
case ' u ' : set_config_option ( & g_serveruser , optarg ) ; break ;
case ' r ' : set_config_option ( & g_redirecturl , optarg ) ; break ;
case ' l ' : statefile = optarg ; break ;
case ' A ' :
if ( ! scan_ip6_net ( optarg , & tmpnet ) ) { usage ( argv [ 0 ] ) ; exit ( 1 ) ; }
accesslist_bless_net ( & tmpnet , 0xffff ) ; /* Allow everything for now */
break ;
case ' f ' : bound + = parse_configfile ( optarg ) ; break ;
case ' h ' : help ( argv [ 0 ] ) ; exit ( 0 ) ;
case ' v ' : {
char buffer [ 8192 ] ;
stats_return_tracker_version ( buffer ) ;
fputs ( buffer , stderr ) ;
exit ( 0 ) ;
}
default :
case ' ? ' : usage ( argv [ 0 ] ) ; exit ( 1 ) ;
}
}
/* Bind to our default tcp/udp ports */
if ( ! bound ) {
ot_try_bind ( serverip , 6969 , FLAG_TCP ) ;
ot_try_bind ( serverip , 6969 , FLAG_UDP ) ;
}
# ifdef WANT_SYSLOGS
openlog ( " opentracker " , 0 , LOG_USER ) ;
setlogmask ( LOG_UPTO ( LOG_INFO ) ) ;
# endif
if ( drop_privileges ( g_serveruser ? g_serveruser : " nobody " , g_serverdir ) = = - 1 )
panic ( " drop_privileges failed, exiting. Last error " ) ;
g_now_seconds = time ( NULL ) ;
pthread_create ( & thread_id , NULL , time_caching_worker , NULL ) ;
/* Create our self pipe which allows us to interrupt mainloops
io_wait in case some data is available to send out */
if ( pipe ( g_self_pipe ) = = - 1 )
panic ( " selfpipe failed: " ) ;
if ( ! io_fd ( g_self_pipe [ 0 ] ) )
panic ( " selfpipe io_fd failed: " ) ;
if ( ! io_fd ( g_self_pipe [ 1 ] ) )
panic ( " selfpipe io_fd failed: " ) ;
io_setcookie ( g_self_pipe [ 0 ] , ( void * ) FLAG_SELFPIPE ) ;
io_wantread ( g_self_pipe [ 0 ] ) ;
defaul_signal_handlers ( ) ;
/* Init all sub systems. This call may fail with an exit() */
trackerlogic_init ( ) ;
# ifdef _DEBUG_RANDOMTORRENTS
fprintf ( stderr , " DEBUG: Generating %d random peers on random torrents. This may take a while. (Setting RANDOMTORRENTS in trackerlogic.h) \n " , RANDOMTORRENTS ) ;
trackerlogic_add_random_torrents ( RANDOMTORRENTS ) ;
fprintf ( stderr , " ... done. \n " ) ;
# endif
if ( statefile )
load_state ( statefile ) ;
install_signal_handlers ( ) ;
if ( ! g_udp_workers )
udp_init ( - 1 , 0 ) ;
server_mainloop ( 0 ) ;
return 0 ;
}
const char * g_version_opentracker_c = " $Source$: $Revision$ \n " ;