static ssize_t handle_read( const int64 clientsocket ) { struct http_data* h = io_getcookie( clientsocket ); ssize_t l; if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) { handle_dead( clientsocket ); return 0; } /* If we get the whole request in one packet, handle it without copying */ if( !array_start( &h->request ) ) { if( memchr( static_inbuf, '\n', l ) ) return http_handle_request( clientsocket, static_inbuf, l ); h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; array_catb( &h->request, static_inbuf, l ); return 0; } h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED; array_catb( &h->request, static_inbuf, l ); if( array_failed( &h->request ) ) return http_issue_error( clientsocket, CODE_HTTPERROR_500 ); if( ( array_bytes( &h->request ) > 8192 ) && !accesslist_isblessed( (char*)&h->ip, OT_PERMISSION_MAY_SYNC ) ) return http_issue_error( clientsocket, CODE_HTTPERROR_500 ); if( memchr( array_start( &h->request ), '\n', array_bytes( &h->request ) ) ) return http_handle_request( clientsocket, array_start( &h->request ), array_bytes( &h->request ) ); return 0; }
static void * livesync_worker( void * args ) { struct ot_workstruct ws; ot_ip6 in_ip; uint16_t in_port; (void)args; /* Initialize our "thread local storage" */ ws.inbuf = ws.request = malloc( LIVESYNC_INCOMING_BUFFSIZE ); ws.outbuf = ws.reply = 0; memcpy( in_ip, V4mappedprefix, sizeof( V4mappedprefix ) ); while( 1 ) { ws.request_size = socket_recv4(g_socket_in, (char*)ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, 12+(char*)in_ip, &in_port); /* Expect at least tracker id and packet type */ if( ws.request_size <= (ssize_t)(sizeof( g_tracker_id ) + sizeof( uint32_t )) ) continue; if( !accesslist_isblessed(in_ip, OT_PERMISSION_MAY_LIVESYNC)) continue; if( !memcmp( ws.inbuf, &g_tracker_id, sizeof( g_tracker_id ) ) ) { /* TODO: log packet coming from ourselves */ continue; } switch( uint32_read_big( sizeof( g_tracker_id ) + (char *)ws.inbuf ) ) { case OT_SYNC_PEER: livesync_handle_peersync( &ws ); break; default: break; } } /* Never returns. */ return NULL; }
static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { int numwant, tmp, scanon; unsigned short port = 0; char *write_ptr; ssize_t len; struct http_data *cookie = io_getcookie( sock ); /* This is to hack around stupid clients that send "announce ?info_hash" */ if( read_ptr[-1] != '?' ) { while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; ++read_ptr; } #ifdef WANT_IP_FROM_PROXY if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) { ot_ip6 proxied_ip; char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" ); if( fwd && scan_ip6( fwd, proxied_ip ) ) OT_SETIP( &ws->peer, proxied_ip ); else OT_SETIP( &ws->peer, cookie->ip ); } else #endif OT_SETIP( &ws->peer, cookie->ip ); ws->peer_id = NULL; ws->hash = NULL; OT_SETPORT( &ws->peer, &port ); OT_PEERFLAG( &ws->peer ) = 0; numwant = 50; scanon = 1; while( scanon ) { switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { case -2: scanon = 0; break; /* TERMINATOR */ case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -3: scan_urlencoded_skipvalue( &read_ptr ); break; case 1: /* matched "port" */ len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; port = htons( tmp ); OT_SETPORT( &ws->peer, &port ); break; case 2: /* matched "left" */ if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; if( !tmp ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; break; case 3: /* matched "event" */ switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { case -1: HTTPERROR_400_PARAM; case 1: /* matched "completed" */ OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; break; case 2: /* matched "stopped" */ OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; break; default: break; } break; case 4: /* matched "numwant" */ len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; if( numwant < 0 ) numwant = 50; if( numwant > 200 ) numwant = 200; break; case 5: /* matched "compact" */ len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; if( !tmp ) HTTPERROR_400_COMPACT; break; case 6: /* matched "info_hash" */ if( ws->hash ) HTTPERROR_400_DOUBLEHASH; /* ignore this, when we have less than 20 bytes */ if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; ws->hash = (ot_hash*)write_ptr; break; #ifdef WANT_IP_FROM_QUERY_STRING case 7: /* matched "ip" */ { char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16; len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE ); tmp_buf2[len] = 0; if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM; OT_SETIP( &ws->peer, tmp_buf1 ); } break; #endif #ifdef WANT_FULLLOG_NETWORKS case 8: /* matched "lognet" */ { //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { char *tmp_buf = ws->reply; ot_net net; signed short parsed, bits; len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE ); tmp_buf[len] = 0; if( len <= 0 ) HTTPERROR_400_PARAM; if( *tmp_buf == '-' ) { loglist_reset( ); return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" ); } parsed = scan_ip6( tmp_buf, net.address ); if( !parsed ) HTTPERROR_400_PARAM; if( tmp_buf[parsed++] != '/' ) bits = 128; else { parsed = scan_short( tmp_buf + parsed, &bits ); if( !parsed ) HTTPERROR_400_PARAM; if( ip6_isv4mapped( net.address ) ) bits += 96; } net.bits = bits; loglist_add_network( &net ); return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" ); //} } #endif break; case 9: /* matched "peer_id" */ /* ignore this, when we have less than 20 bytes */ if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; ws->peer_id = write_ptr; break; } } #ifdef WANT_LOG_NUMWANT numwants[numwant]++; #endif /* XXX DEBUG stats_issue_event( EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ws->reply ); */ /* Scanned whole query string */ if( !ws->hash ) return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws ); else ws->reply_size = add_peer_to_torrent_and_return_peers( FLAG_TCP, ws, numwant ); stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); return ws->reply_size; }
static ssize_t http_handle_stats( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { static const ot_keywords keywords_main[] = { { "mode", 1 }, {"format", 2 }, { NULL, -3 } }; static const ot_keywords keywords_mode[] = { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP }, { "tcp4", TASK_STATS_TCP }, { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE }, { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS }, { "completed", TASK_STATS_COMPLETED }, { "top100", TASK_STATS_TOP100 }, { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION }, { "everything", TASK_STATS_EVERYTHING }, { "statedump", TASK_FULLSCRAPE_TRACKERSTATE }, { "fulllog", TASK_STATS_FULLLOG }, { "woodpeckers", TASK_STATS_WOODPECKERS}, #ifdef WANT_LOG_NUMWANT { "numwants", TASK_STATS_NUMWANTS}, #endif { NULL, -3 } }; static const ot_keywords keywords_format[] = { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED }, { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { NULL, -3 } }; int mode = TASK_STATS_PEERS, scanon = 1, format = 0; #ifdef WANT_RESTRICT_STATS struct http_data *cookie = io_getcookie( sock ); if( !cookie || !accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) HTTPERROR_403_IP; #endif while( scanon ) { switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { case -2: scanon = 0; break; /* TERMINATOR */ case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -3: scan_urlencoded_skipvalue( &read_ptr ); break; case 1: /* matched "mode" */ if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; break; case 2: /* matched "format" */ if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; break; } } #ifdef WANT_FULLSCRAPE if( mode == TASK_FULLSCRAPE_TRACKERSTATE ) { format = mode; mode = TASK_STATS_TPB; } if( mode == TASK_STATS_TPB ) { struct http_data* cookie = io_getcookie( sock ); tai6464 t; #ifdef WANT_COMPRESSION_GZIP ws->request[ws->request_size] = 0; #ifdef WANT_COMPRESSION_GZIP_ALWAYS if( strstr( read_ptr - 1, "gzip" ) ) { #endif cookie->flag |= STRUCT_HTTP_FLAG_GZIP; format |= TASK_FLAG_GZIP; #ifdef WANT_COMPRESSION_GZIP_ALWAYS } #endif #endif /* Pass this task to the worker thread */ cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; /* Clients waiting for us should not easily timeout */ taia_uint( &t, 0 ); io_timeout( sock, t ); fullscrape_deliver( sock, format ); io_dontwantread( sock ); return ws->reply_size = -2; } #endif /* default format for now */ if( ( mode & TASK_CLASS_MASK ) == TASK_STATS ) { tai6464 t; /* Complex stats also include expensive memory debugging tools */ taia_uint( &t, 0 ); io_timeout( sock, t ); stats_deliver( sock, mode ); return ws->reply_size = -2; } /* Simple stats can be answerred immediately */ return ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ); }