static void handle_accept( const int64 serversocket ) { struct http_data *h; unsigned char ip[4]; uint16 port; tai6464 t; int64 i; while( ( i = socket_accept4( serversocket, (char*)ip, &port) ) != -1 ) { /* Put fd into a non-blocking mode */ io_nonblock( i ); if( !io_fd( i ) || !( h = (struct http_data*)malloc( sizeof( struct http_data ) ) ) ) { io_close( i ); continue; } io_setcookie( i, h ); io_wantread( i ); memset( h, 0, sizeof( struct http_data ) ); memmove( h->ip, ip, sizeof( ip ) ); stats_issue_event( EVENT_ACCEPT, 1, ntohl(*(uint32_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 + OT_CLIENT_TIMEOUT) ); io_timeout( i, t ); } if( errno == EAGAIN ) io_eagain( serversocket ); }
static void init_conn(int fd, struct data *d) { struct io_conn *conn; ok1(d->state == 0); d->state++; conn = io_new_conn(fd, io_read(d->buf, sizeof(d->buf), no_timeout, d)); io_set_finish(conn, finish_ok, d); io_timeout(conn, time_from_usec(d->timeout_usec), timeout, d); }
ssize_t http_sendiovecdata( const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) { struct http_data *cookie = io_getcookie( sock ); char *header; int i; size_t header_size, size = iovec_length( &iovec_entries, &iovector ); tai6464 t; /* No cookie? Bad socket. Leave. */ if( !cookie ) { iovec_free( &iovec_entries, &iovector ); HTTPERROR_500; } /* If this socket collected request in a buffer, free it now */ array_reset( &cookie->request ); /* If we came here, wait for the answer is over */ cookie->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_LENGTH_CONTENT_ENCODING ); if( !header ) { iovec_free( &iovec_entries, &iovector ); HTTPERROR_500; } if( cookie->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( cookie->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( &cookie->batch ); iob_addbuf_free( &cookie->batch, header, header_size ); /* Will move to ot_iovec.c */ for( i=0; i<iovec_entries; ++i ) iob_addbuf_munmap( &cookie->batch, iovector[i].iov_base, iovector[i].iov_len ); free( iovector ); /* writeable sockets timeout after 10 minutes */ taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); io_timeout( sock, t ); io_dontwantread( sock ); io_wantwrite( sock ); return 0; }
static ssize_t http_handle_fullscrape( const int64 sock, struct ot_workstruct *ws ) { struct http_data* cookie = io_getcookie( sock ); int format = 0; tai6464 t; #ifdef WANT_MODEST_FULLSCRAPES { ot_scrape_log this_peer, *new_peer; int exactmatch; memcpy( this_peer.ip, cookie->ip, sizeof(ot_ip6)); this_peer.last_fullscrape = g_now_seconds; pthread_mutex_lock(&g_modest_fullscrape_mutex); new_peer = vector_find_or_insert( &g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch ); if( !new_peer ) { pthread_mutex_unlock(&g_modest_fullscrape_mutex); HTTPERROR_500; } if( exactmatch && ( this_peer.last_fullscrape - new_peer->last_fullscrape ) < OT_MODEST_PEER_TIMEOUT ) { pthread_mutex_unlock(&g_modest_fullscrape_mutex); HTTPERROR_402_NOTMODEST; } memcpy( new_peer, &this_peer, sizeof(ot_scrape_log)); pthread_mutex_unlock(&g_modest_fullscrape_mutex); } #endif #ifdef WANT_COMPRESSION_GZIP ws->request[ws->request_size-1] = 0; if( strstr( ws->request, "gzip" ) ) { cookie->flag |= STRUCT_HTTP_FLAG_GZIP; format = TASK_FLAG_GZIP; stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip ); } else #endif stats_issue_event( EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip ); #ifdef _DEBUG_HTTPERROR fprintf( stderr, "%s", ws->debugbuf ); #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, TASK_FULLSCRAPE | format ); io_dontwantread( sock ); return ws->reply_size = -2; }
static void http_senddata( const int64 sock, struct ot_workstruct *ws ) { struct http_data *cookie = io_getcookie( sock ); ssize_t written_size; if( !cookie ) { io_close(sock); return; } /* whoever sends data is not interested in its input-array */ if( ws->keep_alive && ws->header_size != ws->request_size ) { size_t rest = ws->request_size - ws->header_size; if( array_start(&cookie->request) ) { memmove( array_start(&cookie->request), ws->request + ws->header_size, rest ); array_truncate( &cookie->request, 1, rest ); } else array_catb(&cookie->request, ws->request + ws->header_size, rest ); } else array_reset( &cookie->request ); written_size = write( sock, ws->reply, ws->reply_size ); if( ( written_size < 0 ) || ( ( written_size == ws->reply_size ) && !ws->keep_alive ) ) { array_reset( &cookie->request ); free( cookie ); io_close( sock ); return; } if( written_size < ws->reply_size ) { char * outbuf; tai6464 t; if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) { array_reset( &cookie->request ); free(cookie); io_close( sock ); return; } memcpy( outbuf, ws->reply + written_size, ws->reply_size - written_size ); iob_addbuf_free( &cookie->batch, outbuf, ws->reply_size - written_size ); /* writeable short data sockets just have a tcp timeout */ if( !ws->keep_alive ) { taia_uint( &t, 0 ); io_timeout( sock, t ); io_dontwantread( sock ); } io_wantwrite( sock ); } }
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 ); }