mirror of git://erdgeist.org/opentracker
erdgeist
17 years ago
2 changed files with 572 additions and 0 deletions
@ -0,0 +1,544 @@
@@ -0,0 +1,544 @@
|
||||
/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
|
||||
It is considered beerware. Prost. Skol. Cheers or whatever. */ |
||||
|
||||
/* System */ |
||||
#include <sys/types.h> |
||||
#include <sys/uio.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
|
||||
/* Libowfat */ |
||||
#include "byte.h" |
||||
#include "array.h" |
||||
#include "iob.h" |
||||
|
||||
/* Opentracker */ |
||||
#include "trackerlogic.h" |
||||
#include "ot_mutex.h" |
||||
#include "ot_http.h" |
||||
#include "ot_iovec.h" |
||||
#include "scan_urlencoded_query.h" |
||||
#include "ot_fullscrape.h" |
||||
#include "ot_stats.h" |
||||
#include "ot_accesslist.h" |
||||
#include "ot_sync.h" |
||||
|
||||
#ifndef WANT_TRACKER_SYNC |
||||
#define add_peer_to_torrent(A,B,C) add_peer_to_torrent(A,B) |
||||
#endif |
||||
|
||||
#define OT_MAXMULTISCRAPE_COUNT 64 |
||||
static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT]; |
||||
|
||||
enum { |
||||
SUCCESS_HTTP_HEADER_LENGTH = 80, |
||||
SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING = 32, |
||||
SUCCESS_HTTP_SIZE_OFF = 17 }; |
||||
|
||||
/* Our static output buffer */ |
||||
static char static_outbuf[8192]; |
||||
#ifdef _DEBUG_HTTPERROR |
||||
static char debug_request[8192]; |
||||
#endif |
||||
|
||||
static void http_senddata( const int64 client_socket, char *buffer, size_t size ) { |
||||
struct http_data *h = io_getcookie( client_socket ); |
||||
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( client_socket, buffer, size ); |
||||
if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) { |
||||
free( h ); io_close( client_socket ); |
||||
} else { |
||||
char * outbuf; |
||||
tai6464 t; |
||||
|
||||
if( !h ) return; |
||||
if( !( outbuf = malloc( size - written_size ) ) ) { |
||||
free(h); io_close( client_socket ); |
||||
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( client_socket, t ); |
||||
io_dontwantread( client_socket ); |
||||
io_wantwrite( client_socket ); |
||||
} |
||||
} |
||||
|
||||
#define HTTPERROR_400 return http_issue_error( client_socket, "400 Invalid Request", "This server only understands GET." ) |
||||
#define HTTPERROR_400_PARAM return http_issue_error( client_socket, "400 Invalid Request", "Invalid parameter" ) |
||||
#define HTTPERROR_400_COMPACT return http_issue_error( client_socket, "400 Invalid Request", "This server only delivers compact results." ) |
||||
#define HTTPERROR_403_IP return http_issue_error( client_socket, "403 Access Denied", "Your ip address is not allowed to administrate this server." ) |
||||
#define HTTPERROR_404 return http_issue_error( client_socket, "404 Not Found", "No such file or directory." ) |
||||
#define HTTPERROR_500 return http_issue_error( client_socket, "500 Internal Server Error", "A server error has occured. Please retry later." ) |
||||
ssize_t http_issue_error( const int64 client_socket, 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 |
||||
http_senddata( client_socket, static_outbuf, reply_size); |
||||
return -2; |
||||
} |
||||
|
||||
ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct iovec *iovector ) { |
||||
struct http_data *h = io_getcookie( client_socket ); |
||||
char *header; |
||||
int i; |
||||
size_t header_size, size = iovec_length( &iovec_entries, &iovector ); |
||||
tai6464 t; |
||||
|
||||
/* No cookie? Bad socket. Leave. */ |
||||
if( !h ) { |
||||
iovec_free( &iovec_entries, &iovector ); |
||||
HTTPERROR_500; |
||||
} |
||||
|
||||
/* If this socket collected request in a buffer,
|
||||
free it now */ |
||||
if( h->flag & STRUCT_HTTP_FLAG_ARRAY_USED ) { |
||||
h->flag &= ~STRUCT_HTTP_FLAG_ARRAY_USED; |
||||
array_reset( &h->request ); |
||||
} |
||||
|
||||
/* If we came here, wait for the answer is over */ |
||||
h->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; |
||||
|
||||
/* Our answers never are 0 vectors. Return an error. */ |
||||
if( !iovec_entries ) { |
||||
HTTPERROR_500; |
||||
} |
||||
|
||||
/* Prepare space for http header */ |
||||
header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING ); |
||||
if( !header ) { |
||||
iovec_free( &iovec_entries, &iovector ); |
||||
HTTPERROR_500; |
||||
} |
||||
|
||||
if( h->flag & STRUCT_HTTP_FLAG_GZIP ) |
||||
header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: gzip\r\nContent-Length: %zd\r\n\r\n", size ); |
||||
else if( h->flag & STRUCT_HTTP_FLAG_BZIP2 ) |
||||
header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: bzip2\r\nContent-Length: %zd\r\n\r\n", size ); |
||||
else |
||||
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 ); |
||||
|
||||
/* Will move to ot_iovec.c */ |
||||
for( i=0; i<iovec_entries; ++i ) |
||||
iob_addbuf_munmap( &h->batch, iovector[i].iov_base, iovector[i].iov_len ); |
||||
free( iovector ); |
||||
|
||||
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( client_socket, t ); |
||||
io_dontwantread( client_socket ); |
||||
io_wantwrite( client_socket ); |
||||
return 0; |
||||
} |
||||
|
||||
#ifdef WANT_TRACKER_SYNC |
||||
static ssize_t http_handle_sync( const int64 client_socket, char *data ) { |
||||
struct http_data* h = io_getcookie( client_socket ); |
||||
size_t len; |
||||
int mode = SYNC_OUT, scanon = 1; |
||||
char *c = data; |
||||
|
||||
if( !accesslist_isblessed( h->ip, OT_PERMISSION_MAY_SYNC ) ) |
||||
HTTPERROR_403_IP; |
||||
|
||||
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( (uint8_t*)data, len ) ) HTTPERROR_400_PARAM; |
||||
if( mode == SYNC_OUT ) { |
||||
stats_issue_event( EVENT_SYNC_IN, 1, 0 ); |
||||
mode = SYNC_IN; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if( mode == SYNC_OUT ) { |
||||
/* Pass this task to the worker thread */ |
||||
h->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; |
||||
stats_issue_event( EVENT_SYNC_OUT_REQUEST, 1, 0 ); |
||||
sync_deliver( client_socket ); |
||||
io_dontwantread( client_socket ); |
||||
return -2; |
||||
} |
||||
|
||||
/* Simple but proof for now */ |
||||
memmove( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, "OK", 2); |
||||
return 2; |
||||
} |
||||
#endif |
||||
|
||||
static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d, size_t l ) { |
||||
struct http_data* h = io_getcookie( client_socket ); |
||||
char *c = data; |
||||
int mode = TASK_STATS_PEERS, scanon = 1, format = 0; |
||||
|
||||
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,"peer")) |
||||
mode = TASK_STATS_PEERS; |
||||
else if( !byte_diff(data,4,"conn")) |
||||
mode = TASK_STATS_CONNS; |
||||
else if( !byte_diff(data,4,"top5")) |
||||
mode = TASK_STATS_TOP5; |
||||
else if( !byte_diff(data,4,"scrp")) |
||||
mode = TASK_STATS_SCRAPE; |
||||
else if( !byte_diff(data,4,"fscr")) |
||||
mode = TASK_STATS_FULLSCRAPE; |
||||
else if( !byte_diff(data,4,"tcp4")) |
||||
mode = TASK_STATS_TCP; |
||||
else if( !byte_diff(data,4,"udp4")) |
||||
mode = TASK_STATS_UDP; |
||||
else if( !byte_diff(data,4,"s24s")) |
||||
mode = TASK_STATS_SLASH24S; |
||||
else if( !byte_diff(data,4,"tpbs")) |
||||
mode = TASK_STATS_TPB; |
||||
else |
||||
HTTPERROR_400_PARAM; |
||||
break; |
||||
case 6: |
||||
if( byte_diff(data,6,"format")) { |
||||
scan_urlencoded_skipvalue( &c ); |
||||
continue; |
||||
} |
||||
if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 3 ) HTTPERROR_400_PARAM; |
||||
if( !byte_diff(data,3,"bin")) |
||||
format = TASK_FULLSCRAPE_TPB_BINARY; |
||||
else if( !byte_diff(data,3,"ben")) |
||||
format = TASK_FULLSCRAPE; |
||||
else if( !byte_diff(data,3,"url")) |
||||
format = TASK_FULLSCRAPE_TPB_URLENCODED; |
||||
else if( !byte_diff(data,3,"txt")) |
||||
format = TASK_FULLSCRAPE_TPB_ASCII; |
||||
else |
||||
HTTPERROR_400_PARAM; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if( mode == TASK_STATS_TPB ) { |
||||
tai6464 t; |
||||
#ifdef WANT_COMPRESSION_GZIP |
||||
d[l-1] = 0; |
||||
if( strstr( d, "gzip" ) ) { |
||||
h->flag |= STRUCT_HTTP_FLAG_GZIP; |
||||
format |= TASK_FLAG_GZIP; |
||||
} |
||||
#endif |
||||
/* Pass this task to the worker thread */ |
||||
h->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; |
||||
|
||||
/* Clients waiting for us should not easily timeout */ |
||||
taia_uint( &t, 0 ); io_timeout( client_socket, t ); |
||||
fullscrape_deliver( client_socket, format ); |
||||
io_dontwantread( client_socket ); |
||||
return -2; |
||||
} |
||||
|
||||
// default format for now
|
||||
if( !( l = return_stats_for_tracker( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, mode, 0 ) ) ) HTTPERROR_500; |
||||
return l; |
||||
} |
||||
|
||||
static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_t l ) { |
||||
struct http_data* h = io_getcookie( client_socket ); |
||||
int format = 0; |
||||
tai6464 t; |
||||
|
||||
#ifdef WANT_COMPRESSION_GZIP |
||||
d[l-1] = 0; |
||||
if( strstr( d, "gzip" ) ) { |
||||
h->flag |= STRUCT_HTTP_FLAG_GZIP; |
||||
format = TASK_FLAG_GZIP; |
||||
stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, *(int*)h->ip, 0 ); |
||||
} else |
||||
#endif |
||||
stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 ); |
||||
|
||||
#ifdef _DEBUG_HTTPERROR |
||||
write( 2, debug_request, l ); |
||||
#endif |
||||
|
||||
/* Pass this task to the worker thread */ |
||||
h->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; |
||||
/* Clients waiting for us should not easily timeout */ |
||||
taia_uint( &t, 0 ); io_timeout( client_socket, t ); |
||||
fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format ); |
||||
io_dontwantread( client_socket ); |
||||
return -2; |
||||
} |
||||
|
||||
static ssize_t http_handle_scrape( const int64 client_socket, char *data ) { |
||||
int scanon = 1, numwant = 0; |
||||
char *c = data; |
||||
size_t l; |
||||
|
||||
/* 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; |
||||
} |
||||
|
||||
while( scanon ) { |
||||
switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { |
||||
case -2: scanon = 0; break; /* TERMINATOR */ |
||||
case -1: |
||||
if( numwant ) |
||||
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( numwant < OT_MAXMULTISCRAPE_COUNT ) |
||||
memmove( multiscrape_buf + numwant++, data, sizeof(ot_hash) ); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
UTORRENT1600_WORKAROUND: |
||||
|
||||
/* No info_hash found? Inform user */ |
||||
if( !numwant ) HTTPERROR_400_PARAM; |
||||
|
||||
/* Enough for http header + whole scrape string */ |
||||
if( !( l = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500; |
||||
stats_issue_event( EVENT_SCRAPE, 1, l ); |
||||
return l; |
||||
} |
||||
|
||||
static ssize_t http_handle_announce( const int64 client_socket, char *data ) { |
||||
char *c = data; |
||||
int numwant, tmp, scanon; |
||||
ot_peer peer; |
||||
ot_torrent *torrent; |
||||
ot_hash *hash = NULL; |
||||
unsigned short port = htons(6881); |
||||
ssize_t len; |
||||
|
||||
/* 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( client_socket ) )->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; |
||||
} |
||||
} |
||||
|
||||
/* Scanned whole query string */ |
||||
if( !hash ) |
||||
return 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" ); |
||||
|
||||
if( OT_FLAG( &peer ) & PEER_FLAG_STOPPED ) |
||||
len = remove_peer_from_torrent( hash, &peer, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 1 ); |
||||
else { |
||||
torrent = add_peer_to_torrent( hash, &peer, 0 ); |
||||
if( !torrent || !( len = return_peers_for_torrent( hash, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, 1 ) ) ) HTTPERROR_500; |
||||
} |
||||
stats_issue_event( EVENT_ANNOUNCE, 1, len); |
||||
return len; |
||||
} |
||||
|
||||
ssize_t http_handle_request( const int64 client_socket, char *data, size_t recv_length ) { |
||||
char *c, *recv_header=data; |
||||
ssize_t reply_size = 0, reply_off, len; |
||||
|
||||
#ifdef _DEBUG_HTTPERROR |
||||
if( recv_length >= sizeof( debug_request ) ) |
||||
recv_length = sizeof( debug_request) - 1; |
||||
memcpy( debug_request, recv_header, recv_length ); |
||||
debug_request[ recv_length ] = 0; |
||||
#endif |
||||
|
||||
/* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ |
||||
if( byte_diff( data, 5, "GET /") ) HTTPERROR_400; |
||||
|
||||
/* 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); |
||||
|
||||
/* Try to parse the request.
|
||||
In reality we abandoned requiring the url to be correct. This now |
||||
only decodes url encoded characters, we check for announces and |
||||
scrapes by looking for "a*" or "sc" */ |
||||
len = scan_urlencoded_query( &c, data = c, SCAN_PATH ); |
||||
|
||||
/* If parsing returned an error, leave with not found*/ |
||||
if( len <= 0 ) HTTPERROR_404; |
||||
|
||||
/* This is the hardcore match for announce*/ |
||||
if( ( *data == 'a' ) || ( *data == '?' ) ) |
||||
reply_size = http_handle_announce( client_socket, c ); |
||||
else if( !byte_diff( data, 12, "scrape HTTP/" ) ) |
||||
reply_size = http_handle_fullscrape( client_socket, recv_header, recv_length ); |
||||
/* This is the hardcore match for scrape */ |
||||
else if( !byte_diff( data, 2, "sc" ) ) |
||||
reply_size = http_handle_scrape( client_socket, c ); |
||||
/* All the rest is matched the standard way */ |
||||
else switch( len ) { |
||||
#ifdef WANT_TRACKER_SYNC |
||||
case 4: /* sync ? */ |
||||
if( byte_diff( data, 4, "sync") ) HTTPERROR_404; |
||||
reply_size = http_handle_sync( client_socket, c ); |
||||
break; |
||||
#endif |
||||
case 5: /* stats ? */ |
||||
if( byte_diff(data,5,"stats")) HTTPERROR_404; |
||||
reply_size = http_handle_stats( client_socket, c, recv_header, recv_length ); |
||||
break; |
||||
default: |
||||
HTTPERROR_404; |
||||
} |
||||
|
||||
/* If routines handled sending themselves, just return */ |
||||
if( reply_size == -2 ) return 0; |
||||
/* If routine failed, let http error take over */ |
||||
if( reply_size == -1 ) HTTPERROR_500; |
||||
|
||||
/* This one is rather ugly, so I take you step by step through it.
|
||||
|
||||
1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to |
||||
write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string |
||||
plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate |
||||
the space NOT needed to expand in reply_off |
||||
*/ |
||||
reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_outbuf, 0, "%zd", reply_size ); |
||||
|
||||
/* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
|
||||
packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ |
||||
reply_size += 1 + sprintf( static_outbuf + reply_off, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", reply_size ); |
||||
|
||||
/* 3. Finally we join both blocks neatly */ |
||||
static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; |
||||
|
||||
http_senddata( client_socket, static_outbuf + reply_off, reply_size ); |
||||
return reply_size; |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
|
||||
It is considered beerware. Prost. Skol. Cheers or whatever. */ |
||||
|
||||
#ifndef __OT_HTTP_H__ |
||||
#define __OT_HTTP_H__ |
||||
|
||||
typedef enum { |
||||
STRUCT_HTTP_FLAG_ARRAY_USED = 1, |
||||
STRUCT_HTTP_FLAG_IOB_USED = 2, |
||||
STRUCT_HTTP_FLAG_WAITINGFORTASK = 4, |
||||
STRUCT_HTTP_FLAG_GZIP = 8, |
||||
STRUCT_HTTP_FLAG_BZIP2 = 16 |
||||
} STRUCT_HTTP_FLAG; |
||||
|
||||
struct http_data { |
||||
union { |
||||
array request; |
||||
io_batch batch; |
||||
}; |
||||
char ip[4]; |
||||
STRUCT_HTTP_FLAG flag; |
||||
}; |
||||
|
||||
ssize_t http_handle_request( const int64 s, char *data, size_t l ); |
||||
ssize_t http_sendiovecdata( const int64 s, int iovec_entries, struct iovec *iovector ); |
||||
ssize_t http_issue_error( const int64 s, const char *title, const char *message ); |
||||
|
||||
#endif |
Loading…
Reference in new issue