opentracker.c 8.5 KB
Newer Older
1
/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
E
erdgeist 已提交
2 3 4 5
   It is considered beerware. Prost. Skol. Cheers or whatever.
   Some of the stuff below is stolen from Fefes example libowfat httpd.
*/

6
/* System */
E
erdgeist 已提交
7
#include <string.h>
E
Kickoff  
erdgeist 已提交
8
#include <sys/types.h>
E
erdgeist 已提交
9
#include <sys/socket.h>
E
Kickoff  
erdgeist 已提交
10 11 12
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
D
denis 已提交
13 14
#include <signal.h>
#include <stdio.h>
15
#include <pwd.h>
E
Kickoff  
erdgeist 已提交
16

17 18 19 20 21 22 23 24 25 26
/* Libowfat */
#include "socket.h"
#include "io.h"
#include "iob.h"
#include "array.h"
#include "fmt.h"
#include "scan.h"
#include "ip4.h"

/* Opentracker */
E
erdgeist 已提交
27
#include "trackerlogic.h"
28
#include "ot_iovec.h"
29
#include "ot_mutex.h"
30 31
#include "ot_http.h"
#include "ot_udp.h"
32
#include "ot_clean.h"
33 34
#include "ot_accesslist.h"
#include "ot_stats.h"
E
erdgeist 已提交
35

36
/* Globals */
37
time_t g_now;
E
erdgeist 已提交
38
char * g_redirecturl = NULL;
39

40
/* To always have space for error messages ;) */
41
static char static_inbuf[8192];
E
erdgeist 已提交
42

43 44
static char *FLAG_TCP = "T";
static char *FLAG_UDP = "U";
E
Kickoff  
erdgeist 已提交
45

46
static void panic( const char *routine ) {
47
  fprintf( stderr, "%s: %s\n", routine, strerror(errno) );
E
erdgeist 已提交
48
  exit( 111 );
E
Kickoff  
erdgeist 已提交
49 50
}

51
static void signal_handler( int s ) {
52 53
  if( s == SIGINT ) {
    signal( SIGINT, SIG_IGN);
54

55
    trackerlogic_deinit();
56
    exit( 0 );
57 58 59
  } else if( s == SIGALRM ) {
    g_now = time(NULL);
    alarm(5);
60
  }
D
denis 已提交
61 62
}

63
static void usage( char *name ) {
E
erdgeist 已提交
64
  fprintf( stderr, "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-A ip]"
65 66 67 68 69 70
#ifdef WANT_BLACKLISTING
  " [-b blacklistfile]"
#elif defined ( WANT_CLOSED_TRACKER )
  " [-w whitelistfile]"
#endif
  "\n", name );
E
erdgeist 已提交
71 72
}

E
erdgeist 已提交
73
#define HELPLINE(opt,desc) fprintf(stderr, "\t%-10s%s\n",opt,desc)
74
static void help( char *name ) {
E
erdgeist 已提交
75
  usage( name );
E
erdgeist 已提交
76 77 78 79

  HELPLINE("-i ip","specify ip to bind to (default: *, you may specify more than one)");
  HELPLINE("-p port","specify tcp port to bind to (default: 6969, you may specify more than one)");
  HELPLINE("-P port","specify udp port to bind to (default: 6969, you may specify more than one)");
E
erdgeist 已提交
80
  HELPLINE("-r redirecturl","specify url where / should be redirected to (default none)");
81
  HELPLINE("-d dir","specify directory to try to chroot to (default: \".\")");
E
erdgeist 已提交
82
  HELPLINE("-A ip","bless an ip address as admin address (e.g. to allow syncs from this address)");
83 84
#ifdef WANT_BLACKLISTING
  HELPLINE("-b file","specify blacklist file.");
85 86
#elif defined( WANT_CLOSED_TRACKER )
  HELPLINE("-w file","specify whitelist file.");
87
#endif
E
erdgeist 已提交
88 89

  fprintf( stderr, "\nExample:   ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -i 10.1.1.23 -p 2710 -p 80\n" );
90
}
E
erdgeist 已提交
91
#undef HELPLINE
92

93 94 95 96 97 98 99 100 101 102 103 104 105 106
static void handle_dead( const int64 socket ) {
  struct http_data* h=io_getcookie( socket );
  if( h ) {
    if( h->flag & STRUCT_HTTP_FLAG_IOB_USED )
      iob_reset( &h->batch );
    if( h->flag & STRUCT_HTTP_FLAG_ARRAY_USED )
      array_reset( &h->request );
    if( h->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK )
      mutex_workqueue_canceltask( socket );
    free( h );
  }
  io_close( socket );
}

107
static ssize_t handle_read( const int64 clientsocket ) {
108
  struct http_data* h = io_getcookie( clientsocket );
109
  ssize_t l;
110

111 112 113 114
  if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) {
    handle_dead( clientsocket );
    return 0;
  }
115

116 117
  /* If we get the whole request in one packet, handle it without copying */
  if( !array_start( &h->request ) ) {
118
    if( memchr( static_inbuf, '\n', l ) )
119
      return http_handle_request( clientsocket, static_inbuf, l );
120
    h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
121 122
    array_catb( &h->request, static_inbuf, l );
    return 0;
123 124
  }

125
  h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
126 127
  array_catb( &h->request, static_inbuf, l );

E
erdgeist 已提交
128
  if( array_failed( &h->request ) )
E
erdgeist 已提交
129
    return http_issue_error( clientsocket, CODE_HTTPERROR_500 );
130

131
  if( ( array_bytes( &h->request ) > 8192 ) && !accesslist_isblessed( (char*)&h->ip, OT_PERMISSION_MAY_SYNC ) )
E
erdgeist 已提交
132
     return http_issue_error( clientsocket, CODE_HTTPERROR_500 );
133

134
  if( memchr( array_start( &h->request ), '\n', array_bytes( &h->request ) ) )
135 136 137
    return http_handle_request( clientsocket, array_start( &h->request ), array_bytes( &h->request ) );

  return 0;
138 139
}

140
static void handle_write( const int64 clientsocket ) {
E
erdgeist 已提交
141
  struct http_data* h=io_getcookie( clientsocket );
142 143
  if( !h || ( iob_send( clientsocket, &h->batch ) <= 0 ) )
    handle_dead( clientsocket );
144 145
}

146 147
static void handle_accept( const int64 serversocket ) {
  struct http_data *h;
148 149
  unsigned char ip[4];
  uint16 port;
150 151 152 153 154
  tai6464 t;
  int64 i;

  while( ( i = socket_accept4( serversocket, (char*)ip, &port) ) != -1 ) {

155 156 157
    /* Put fd into a non-blocking mode */
    io_nonblock( i );

158
    if( !io_fd( i ) ||
159
        !( h = (struct http_data*)malloc( sizeof( struct http_data ) ) ) ) {
160 161 162
      io_close( i );
      continue;
    }
E
erdgeist 已提交
163
    io_setcookie( i, h );
164 165
    io_wantread( i );

166
    memset( h, 0, sizeof( struct http_data ) );
167
    memmove( h->ip, ip, sizeof( ip ) );
168

169
    stats_issue_event( EVENT_ACCEPT, 1, 0);
170

171 172 173 174
    /* 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 + OT_CLIENT_TIMEOUT) );
175
    io_timeout( i, t );
176 177
  }

178
  if( errno == EAGAIN )
179 180 181
    io_eagain( serversocket );
}

182
static void server_mainloop( ) {
183 184 185 186
  static time_t ot_last_clean_time;
  time_t        next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
  struct        iovec *iovector;
  int           iovec_entries;
187

E
erdgeist 已提交
188
  for( ; ; ) {
189
    int64 i;
190

191
    io_wait();
192

E
erdgeist 已提交
193
    while( ( i = io_canread( ) ) != -1 ) {
E
erdgeist 已提交
194 195
      const void *cookie = io_getcookie( i );
      if( cookie == FLAG_TCP )
196
        handle_accept( i );
E
erdgeist 已提交
197
      else if( cookie == FLAG_UDP )
198
        handle_udp4( i );
199 200 201 202
      else
        handle_read( i );
    }

203
    while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 )
204
      http_sendiovecdata( i, iovec_entries, iovector );
205

E
erdgeist 已提交
206
    while( ( i = io_canwrite( ) ) != -1 )
207 208
      handle_write( i );

209
    if( g_now > next_timeout_check ) {
210 211
      while( ( i = io_timeouted() ) != -1 )
        handle_dead( i );
212
      next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
213
    }
214 215

    /* See if we need to move our pools */
216 217
    if( NOW != ot_last_clean_time ) {
      ot_last_clean_time = NOW;
218 219
      clean_all_torrents();
    }
220 221 222

    /* Enforce setting the clock */
    signal_handler( SIGALRM );
223 224 225
  }
}

E
erdgeist 已提交
226 227 228
static void ot_try_bind( char ip[4], uint16 port, int is_tcp ) {
  int64 s = is_tcp ? socket_tcp4( ) : socket_udp4();

229 230 231
  if( socket_bind4_reuse( s, ip, port ) == -1 )
    panic( "socket_bind4_reuse" );

E
erdgeist 已提交
232
  if( is_tcp && ( socket_listen( s, SOMAXCONN) == -1 ) )
233 234 235 236 237
    panic( "socket_listen" );

  if( !io_fd( s ) )
    panic( "io_fd" );

E
erdgeist 已提交
238
  io_setcookie( s, is_tcp ? FLAG_TCP : FLAG_UDP );
239 240 241 242 243

  io_wantread( s );
}

int main( int argc, char **argv ) {
244
  struct passwd *pws = NULL;
245
  char serverip[4] = {0,0,0,0}, tmpip[4];
246
  char *serverdir = ".";
247
  int bound = 0, scanon = 1;
248 249 250
#ifdef WANT_ACCESS_CONTROL
  char *accesslist_filename = NULL;
#endif
251

252
  while( scanon ) {
E
erdgeist 已提交
253
    switch( getopt( argc, argv, ":i:p:A:P:d:r:"
254 255 256 257 258 259
#ifdef WANT_BLACKLISTING
"b:"
#elif defined( WANT_CLOSED_TRACKER )
"w:"
#endif
    "h" ) ) {
260
      case -1 : scanon = 0; break;
261
      case 'i': scan_ip4( optarg, serverip ); break;
262
#ifdef WANT_BLACKLISTING
263 264 265
      case 'b': accesslist_filename = optarg; break;
#elif defined( WANT_CLOSED_TRACKER )
      case 'w': accesslist_filename = optarg; break;
266
#endif
267 268
      case 'p': ot_try_bind( serverip, (uint16)atol( optarg ), 1 ); bound++; break;
      case 'P': ot_try_bind( serverip, (uint16)atol( optarg ), 0 ); bound++; break;
269
      case 'd': serverdir = optarg; break;
E
erdgeist 已提交
270
      case 'r': g_redirecturl = optarg; break;
271
      case 'A':
272 273
        scan_ip4( optarg, tmpip );
        accesslist_blessip( tmpip, 0xffff ); /* Allow everything for now */
274
        break;
E
erdgeist 已提交
275
      case 'h': help( argv[0] ); exit( 0 );
276
      default:
E
erdgeist 已提交
277
      case '?': usage( argv[0] ); exit( 1 );
278
    }
279
  }
E
erdgeist 已提交
280

E
erdgeist 已提交
281
  /* Bind to our default tcp/udp ports */
282
  if( !bound) {
E
erdgeist 已提交
283
    ot_try_bind( serverip, 6969, 1 );
284 285
    ot_try_bind( serverip, 6969, 0 );
  }
286

287 288
  /* Drop permissions */
  pws = getpwnam( "nobody" );
289 290 291 292 293 294 295
  if( !pws ) {
    setegid( (gid_t)-2 ); setuid( (uid_t)-2 );
    setgid( (gid_t)-2 ); seteuid( (uid_t)-2 );
  } else {
    setegid( pws->pw_gid ); setuid( pws->pw_uid );
    setgid( pws->pw_gid ); seteuid( pws->pw_uid );
  }
E
erdgeist 已提交
296
  endpwent();
E
erdgeist 已提交
297

298
  accesslist_init( accesslist_filename );
299

300
  signal( SIGPIPE, SIG_IGN );
301 302
  signal( SIGINT,  signal_handler );
  signal( SIGALRM, signal_handler );
303

304 305
  g_now = time( NULL );

306
  if( trackerlogic_init( serverdir ) == -1 )
E
erdgeist 已提交
307
    panic( "Logic not started" );
308

309
  alarm(5);
E
erdgeist 已提交
310

311
  server_mainloop( );
312 313

  return 0;
E
Kickoff  
erdgeist 已提交
314
}