An open and free bittorrent tracker https://erdgeist.org/gitweb/opentracker
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1004 lines
33 KiB

/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
16 years ago
It is considered beerware. Prost. Skol. Cheers or whatever.
Some of the stuff below is stolen from Fefes example libowfat httpd.
*/
16 years ago
#include "socket.h"
#include "io.h"
#include "iob.h"
16 years ago
#include "buffer.h"
#include "array.h"
#include "byte.h"
16 years ago
#include "case.h"
#include "fmt.h"
#include "str.h"
#include "scan.h"
#include "ip4.h"
#include <string.h>
16 years ago
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/mman.h>
16 years ago
#include <arpa/inet.h>
16 years ago
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <pwd.h>
16 years ago
#include "trackerlogic.h"
#include "scan_urlencoded_query.h"
/* Globals */
static unsigned long long ot_overall_tcp_connections = 0;
static unsigned long long ot_overall_udp_connections = 0;
static unsigned long long ot_overall_tcp_successfulannounces = 0;
static unsigned long long ot_overall_udp_successfulannounces = 0;
static unsigned long long ot_full_scrape_count = 0;
static unsigned long long ot_full_scrape_size = 0;
static time_t ot_start_time;
static const size_t SUCCESS_HTTP_HEADER_LENGTH = 80;
static const size_t SUCCESS_HTTP_SIZE_OFF = 17;
static uint32_t g_adminip_addresses[OT_ADMINIP_MAX];
static unsigned int g_adminip_count = 0;
time_t g_now;
#if defined ( WANT_BLACKLISTING ) && defined (WANT_CLOSED_TRACKER )
#error WANT_BLACKLISTING and WANT_CLOSED_TRACKER are exclusive.
#endif
#if defined ( WANT_BLACKLISTING ) || defined (WANT_CLOSED_TRACKER )
static char *accesslist_filename = NULL;
#define WANT_ACCESS_CONTROL
#endif
15 years ago
#ifndef WANT_TRACKER_SYNC
#define add_peer_to_torrent(A,B,C) add_peer_to_torrent(A,B)
#endif
#ifndef NO_FULLSCRAPE_LOGGING
#define LOG_TO_STDERR( ... ) fprintf( stderr, __VA_ARGS__ )
#else
#define LOG_TO_STDERR( ... )
#endif
/* To always have space for error messages ;) */
static char static_inbuf[8192];
static char static_outbuf[8192];
#define OT_MAXMULTISCRAPE_COUNT 64
static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT];
static char *FLAG_TCP = "TCP";
static char *FLAG_UDP = "UDP";
static size_t ot_sockets_count = 0;
#ifdef _DEBUG_HTTPERROR
static char debug_request[8192];
#define _DEBUG_HTTPERROR_PARAM( param ) , param
#else
#define _DEBUG_HTTPERROR_PARAM( param )
#endif
typedef enum {
STRUCT_HTTP_FLAG_ARRAY_USED = 1,
STRUCT_HTTP_FLAG_IOB_USED = 2
} STRUCT_HTTP_FLAG;
struct http_data {
union {
array request;
io_batch batch;
};
unsigned char ip[4];
STRUCT_HTTP_FLAG flag;
};
#define NOTBLESSED( h ) (!bsearch( &h->ip, g_adminip_addresses, g_adminip_count, 4, ot_ip_compare ))
static int ot_ip_compare( const void *a, const void *b ) { return memcmp( a,b,4 ); }
/* Prototypes */
int main( int argc, char **argv );
static void httperror( const int64 s, const char *title, const char *message );
static void httpresponse( const int64 s, char *data _DEBUG_HTTPERROR_PARAM(size_t l ) );
static void sendmmapdata( const int64 s, char *buffer, const size_t size );
static void senddata( const int64 s, char *buffer, const size_t size );
static void server_mainloop( );
static void handle_timeouted( void );
static void handle_accept( const int64 serversocket );
static void handle_read( const int64 clientsocket );
static void handle_write( const int64 clientsocket );
static void handle_udp4( const int64 serversocket );
static void ot_try_bind( char ip[4], uint16 port, int is_tcp );
static void usage( char *name );
static void help( char *name );
static void carp( const char *routine );
static void panic( const char *routine );
static void signal_handler( int s );
#define HTTPERROR_400 return httperror( s, "400 Invalid Request", "This server only understands GET." )
#define HTTPERROR_400_PARAM return httperror( s, "400 Invalid Request", "Invalid parameter" )
#define HTTPERROR_400_COMPACT return httperror( s, "400 Invalid Request", "This server only delivers compact results." )
#define HTTPERROR_403_IP return httperror( s, "403 Access Denied", "Your ip address is not allowed to administrate this server." )
#define HTTPERROR_404 return httperror( s, "404 Not Found", "No such file or directory." )
#define HTTPERROR_500 return httperror( s, "500 Internal Server Error", "A server error has occured. Please retry later." )
/* End of prototypes */
static void carp( const char *routine ) {
buffer_puts( buffer_2, routine );
buffer_puts( buffer_2, ": " );
buffer_puterror( buffer_2 );
buffer_putnlflush( buffer_2 );
16 years ago
}
static void panic( const char *routine ) {
carp( routine );
exit( 111 );
16 years ago
}
static void httperror( const int64 s, const char *title, const char *message ) {
size_t reply_size = sprintf( static_outbuf, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n",
title, strlen(message)+strlen(title)+16-4,title+4);
#ifdef _DEBUG_HTTPERROR
fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request );
#endif
senddata(s,static_outbuf,reply_size);
}
static void sendmmapdata( const int64 s, char *buffer, size_t size ) {
struct http_data *h = io_getcookie( s );
char *header;
size_t header_size;
tai6464 t;
if( !h ) {
munmap( buffer, size );
return;
}
if( h->flag & STRUCT_HTTP_FLAG_ARRAY_USED ) {
h->flag &= ~STRUCT_HTTP_FLAG_ARRAY_USED;
array_reset( &h->request );
}
header = malloc( SUCCESS_HTTP_HEADER_LENGTH );
if( !header ) {
munmap( buffer, size );
HTTPERROR_500;
}
header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size );
iob_reset( &h->batch );
iob_addbuf_free( &h->batch, header, header_size );
iob_addbuf_munmap( &h->batch, buffer, size );
h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
/* writeable sockets timeout after twice the pool timeout
which defaults to 5 minutes (e.g. after 10 minutes) */
taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND );
io_timeout( s, t );
io_dontwantread( s );
io_wantwrite( s );
}
static void senddata( const int64 s, char *buffer, size_t size ) {
struct http_data *h = io_getcookie( s );
ssize_t written_size;
/* whoever sends data is not interested in its input-array */
if( h && ( h->flag & STRUCT_HTTP_FLAG_ARRAY_USED ) ) {
h->flag &= ~STRUCT_HTTP_FLAG_ARRAY_USED;
array_reset( &h->request );
}
written_size = write( s, buffer, size );
if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) {
free( h ); io_close( s );
} else {
char * outbuf;
tai6464 t;
if( !h ) return;
if( !( outbuf = malloc( size - written_size ) ) ) {
free(h); io_close( s );
return;
}
iob_reset( &h->batch );
memmove( outbuf, buffer + written_size, size - written_size );
iob_addbuf_free( &h->batch, outbuf, size - written_size );
h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
/* writeable short data sockets just have a tcp timeout */
taia_uint( &t, 0 ); io_timeout( s, t );
io_dontwantread( s );
io_wantwrite( s );
}
16 years ago
}
static void httpresponse( const int64 s, char *data _DEBUG_HTTPERROR_PARAM( size_t l ) ) {
struct http_data* h = io_getcookie( s );
char *c, *reply;
ot_peer peer;
ot_torrent *torrent;
ot_hash *hash = NULL;
int numwant, tmp, scanon, mode, scrape_count;
unsigned short port = htons(6881);
time_t t;
ssize_t len;
size_t reply_size = 0, reply_off;
#ifdef _DEBUG_HTTPERROR
if( l >= sizeof( debug_request ) )
l = sizeof( debug_request) - 1;
memcpy( debug_request, data, l );
debug_request[ l ] = 0;
#endif
/* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
if( byte_diff( data, 5, "GET /") ) HTTPERROR_400;
16 years ago
/* Query string MUST terminate with SP -- we know that theres at least a '\n' where this search terminates */
for( c = data + 5; *c!=' ' && *c != '\t' && *c != '\n' && *c != '\r'; ++c ) ;
if( *c != ' ' ) HTTPERROR_400;
/* Skip leading '/' */
for( c = data+4; *c == '/'; ++c);
switch( scan_urlencoded_query( &c, data = c, SCAN_PATH ) ) {
#ifdef WANT_TRACKER_SYNC
/******************************
* S Y N C *
******************************/
case 4: /* sync ? */
if( *data == 'a' ) goto ANNOUNCE_WORKAROUND;
if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
if( byte_diff( data, 4, "sync") ) HTTPERROR_404;
if( NOTBLESSED( h ) ) HTTPERROR_403_IP;
LOG_TO_STDERR( "sync: %d.%d.%d.%d\n", h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
mode = SYNC_OUT;
scanon = 1;
while( scanon ) {
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
case -2: scanon = 0; break; /* TERMINATOR */
case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
default: scan_urlencoded_skipvalue( &c ); break;
case 9:
if(byte_diff(data,9,"changeset")) {
scan_urlencoded_skipvalue( &c );
continue;
}
/* ignore this, when we dont at least see "d4:syncdee" */
if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) < 10 ) HTTPERROR_400_PARAM;
if( add_changeset_to_tracker( (ot_byte*)data, len ) ) HTTPERROR_400_PARAM;
mode = SYNC_IN;
break;
}
}
if( mode == SYNC_OUT ) {
if( !( reply_size = return_changeset_for_tracker( &reply ) ) ) HTTPERROR_500;
return sendmmapdata( s, reply, reply_size );
}
/* Simple but proof for now */
reply = "OK";
reply_size = 2;
break;
#endif
/******************************
* S T A T S *
******************************/
case 5: /* stats ? */
if( *data == 'a' ) goto ANNOUNCE_WORKAROUND;
if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
if( byte_diff(data,5,"stats")) HTTPERROR_404;
scanon = 1;
mode = STATS_MRTG;
while( scanon ) {
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
case -2: scanon = 0; break; /* TERMINATOR */
case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
default: scan_urlencoded_skipvalue( &c ); break;
case 4:
if( byte_diff(data,4,"mode")) {
scan_urlencoded_skipvalue( &c );
continue;
}
if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 4 ) HTTPERROR_400_PARAM;
if( !byte_diff(data,4,"mrtg"))
mode = STATS_MRTG;
else if( !byte_diff(data,4,"top5"))
mode = STATS_TOP5;
else if( !byte_diff(data,4,"fscr"))
mode = STATS_FULLSCRAPE;
else if( !byte_diff(data,4,"dmem"))
mode = STATS_DMEM;
else if( !byte_diff(data,4,"tcp4"))
mode = STATS_TCP;
else if( !byte_diff(data,4,"udp4"))
mode = STATS_UDP;
else if( !byte_diff(data,4,"s24s"))
mode = STATS_SLASH24S;
else if( !byte_diff(data,4,"s24S"))
mode = STATS_SLASH24S_OLD;
else
HTTPERROR_400_PARAM;
}
}
switch( mode)
{
case STATS_DMEM:
LOG_TO_STDERR( "stats: %d.%d.%d.%d - mode: dmem\n", h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
if( !( reply_size = return_memstat_for_tracker( &reply ) ) ) HTTPERROR_500;
return sendmmapdata( s, reply, reply_size );
case STATS_UDP:
t = time( NULL ) - ot_start_time;
reply_size = sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH,
"%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats.",
ot_overall_udp_connections, ot_overall_udp_successfulannounces, (int)t, (int)(t / 3600) );
break;
case STATS_TCP:
t = time( NULL ) - ot_start_time;
reply_size = sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH,
"%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats.",
ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, (int)t, (int)(t / 3600) );
break;
default:
case STATS_MRTG:
/* Enough for http header + whole scrape string */
if( !( reply_size = return_stats_for_tracker( SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, mode ) ) ) HTTPERROR_500;
break;
case STATS_FULLSCRAPE:
t = time( NULL ) - ot_start_time;
reply_size = sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH,
"%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats.",
ot_full_scrape_count * 1000, ot_full_scrape_size, (int)t, (int)(t / 3600) );
break;
case STATS_SLASH24S:
{
LOG_TO_STDERR( "stats: %d.%d.%d.%d - mode: s24s\n", h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
ot_dword diff; struct timeval tv1, tv2; gettimeofday( &tv1, NULL );
if( !( reply_size = return_stats_for_slash24s( SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 25, 16 ) ) ) HTTPERROR_500;
gettimeofday( &tv2, NULL ); diff = ( tv2.tv_sec - tv1.tv_sec ) * 1000000 + tv2.tv_usec - tv1.tv_usec;
15 years ago
reply_size += sprintf( SUCCESS_HTTP_HEADER_LENGTH + static_outbuf + reply_size, "Time taken: %u\n", diff );
break;
}
case STATS_SLASH24S_OLD:
{
LOG_TO_STDERR( "stats: %d.%d.%d.%d - mode: s24s old\n", h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
ot_dword diff; struct timeval tv1, tv2; gettimeofday( &tv1, NULL );
if( !( reply_size = return_stats_for_slash24s_old( SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 25, 16 ) ) ) HTTPERROR_500;
gettimeofday( &tv2, NULL ); diff = ( tv2.tv_sec - tv1.tv_sec ) * 1000000 + tv2.tv_usec - tv1.tv_usec;
15 years ago
reply_size += sprintf( SUCCESS_HTTP_HEADER_LENGTH + static_outbuf + reply_size, "Time taken: %u\n", diff );
break;
}
}
16 years ago
break;
/******************************
* S C R A P E *
******************************/
case 6: /* scrape ? */
if( *data == 'a' ) goto ANNOUNCE_WORKAROUND;
if( byte_diff( data, 6, "scrape") ) HTTPERROR_404;
/* Full scrape... you might want to limit that */
if( !byte_diff( data, 12, "scrape HTTP/" ) ) {
LOG_TO_STDERR( "[%08d] scrp: %d.%d.%d.%d - FULL SCRAPE\n", (unsigned int)(g_now - ot_start_time), h->ip[0], h->ip[1], h->ip[2], h->ip[3] );
#ifdef _DEBUG_HTTPERROR
write( 2, debug_request, l );
#endif
if( !( reply_size = return_fullscrape_for_tracker( &reply ) ) ) HTTPERROR_500;
/* Stat keeping */
ot_overall_tcp_successfulannounces++;
ot_full_scrape_count++;
ot_full_scrape_size += reply_size;
return sendmmapdata( s, reply, reply_size );
}
SCRAPE_WORKAROUND:
/* This is to hack around stupid clients that send "scrape ?info_hash" */
if( c[-1] != '?' ) {
while( ( *c != '?' ) && ( *c != '\n' ) ) ++c;
if( *c == '\n' ) HTTPERROR_400_PARAM;
++c;
}
scanon = 1;
scrape_count = 0;
while( scanon ) {
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
case -2: scanon = 0; break; /* TERMINATOR */
case -1:
if( scrape_count )
goto UTORRENT1600_WORKAROUND;
HTTPERROR_400_PARAM; /* PARSE ERROR */
default: scan_urlencoded_skipvalue( &c ); break;
case 9:
if(byte_diff(data,9,"info_hash")) {
scan_urlencoded_skipvalue( &c );
continue;
}
/* ignore this, when we have less than 20 bytes */
if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) {
#ifdef WANT_UTORRENT1600_WORKAROUND
if( data[20] != '?' )
#endif
HTTPERROR_400_PARAM;
}
if( scrape_count < OT_MAXMULTISCRAPE_COUNT )
memmove( multiscrape_buf + scrape_count++, data, sizeof(ot_hash) );
break;
}
}
UTORRENT1600_WORKAROUND:
/* No info_hash found? Inform user */
if( !scrape_count ) HTTPERROR_400_PARAM;
/* Enough for http header + whole scrape string */
if( !( reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, scrape_count, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500;
ot_overall_tcp_successfulannounces++;
break;
/******************************
* A N N O U N C E *
******************************/
case 8:
if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
if( *data != 'a' ) HTTPERROR_404;
ANNOUNCE_WORKAROUND:
/* This is to hack around stupid clients that send "announce ?info_hash" */
if( c[-1] != '?' ) {
while( ( *c != '?' ) && ( *c != '\n' ) ) ++c;
if( *c == '\n' ) HTTPERROR_400_PARAM;
++c;
}
OT_SETIP( &peer, ((struct http_data*)io_getcookie( s ) )->ip );
OT_SETPORT( &peer, &port );
OT_FLAG( &peer ) = 0;
numwant = 50;
scanon = 1;
while( scanon ) {
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
case -2: scanon = 0; break; /* TERMINATOR */
case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
default: scan_urlencoded_skipvalue( &c ); break;
#ifdef WANT_IP_FROM_QUERY_STRING
case 2:
if(!byte_diff(data,2,"ip")) {
unsigned char ip[4];
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) HTTPERROR_400_PARAM;
OT_SETIP( &peer, ip );
} else
scan_urlencoded_skipvalue( &c );
break;
#endif
case 4:
if( !byte_diff( data, 4, "port" ) ) {
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
port = htons( tmp ); OT_SETPORT( &peer, &port );
} else if( !byte_diff( data, 4, "left" ) ) {
if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
if( scan_fixed_int( data, len, &tmp ) ) tmp = 0;
if( !tmp ) OT_FLAG( &peer ) |= PEER_FLAG_SEEDING;
} else
scan_urlencoded_skipvalue( &c );
break;
case 5:
if( byte_diff( data, 5, "event" ) )
scan_urlencoded_skipvalue( &c );
else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) {
case -1:
HTTPERROR_400_PARAM;
case 7:
if( !byte_diff( data, 7, "stopped" ) ) OT_FLAG( &peer ) |= PEER_FLAG_STOPPED;
break;
case 9:
if( !byte_diff( data, 9, "completed" ) ) OT_FLAG( &peer ) |= PEER_FLAG_COMPLETED;
default: /* Fall through intended */
break;
}
break;
case 7:
if(!byte_diff(data,7,"numwant")) {
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM;
if( numwant < 0 ) numwant = 50;
if( numwant > 200 ) numwant = 200;
} else if(!byte_diff(data,7,"compact")) {
len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) HTTPERROR_400_PARAM;
if( !tmp ) HTTPERROR_400_COMPACT;
} else
scan_urlencoded_skipvalue( &c );
break;
case 9:
if(byte_diff(data,9,"info_hash")) {
scan_urlencoded_skipvalue( &c );
continue;
}
/* ignore this, when we have less than 20 bytes */
if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
hash = (ot_hash*)data;
break;
16 years ago
}
}
/* Scanned whole query string */
if( !hash ) {
reply_size = sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, "d14:failure reason81:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" );
break;
}
if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED )
reply_size = remove_peer_from_torrent( hash, &peer, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 1 );
else {
torrent = add_peer_to_torrent( hash, &peer, 0 );
if( !torrent || !( reply_size = return_peers_for_torrent( torrent, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 1 ) ) ) HTTPERROR_500;
}
ot_overall_tcp_successfulannounces++;
break;
case 11:
if( *data == 'a' ) goto ANNOUNCE_WORKAROUND;
if( !byte_diff( data, 2, "sc" ) ) goto SCRAPE_WORKAROUND;
if( byte_diff( data, 11, "mrtg_scrape" ) ) HTTPERROR_404;
t = time( NULL ) - ot_start_time;
reply_size = sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH,
"%llu\n%llu\n%i seconds (%i hours)\nopentracker - Pretuned by german engineers, currently handling %llu connections per second.",
ot_overall_tcp_connections+ot_overall_udp_connections, ot_overall_tcp_successfulannounces+ot_overall_udp_successfulannounces, (int)t, (int)(t / 3600), (ot_overall_tcp_connections+ot_overall_udp_connections) / ( (unsigned int)t ? (unsigned int)t : 1 ) );
break;
default: