diff options
Diffstat (limited to 'ot_http.c')
| -rw-r--r-- | ot_http.c | 834 |
1 files changed, 494 insertions, 340 deletions
| @@ -4,506 +4,655 @@ | |||
| 4 | $id$ */ | 4 | $id$ */ |
| 5 | 5 | ||
| 6 | /* System */ | 6 | /* System */ |
| 7 | #include <sys/types.h> | 7 | #define _GNU_SOURCE |
| 8 | #include <arpa/inet.h> | 8 | #include <arpa/inet.h> |
| 9 | #include <stdlib.h> | 9 | #include <pthread.h> |
| 10 | #include <stdio.h> | 10 | #include <stdio.h> |
| 11 | #include <stdlib.h> | ||
| 11 | #include <string.h> | 12 | #include <string.h> |
| 13 | #include <sys/types.h> | ||
| 12 | #include <unistd.h> | 14 | #include <unistd.h> |
| 13 | #include <pthread.h> | ||
| 14 | 15 | ||
| 15 | /* Libowfat */ | 16 | /* Libowfat */ |
| 16 | #include "byte.h" | ||
| 17 | #include "array.h" | 17 | #include "array.h" |
| 18 | #include "byte.h" | ||
| 19 | #include "case.h" | ||
| 18 | #include "iob.h" | 20 | #include "iob.h" |
| 19 | #include "ip6.h" | 21 | #include "ip6.h" |
| 20 | #include "scan.h" | 22 | #include "scan.h" |
| 21 | #include "case.h" | ||
| 22 | 23 | ||
| 23 | /* Opentracker */ | 24 | /* Opentracker */ |
| 24 | #include "trackerlogic.h" | 25 | #include "ot_accesslist.h" |
| 25 | #include "ot_mutex.h" | 26 | #include "ot_fullscrape.h" |
| 26 | #include "ot_http.h" | 27 | #include "ot_http.h" |
| 27 | #include "ot_iovec.h" | 28 | #include "ot_iovec.h" |
| 28 | #include "scan_urlencoded_query.h" | 29 | #include "ot_mutex.h" |
| 29 | #include "ot_fullscrape.h" | ||
| 30 | #include "ot_stats.h" | 30 | #include "ot_stats.h" |
| 31 | #include "ot_accesslist.h" | 31 | #include "scan_urlencoded_query.h" |
| 32 | #include "trackerlogic.h" | ||
| 33 | |||
| 34 | #ifdef WANT_NO_AUTO_FREE | ||
| 35 | #define OT_IOB_INIT(B) (bzero(B, sizeof(io_batch)), 0) | ||
| 36 | #else | ||
| 37 | #define OT_IOB_INIT(B) iob_init_autofree(B, 0) | ||
| 38 | #endif | ||
| 32 | 39 | ||
| 33 | #define OT_MAXMULTISCRAPE_COUNT 64 | 40 | #define OT_MAXMULTISCRAPE_COUNT 64 |
| 41 | #define OT_BATCH_LIMIT (1024 * 1024 * 16) | ||
| 34 | extern char *g_redirecturl; | 42 | extern char *g_redirecturl; |
| 35 | 43 | ||
| 36 | char *g_stats_path; | 44 | char *g_stats_path; |
| 37 | ssize_t g_stats_path_len; | 45 | ssize_t g_stats_path_len; |
| 38 | 46 | ||
| 39 | enum { | 47 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; |
| 40 | SUCCESS_HTTP_HEADER_LENGTH = 80, | ||
| 41 | SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32, | ||
| 42 | SUCCESS_HTTP_SIZE_OFF = 17 }; | ||
| 43 | 48 | ||
| 44 | static void http_senddata( const int64 sock, struct ot_workstruct *ws ) { | 49 | static void http_senddata(const int64 sock, struct ot_workstruct *ws) { |
| 45 | struct http_data *cookie = io_getcookie( sock ); | 50 | struct http_data *cookie = io_getcookie(sock); |
| 46 | ssize_t written_size; | 51 | ssize_t written_size; |
| 47 | 52 | ||
| 48 | if( !cookie ) { io_close(sock); return; } | 53 | if (!cookie) { |
| 54 | io_close(sock); | ||
| 55 | return; | ||
| 56 | } | ||
| 49 | 57 | ||
| 50 | /* whoever sends data is not interested in its input-array */ | 58 | /* whoever sends data is not interested in its input-array */ |
| 51 | if( ws->keep_alive && ws->header_size != ws->request_size ) { | 59 | if (ws->keep_alive && ws->header_size != ws->request_size) { |
| 52 | size_t rest = ws->request_size - ws->header_size; | 60 | size_t rest = ws->request_size - ws->header_size; |
| 53 | if( array_start(&cookie->request) ) { | 61 | if (array_start(&cookie->request)) { |
| 54 | memmove( array_start(&cookie->request), ws->request + ws->header_size, rest ); | 62 | memmove(array_start(&cookie->request), ws->request + ws->header_size, rest); |
| 55 | array_truncate( &cookie->request, 1, rest ); | 63 | array_truncate(&cookie->request, 1, rest); |
| 56 | } else | 64 | } else |
| 57 | array_catb(&cookie->request, ws->request + ws->header_size, rest ); | 65 | array_catb(&cookie->request, ws->request + ws->header_size, rest); |
| 58 | } else | 66 | } else |
| 59 | array_reset( &cookie->request ); | 67 | array_reset(&cookie->request); |
| 60 | 68 | ||
| 61 | written_size = write( sock, ws->reply, ws->reply_size ); | 69 | written_size = write(sock, ws->reply, ws->reply_size); |
| 62 | if( ( written_size < 0 ) || ( ( written_size == ws->reply_size ) && !ws->keep_alive ) ) { | 70 | if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) { |
| 63 | array_reset( &cookie->request ); | 71 | array_reset(&cookie->request); |
| 64 | free( cookie ); io_close( sock ); return; | 72 | free(cookie); |
| 73 | io_close(sock); | ||
| 74 | return; | ||
| 65 | } | 75 | } |
| 66 | 76 | ||
| 67 | if( written_size < ws->reply_size ) { | 77 | if (written_size < ws->reply_size) { |
| 68 | char * outbuf; | 78 | char *outbuf; |
| 69 | tai6464 t; | 79 | tai6464 t; |
| 70 | 80 | ||
| 71 | if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) { | 81 | if (!(outbuf = malloc(ws->reply_size - written_size))) { |
| 72 | array_reset( &cookie->request ); | 82 | array_reset(&cookie->request); |
| 73 | free(cookie); io_close( sock ); | 83 | free(cookie); |
| 84 | io_close(sock); | ||
| 74 | return; | 85 | return; |
| 75 | } | 86 | } |
| 76 | 87 | ||
| 77 | memcpy( outbuf, ws->reply + written_size, ws->reply_size - written_size ); | 88 | memcpy(outbuf, ws->reply + written_size, ws->reply_size - written_size); |
| 78 | iob_addbuf_free( &cookie->batch, outbuf, ws->reply_size - written_size ); | 89 | if (!cookie->batch) { |
| 90 | cookie->batch = malloc(sizeof(io_batch)); | ||
| 91 | if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) { | ||
| 92 | free(cookie->batch); | ||
| 93 | free(outbuf); | ||
| 94 | array_reset(&cookie->request); | ||
| 95 | free(cookie); | ||
| 96 | io_close(sock); | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | cookie->batches = 1; | ||
| 101 | } | ||
| 102 | |||
| 103 | iob_addbuf_free(cookie->batch, outbuf, ws->reply_size - written_size); | ||
| 79 | 104 | ||
| 80 | /* writeable short data sockets just have a tcp timeout */ | 105 | /* writeable short data sockets just have a tcp timeout */ |
| 81 | if( !ws->keep_alive ) { | 106 | if (!ws->keep_alive) { |
| 82 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 107 | taia_uint(&t, 0); |
| 83 | io_dontwantread( sock ); | 108 | io_timeout(sock, t); |
| 109 | io_dontwantread(sock); | ||
| 84 | } | 110 | } |
| 85 | io_wantwrite( sock ); | 111 | io_wantwrite(sock); |
| 86 | } | 112 | } |
| 87 | } | 113 | } |
| 88 | 114 | ||
| 89 | #define HTTPERROR_302 return http_issue_error( sock, ws, CODE_HTTPERROR_302 ) | 115 | #define HTTPERROR_302 return http_issue_error(sock, ws, CODE_HTTPERROR_302) |
| 90 | #define HTTPERROR_400 return http_issue_error( sock, ws, CODE_HTTPERROR_400 ) | 116 | #define HTTPERROR_400 return http_issue_error(sock, ws, CODE_HTTPERROR_400) |
| 91 | #define HTTPERROR_400_PARAM return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) | 117 | #define HTTPERROR_400_PARAM return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM) |
| 92 | #define HTTPERROR_400_COMPACT return http_issue_error( sock, ws, CODE_HTTPERROR_400_COMPACT ) | 118 | #define HTTPERROR_400_COMPACT return http_issue_error(sock, ws, CODE_HTTPERROR_400_COMPACT) |
| 93 | #define HTTPERROR_400_DOUBLEHASH return http_issue_error( sock, ws, CODE_HTTPERROR_400_PARAM ) | 119 | #define HTTPERROR_400_DOUBLEHASH return http_issue_error(sock, ws, CODE_HTTPERROR_400_PARAM) |
| 94 | #define HTTPERROR_402_NOTMODEST return http_issue_error( sock, ws, CODE_HTTPERROR_402_NOTMODEST ) | 120 | #define HTTPERROR_402_NOTMODEST return http_issue_error(sock, ws, CODE_HTTPERROR_402_NOTMODEST) |
| 95 | #define HTTPERROR_403_IP return http_issue_error( sock, ws, CODE_HTTPERROR_403_IP ) | 121 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) |
| 96 | #define HTTPERROR_404 return http_issue_error( sock, ws, CODE_HTTPERROR_404 ) | 122 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) |
| 97 | #define HTTPERROR_500 return http_issue_error( sock, ws, CODE_HTTPERROR_500 ) | 123 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) |
| 98 | ssize_t http_issue_error( const int64 sock, struct ot_workstruct *ws, int code ) { | 124 | ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) { |
| 99 | char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", | 125 | char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", |
| 100 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; | 126 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; |
| 101 | char *title = error_code[code]; | 127 | char *title = error_code[code]; |
| 102 | 128 | ||
| 103 | ws->reply = ws->outbuf; | 129 | ws->reply = ws->outbuf; |
| 104 | if( code == CODE_HTTPERROR_302 ) | 130 | if (code == CODE_HTTPERROR_302) |
| 105 | ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); | 131 | ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl); |
| 106 | else | 132 | else |
| 107 | ws->reply_size = snprintf( ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4); | 133 | ws->reply_size = snprintf(ws->reply, G_OUTBUF_SIZE, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, |
| 134 | strlen(title) + 16 - 4, title + 4); | ||
| 108 | 135 | ||
| 109 | #ifdef _DEBUG_HTTPERROR | 136 | #ifdef _DEBUG_HTTPERROR |
| 110 | fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf ); | 137 | fprintf(stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf); |
| 111 | #endif | 138 | #endif |
| 112 | stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); | 139 | stats_issue_event(EVENT_FAILED, FLAG_TCP, code); |
| 113 | http_senddata( sock, ws ); | 140 | http_senddata(sock, ws); |
| 114 | return ws->reply_size = -2; | 141 | return ws->reply_size = -2; |
| 115 | } | 142 | } |
| 116 | 143 | ||
| 117 | ssize_t http_sendiovecdata( const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) { | 144 | ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) { |
| 118 | struct http_data *cookie = io_getcookie( sock ); | 145 | struct http_data *cookie = io_getcookie(sock); |
| 119 | char *header; | 146 | io_batch *current; |
| 120 | int i; | 147 | char *header; |
| 121 | size_t header_size, size = iovec_length( &iovec_entries, &iovector ); | 148 | const char *encoding = ""; |
| 122 | tai6464 t; | 149 | size_t i, header_size, size = iovec_length(&iovec_entries, (const struct iovec **)&iovector); |
| 150 | tai6464 t; | ||
| 123 | 151 | ||
| 124 | /* No cookie? Bad socket. Leave. */ | 152 | /* No cookie? Bad socket. Leave. */ |
| 125 | if( !cookie ) { | 153 | if (!cookie) { |
| 126 | iovec_free( &iovec_entries, &iovector ); | 154 | iovec_free(&iovec_entries, &iovector); |
| 127 | HTTPERROR_500; | 155 | HTTPERROR_500; |
| 128 | } | 156 | } |
| 129 | 157 | ||
| 130 | /* If this socket collected request in a buffer, free it now */ | 158 | /* If this socket collected request in a buffer, free it now */ |
| 131 | array_reset( &cookie->request ); | 159 | array_reset(&cookie->request); |
| 132 | 160 | ||
| 133 | /* If we came here, wait for the answer is over */ | 161 | /* If we came here, wait for the answer is over */ |
| 134 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; | 162 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { |
| 135 | 163 | io_dontwantread(sock); | |
| 136 | /* Our answers never are 0 vectors. Return an error. */ | 164 | cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK; |
| 137 | if( !iovec_entries ) { | ||
| 138 | HTTPERROR_500; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Prepare space for http header */ | ||
| 142 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING ); | ||
| 143 | if( !header ) { | ||
| 144 | iovec_free( &iovec_entries, &iovector ); | ||
| 145 | HTTPERROR_500; | ||
| 146 | } | 165 | } |
| 147 | 166 | ||
| 148 | if( cookie->flag & STRUCT_HTTP_FLAG_GZIP ) | 167 | if (iovec_entries) { |
| 149 | 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 ); | 168 | |
| 150 | else if( cookie->flag & STRUCT_HTTP_FLAG_BZIP2 ) | 169 | if (cookie->flag & STRUCT_HTTP_FLAG_ZSTD) |
| 151 | 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 ); | 170 | encoding = "Content-Encoding: zstd\r\n"; |
| 152 | else | 171 | else if (cookie->flag & STRUCT_HTTP_FLAG_GZIP) |
| 153 | header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size ); | 172 | encoding = "Content-Encoding: gzip\r\n"; |
| 173 | else if (cookie->flag & STRUCT_HTTP_FLAG_BZIP2) | ||
| 174 | encoding = "Content-Encoding: bzip2\r\n"; | ||
| 175 | |||
| 176 | if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED)) | ||
| 177 | header_size = asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n%sContent-Length: %zd\r\n\r\n", encoding, size); | ||
| 178 | else { | ||
| 179 | if (!(cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) { | ||
| 180 | header_size = | ||
| 181 | asprintf(&header, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n%sTransfer-Encoding: chunked\r\n\r\n%zx\r\n", encoding, size); | ||
| 182 | cookie->flag |= STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; | ||
| 183 | } else | ||
| 184 | header_size = asprintf(&header, "%zx\r\n", size); | ||
| 185 | } | ||
| 186 | if (!header) { | ||
| 187 | iovec_free(&iovec_entries, &iovector); | ||
| 188 | HTTPERROR_500; | ||
| 189 | } | ||
| 154 | 190 | ||
| 155 | iob_reset( &cookie->batch ); | 191 | if (!cookie->batch) { |
| 156 | iob_addbuf_free( &cookie->batch, header, header_size ); | 192 | cookie->batch = malloc(sizeof(io_batch)); |
| 193 | if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) { | ||
| 194 | free(cookie->batch); | ||
| 195 | free(header); | ||
| 196 | iovec_free(&iovec_entries, &iovector); | ||
| 197 | HTTPERROR_500; | ||
| 198 | } | ||
| 199 | cookie->batches = 1; | ||
| 200 | } | ||
| 201 | current = cookie->batch + cookie->batches - 1; | ||
| 202 | iob_addbuf_free(current, header, header_size); | ||
| 203 | |||
| 204 | /* Split huge iovectors into separate io_batches */ | ||
| 205 | for (i = 0; i < (size_t)iovec_entries; ++i) { | ||
| 206 | /* If the current batch's limit is reached, try to reallocate a new batch to work on */ | ||
| 207 | if (current->bytesleft > OT_BATCH_LIMIT) { | ||
| 208 | io_batch *new_batch = realloc(cookie->batch, (cookie->batches + 1) * sizeof(io_batch)); | ||
| 209 | if (new_batch) { | ||
| 210 | cookie->batch = new_batch; | ||
| 211 | if (OT_IOB_INIT(current) != -1) | ||
| 212 | current = cookie->batch + cookie->batches++; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | iob_addbuf_free(current, iovector[i].iov_base, iovector[i].iov_len); | ||
| 216 | } | ||
| 217 | free(iovector); | ||
| 218 | if (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) | ||
| 219 | iob_addbuf(current, "\r\n", 2); | ||
| 220 | } | ||
| 157 | 221 | ||
| 158 | /* Will move to ot_iovec.c */ | 222 | if ((cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) && cookie->batch && !is_partial) { |
| 159 | for( i=0; i<iovec_entries; ++i ) | 223 | current = cookie->batch + cookie->batches - 1; |
| 160 | iob_addbuf_munmap( &cookie->batch, iovector[i].iov_base, iovector[i].iov_len ); | 224 | iob_addbuf(current, "0\r\n\r\n", 5); |
| 161 | free( iovector ); | 225 | cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; |
| 226 | } | ||
| 162 | 227 | ||
| 163 | /* writeable sockets timeout after 10 minutes */ | 228 | /* writeable sockets timeout after 10 minutes */ |
| 164 | taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); | 229 | taia_now(&t); |
| 165 | io_timeout( sock, t ); | 230 | taia_addsec(&t, &t, OT_CLIENT_TIMEOUT_SEND); |
| 166 | io_dontwantread( sock ); | 231 | io_timeout(sock, t); |
| 167 | io_wantwrite( sock ); | 232 | io_wantwrite(sock); |
| 168 | return 0; | 233 | return 0; |
| 169 | } | 234 | } |
| 170 | 235 | ||
| 171 | static ssize_t http_handle_stats( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | 236 | static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { |
| 172 | static const ot_keywords keywords_main[] = | 237 | static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}}; |
| 173 | { { "mode", 1 }, {"format", 2 }, { NULL, -3 } }; | 238 | static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS}, |
| 174 | static const ot_keywords keywords_mode[] = | 239 | {"conn", TASK_STATS_CONNS}, |
| 175 | { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP }, { "tcp4", TASK_STATS_TCP }, | 240 | {"scrp", TASK_STATS_SCRAPE}, |
| 176 | { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE }, | 241 | {"udp4", TASK_STATS_UDP}, |
| 177 | { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS }, { "completed", TASK_STATS_COMPLETED }, | 242 | {"tcp4", TASK_STATS_TCP}, |
| 178 | { "top100", TASK_STATS_TOP100 }, { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION }, | 243 | {"busy", TASK_STATS_BUSY_NETWORKS}, |
| 179 | { "everything", TASK_STATS_EVERYTHING }, { "statedump", TASK_FULLSCRAPE_TRACKERSTATE }, { "fulllog", TASK_STATS_FULLLOG }, | 244 | {"torr", TASK_STATS_TORRENTS}, |
| 180 | { "woodpeckers", TASK_STATS_WOODPECKERS}, | 245 | {"fscr", TASK_STATS_FULLSCRAPE}, |
| 246 | {"s24s", TASK_STATS_SLASH24S}, | ||
| 247 | {"tpbs", TASK_STATS_TPB}, | ||
| 248 | {"herr", TASK_STATS_HTTPERRORS}, | ||
| 249 | {"completed", TASK_STATS_COMPLETED}, | ||
| 250 | {"top100", TASK_STATS_TOP100}, | ||
| 251 | {"top10", TASK_STATS_TOP10}, | ||
| 252 | {"renew", TASK_STATS_RENEW}, | ||
| 253 | {"syncs", TASK_STATS_SYNCS}, | ||
| 254 | {"version", TASK_STATS_VERSION}, | ||
| 255 | {"everything", TASK_STATS_EVERYTHING}, | ||
| 256 | {"statedump", TASK_FULLSCRAPE_TRACKERSTATE}, | ||
| 257 | {"fulllog", TASK_STATS_FULLLOG}, | ||
| 258 | {"woodpeckers", TASK_STATS_WOODPECKERS}, | ||
| 181 | #ifdef WANT_LOG_NUMWANT | 259 | #ifdef WANT_LOG_NUMWANT |
| 182 | { "numwants", TASK_STATS_NUMWANTS}, | 260 | {"numwants", TASK_STATS_NUMWANTS}, |
| 183 | #endif | 261 | #endif |
| 184 | { NULL, -3 } }; | 262 | {NULL, -3}}; |
| 185 | static const ot_keywords keywords_format[] = | 263 | static const ot_keywords keywords_format[] = {{"bin", TASK_FULLSCRAPE_TPB_BINARY}, {"ben", TASK_FULLSCRAPE}, |
| 186 | { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED }, | 264 | {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, |
| 187 | { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { "txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS }, { NULL, -3 } }; | 265 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; |
| 188 | 266 | ||
| 189 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; | 267 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; |
| 190 | 268 | ||
| 191 | #ifdef WANT_RESTRICT_STATS | 269 | #ifdef WANT_RESTRICT_STATS |
| 192 | struct http_data *cookie = io_getcookie( sock ); | 270 | struct http_data *cookie = io_getcookie(sock); |
| 193 | 271 | ||
| 194 | if( !cookie || !accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) | 272 | if (!cookie || !accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT)) |
| 195 | HTTPERROR_403_IP; | 273 | HTTPERROR_403_IP; |
| 196 | #endif | 274 | #endif |
| 197 | 275 | ||
| 198 | while( scanon ) { | 276 | while (scanon) { |
| 199 | switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { | 277 | switch (scan_find_keywords(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
| 200 | case -2: scanon = 0; break; /* TERMINATOR */ | 278 | case -2: |
| 201 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 279 | scanon = 0; |
| 202 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; | 280 | break; /* TERMINATOR */ |
| 203 | case 1: /* matched "mode" */ | 281 | case -1: |
| 204 | if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; | 282 | HTTPERROR_400_PARAM; /* PARSE ERROR */ |
| 283 | case -3: | ||
| 284 | scan_urlencoded_skipvalue(&read_ptr); | ||
| 285 | break; | ||
| 286 | case 1: /* matched "mode" */ | ||
| 287 | if ((mode = scan_find_keywords(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) | ||
| 288 | HTTPERROR_400_PARAM; | ||
| 205 | break; | 289 | break; |
| 206 | case 2: /* matched "format" */ | 290 | case 2: /* matched "format" */ |
| 207 | if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; | 291 | if ((format = scan_find_keywords(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) |
| 292 | HTTPERROR_400_PARAM; | ||
| 208 | break; | 293 | break; |
| 294 | case 3: | ||
| 295 | HTTPERROR_400_PARAM; /* If the stats URL was mistakenly added as announce URL, return a 400 */ | ||
| 209 | } | 296 | } |
| 210 | } | 297 | } |
| 211 | 298 | ||
| 212 | #ifdef WANT_FULLSCRAPE | 299 | #ifdef WANT_FULLSCRAPE |
| 213 | if( mode == TASK_FULLSCRAPE_TRACKERSTATE ) { | 300 | if (mode == TASK_FULLSCRAPE_TRACKERSTATE) { |
| 214 | format = mode; mode = TASK_STATS_TPB; | 301 | format = mode; |
| 302 | mode = TASK_STATS_TPB; | ||
| 215 | } | 303 | } |
| 216 | 304 | ||
| 217 | if( mode == TASK_STATS_TPB ) { | 305 | if (mode == TASK_STATS_TPB) { |
| 218 | struct http_data* cookie = io_getcookie( sock ); | 306 | struct http_data *cookie = io_getcookie(sock); |
| 219 | tai6464 t; | 307 | tai6464 t; |
| 220 | #ifdef WANT_COMPRESSION_GZIP | 308 | #ifdef WANT_COMPRESSION_GZIP |
| 221 | ws->request[ws->request_size] = 0; | 309 | ws->request[ws->request_size] = 0; |
| 222 | #ifdef WANT_COMPRESSION_GZIP_ALWAYS | 310 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS |
| 223 | if( strstr( read_ptr - 1, "gzip" ) ) { | 311 | if (strstr(read_ptr - 1, "gzip")) { |
| 224 | #endif | 312 | #endif |
| 225 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | 313 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; |
| 226 | format |= TASK_FLAG_GZIP; | 314 | format |= TASK_FLAG_GZIP; |
| 227 | #ifdef WANT_COMPRESSION_GZIP_ALWAYS | 315 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS |
| 228 | } | 316 | } |
| 229 | #endif | 317 | #endif |
| 230 | #endif | 318 | #endif |
| 231 | /* Pass this task to the worker thread */ | 319 | /* Pass this task to the worker thread */ |
| 232 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; | 320 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
| 233 | 321 | ||
| 234 | /* Clients waiting for us should not easily timeout */ | 322 | /* Clients waiting for us should not easily timeout */ |
| 235 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 323 | taia_uint(&t, 0); |
| 236 | fullscrape_deliver( sock, format ); | 324 | io_timeout(sock, t); |
| 237 | io_dontwantread( sock ); | 325 | fullscrape_deliver(sock, format); |
| 326 | io_dontwantread(sock); | ||
| 238 | return ws->reply_size = -2; | 327 | return ws->reply_size = -2; |
| 239 | } | 328 | } |
| 240 | #endif | 329 | #endif |
| 241 | 330 | ||
| 242 | /* default format for now */ | 331 | /* default format for now */ |
| 243 | if( ( mode & TASK_CLASS_MASK ) == TASK_STATS ) { | 332 | if ((mode & TASK_CLASS_MASK) == TASK_STATS) { |
| 244 | tai6464 t; | 333 | tai6464 t; |
| 245 | /* Complex stats also include expensive memory debugging tools */ | 334 | /* Complex stats also include expensive memory debugging tools */ |
| 246 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 335 | taia_uint(&t, 0); |
| 247 | stats_deliver( sock, mode ); | 336 | io_timeout(sock, t); |
| 337 | stats_deliver(sock, mode); | ||
| 248 | return ws->reply_size = -2; | 338 | return ws->reply_size = -2; |
| 249 | } | 339 | } |
| 250 | 340 | ||
| 251 | /* Simple stats can be answerred immediately */ | 341 | /* Simple stats can be answerred immediately */ |
| 252 | return ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ); | 342 | return ws->reply_size = return_stats_for_tracker(ws->reply, mode, 0); |
| 253 | } | 343 | } |
| 254 | 344 | ||
| 255 | #ifdef WANT_MODEST_FULLSCRAPES | 345 | #ifdef WANT_MODEST_FULLSCRAPES |
| 256 | static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; | 346 | static pthread_mutex_t g_modest_fullscrape_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 257 | static ot_vector g_modest_fullscrape_timeouts; | 347 | static ot_vector g_modest_fullscrape_timeouts; |
| 258 | typedef struct { ot_ip6 ip; ot_time last_fullscrape; } ot_scrape_log; | 348 | typedef struct { |
| 349 | ot_ip6 ip; | ||
| 350 | ot_time last_fullscrape; | ||
| 351 | } ot_scrape_log; | ||
| 259 | #endif | 352 | #endif |
| 260 | 353 | ||
| 261 | #ifdef WANT_FULLSCRAPE | 354 | #ifdef WANT_FULLSCRAPE |
| 262 | static ssize_t http_handle_fullscrape( const int64 sock, struct ot_workstruct *ws ) { | 355 | static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws) { |
| 263 | struct http_data* cookie = io_getcookie( sock ); | 356 | struct http_data *cookie = io_getcookie(sock); |
| 264 | int format = 0; | 357 | int format = 0; |
| 265 | tai6464 t; | 358 | tai6464 t; |
| 266 | 359 | ||
| 267 | #ifdef WANT_MODEST_FULLSCRAPES | 360 | #ifdef WANT_MODEST_FULLSCRAPES |
| 268 | { | 361 | { |
| 269 | ot_scrape_log this_peer, *new_peer; | 362 | ot_scrape_log this_peer, *new_peer; |
| 270 | int exactmatch; | 363 | int exactmatch; |
| 271 | memcpy( this_peer.ip, cookie->ip, sizeof(ot_ip6)); | 364 | memcpy(this_peer.ip, cookie->ip, sizeof(ot_ip6)); |
| 272 | this_peer.last_fullscrape = g_now_seconds; | 365 | this_peer.last_fullscrape = g_now_seconds; |
| 273 | pthread_mutex_lock(&g_modest_fullscrape_mutex); | 366 | pthread_mutex_lock(&g_modest_fullscrape_mutex); |
| 274 | new_peer = vector_find_or_insert( &g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch ); | 367 | new_peer = vector_find_or_insert(&g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch); |
| 275 | if( !new_peer ) { | 368 | if (!new_peer) { |
| 276 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 369 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
| 277 | HTTPERROR_500; | 370 | HTTPERROR_500; |
| 278 | } | 371 | } |
| 279 | if( exactmatch && ( this_peer.last_fullscrape - new_peer->last_fullscrape ) < OT_MODEST_PEER_TIMEOUT ) { | 372 | if (exactmatch && (this_peer.last_fullscrape - new_peer->last_fullscrape) < OT_MODEST_PEER_TIMEOUT) { |
| 280 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 373 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
| 281 | HTTPERROR_402_NOTMODEST; | 374 | HTTPERROR_402_NOTMODEST; |
| 282 | } | 375 | } |
| 283 | memcpy( new_peer, &this_peer, sizeof(ot_scrape_log)); | 376 | memcpy(new_peer, &this_peer, sizeof(ot_scrape_log)); |
| 284 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 377 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
| 285 | } | 378 | } |
| 286 | #endif | 379 | #endif |
| 287 | 380 | ||
| 381 | |||
| 382 | #if defined(WANT_COMPRESSION_GZIP) || defined(WANT_COMPRESSION_ZSTD) | ||
| 383 | ws->request[ws->request_size - 1] = 0; | ||
| 288 | #ifdef WANT_COMPRESSION_GZIP | 384 | #ifdef WANT_COMPRESSION_GZIP |
| 289 | ws->request[ws->request_size-1] = 0; | 385 | if (strstr(ws->request, "gzip")) { |
| 290 | if( strstr( ws->request, "gzip" ) ) { | ||
| 291 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | 386 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; |
| 292 | format = TASK_FLAG_GZIP; | 387 | format |= TASK_FLAG_GZIP; |
| 293 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip ); | 388 | } |
| 294 | } else | 389 | #endif |
| 390 | #ifdef WANT_COMPRESSION_ZSTD | ||
| 391 | if (strstr(ws->request, "zstd")) { | ||
| 392 | cookie->flag |= STRUCT_HTTP_FLAG_ZSTD; | ||
| 393 | format |= TASK_FLAG_ZSTD; | ||
| 394 | } | ||
| 395 | #endif | ||
| 396 | |||
| 397 | #if defined(WANT_COMPRESSION_ZSTD) && defined(WANT_COMPRESSION_ZSTD_ALWAYS) | ||
| 398 | cookie->flag |= STRUCT_HTTP_FLAG_ZSTD; | ||
| 399 | format |= TASK_FLAG_ZSTD; | ||
| 400 | #endif | ||
| 401 | |||
| 402 | #if defined(WANT_COMPRESSION_GZIP) && defined(WANT_COMPRESSION_GZIP_ALWAYS) | ||
| 403 | cookie->flag |= STRUCT_HTTP_FLAG_GZIP; | ||
| 404 | format |= TASK_FLAG_GZIP; | ||
| 295 | #endif | 405 | #endif |
| 296 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip ); | 406 | #endif |
| 407 | |||
| 408 | stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); | ||
| 297 | 409 | ||
| 298 | #ifdef _DEBUG_HTTPERROR | 410 | #ifdef _DEBUG_HTTPERROR |
| 299 | fprintf( stderr, "%s", ws->debugbuf ); | 411 | fprintf(stderr, "%s", ws->debugbuf); |
| 300 | #endif | 412 | #endif |
| 301 | 413 | ||
| 302 | /* Pass this task to the worker thread */ | 414 | /* Pass this task to the worker thread */ |
| 303 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; | 415 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED; |
| 304 | /* Clients waiting for us should not easily timeout */ | 416 | /* Clients waiting for us should not easily timeout */ |
| 305 | taia_uint( &t, 0 ); io_timeout( sock, t ); | 417 | taia_uint(&t, 0); |
| 306 | fullscrape_deliver( sock, TASK_FULLSCRAPE | format ); | 418 | io_timeout(sock, t); |
| 307 | io_dontwantread( sock ); | 419 | fullscrape_deliver(sock, TASK_FULLSCRAPE | format); |
| 420 | io_dontwantread(sock); | ||
| 308 | return ws->reply_size = -2; | 421 | return ws->reply_size = -2; |
| 309 | } | 422 | } |
| 310 | #endif | 423 | #endif |
| 311 | 424 | ||
| 312 | static ssize_t http_handle_scrape( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | 425 | static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { |
| 313 | static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } }; | 426 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; |
| 314 | 427 | ||
| 315 | ot_hash * multiscrape_buf = (ot_hash*)ws->request; | 428 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; |
| 316 | int scanon = 1, numwant = 0; | 429 | int scanon = 1; |
| 430 | size_t numwant = 0; | ||
| 317 | 431 | ||
| 318 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ | 432 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ |
| 319 | if( read_ptr[-1] != '?' ) { | 433 | if (read_ptr[-1] != '?') { |
| 320 | while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; | 434 | while ((*read_ptr != '?') && (*read_ptr != '\n')) |
| 321 | if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; | 435 | ++read_ptr; |
| 436 | if (*read_ptr == '\n') | ||
| 437 | HTTPERROR_400_PARAM; | ||
| 322 | ++read_ptr; | 438 | ++read_ptr; |
| 323 | } | 439 | } |
| 324 | 440 | ||
| 325 | while( scanon ) { | 441 | while (scanon) { |
| 326 | switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { | 442 | switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
| 327 | case -2: scanon = 0; break; /* TERMINATOR */ | 443 | case -2: |
| 328 | default: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 444 | scanon = 0; |
| 329 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; | 445 | break; /* TERMINATOR */ |
| 330 | case 1: /* matched "info_hash" */ | 446 | default: |
| 447 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | ||
| 448 | case -3: | ||
| 449 | scan_urlencoded_skipvalue(&read_ptr); | ||
| 450 | break; | ||
| 451 | case 1: /* matched "info_hash" */ | ||
| 331 | /* ignore this, when we have less than 20 bytes */ | 452 | /* ignore this, when we have less than 20 bytes */ |
| 332 | if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) | 453 | if (scan_urlencoded_query(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash)) |
| 333 | HTTPERROR_400_PARAM; | 454 | HTTPERROR_400_PARAM; |
| 334 | break; | 455 | break; |
| 335 | } | 456 | } |
| 336 | } | 457 | } |
| 337 | 458 | ||
| 338 | /* No info_hash found? Inform user */ | 459 | /* No info_hash found? Inform user */ |
| 339 | if( !numwant ) HTTPERROR_400_PARAM; | 460 | if (!numwant) |
| 461 | HTTPERROR_400_PARAM; | ||
| 340 | 462 | ||
| 341 | /* Limit number of hashes to process */ | 463 | /* Limit number of hashes to process */ |
| 342 | if( numwant > OT_MAXMULTISCRAPE_COUNT ) | 464 | if (numwant > OT_MAXMULTISCRAPE_COUNT) |
| 343 | numwant = OT_MAXMULTISCRAPE_COUNT; | 465 | numwant = OT_MAXMULTISCRAPE_COUNT; |
| 344 | 466 | ||
| 345 | /* Enough for http header + whole scrape string */ | 467 | /* Enough for http header + whole scrape string */ |
| 346 | ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ); | 468 | ws->reply_size = return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, ws->reply); |
| 347 | stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size ); | 469 | stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size); |
| 348 | return ws->reply_size; | 470 | return ws->reply_size; |
| 349 | } | 471 | } |
| 350 | 472 | ||
| 351 | #ifdef WANT_LOG_NUMWANT | 473 | #ifdef WANT_LOG_NUMWANT |
| 352 | unsigned long long numwants[201]; | 474 | unsigned long long numwants[201]; |
| 353 | #endif | 475 | #endif |
| 354 | 476 | ||
| 355 | #if defined( WANT_KEEPALIVE ) || defined( WANT_IP_FROM_PROXY ) | 477 | #if defined(WANT_KEEPALIVE) || defined(WANT_IP_FROM_PROXY) |
| 356 | static char* http_header( char *data, size_t byte_count, char *header ) { | 478 | static char *http_header(char *data, size_t byte_count, char *header) { |
| 357 | size_t i; | 479 | size_t i; |
| 358 | long sl = strlen( header ); | 480 | long sl = strlen(header); |
| 359 | for( i = 0; i + sl + 2 < byte_count; ++i ) { | 481 | for (i = 0; i + sl + 2 < byte_count; ++i) { |
| 360 | if( data[i] != '\n' || data[ i + sl + 1] != ':' ) continue; | 482 | if (data[i] != '\n' || data[i + sl + 1] != ':') |
| 361 | if( !case_equalb( data + i + 1, sl, header ) ) continue; | 483 | continue; |
| 484 | if (!case_equalb(data + i + 1, sl, header)) | ||
| 485 | continue; | ||
| 362 | data += i + sl + 2; | 486 | data += i + sl + 2; |
| 363 | while( *data == ' ' || *data == '\t' ) ++data; | 487 | while (*data == ' ' || *data == '\t') |
| 488 | ++data; | ||
| 364 | return data; | 489 | return data; |
| 365 | } | 490 | } |
| 366 | return 0; | 491 | return 0; |
| 367 | } | 492 | } |
| 368 | #endif | 493 | #endif |
| 369 | 494 | ||
| 370 | static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "compact6", 5 }, { "info_hash", 6 }, | 495 | static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event", 3}, {"numwant", 4}, {"compact", 5}, {"compact6", 5}, {"info_hash", 6}, |
| 371 | #ifdef WANT_IP_FROM_QUERY_STRING | 496 | #ifdef WANT_IP_FROM_QUERY_STRING |
| 372 | { "ip", 7 }, | 497 | {"ip", 7}, |
| 373 | #endif | 498 | #endif |
| 374 | #ifdef WANT_FULLLOG_NETWORKS | 499 | #ifdef WANT_FULLLOG_NETWORKS |
| 375 | { "lognet", 8 }, | 500 | {"lognet", 8}, |
| 376 | #endif | 501 | #endif |
| 377 | { "peer_id", 9 }, | 502 | {"peer_id", 9}, {NULL, -3}}; |
| 378 | { NULL, -3 } }; | 503 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; |
| 379 | static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } }; | 504 | static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { |
| 380 | static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { | 505 | size_t numwant; |
| 381 | int numwant, tmp, scanon; | 506 | int tmp, scanon; |
| 382 | unsigned short port = 0; | 507 | unsigned short port = 0; |
| 383 | char *write_ptr; | 508 | char *write_ptr; |
| 384 | ssize_t len; | 509 | ssize_t len; |
| 385 | struct http_data *cookie = io_getcookie( sock ); | 510 | struct http_data *cookie = io_getcookie(sock); |
| 386 | 511 | ||
| 387 | /* This is to hack around stupid clients that send "announce ?info_hash" */ | 512 | /* This is to hack around stupid clients that send "announce ?info_hash" */ |
| 388 | if( read_ptr[-1] != '?' ) { | 513 | if (read_ptr[-1] != '?') { |
| 389 | while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; | 514 | while ((*read_ptr != '?') && (*read_ptr != '\n')) |
| 390 | if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; | 515 | ++read_ptr; |
| 516 | if (*read_ptr == '\n') | ||
| 517 | HTTPERROR_400_PARAM; | ||
| 391 | ++read_ptr; | 518 | ++read_ptr; |
| 392 | } | 519 | } |
| 393 | 520 | ||
| 394 | #ifdef WANT_IP_FROM_PROXY | 521 | #ifdef WANT_IP_FROM_PROXY |
| 395 | if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) { | 522 | if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { |
| 396 | ot_ip6 proxied_ip; | 523 | ot_ip6 proxied_ip; |
| 397 | char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" ); | 524 | char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); |
| 398 | if( fwd && scan_ip6( fwd, proxied_ip ) ) | 525 | if (fwd && scan_ip6(fwd, proxied_ip)) { |
| 399 | OT_SETIP( &ws->peer, proxied_ip ); | 526 | OT_SETIP(ws->peer, proxied_ip); |
| 400 | else | 527 | } else |
| 401 | OT_SETIP( &ws->peer, cookie->ip ); | 528 | OT_SETIP(ws->peer, cookie->ip); |
| 402 | } else | 529 | } else |
| 403 | #endif | 530 | #endif |
| 404 | OT_SETIP( &ws->peer, cookie->ip ); | 531 | OT_SETIP(ws->peer, cookie->ip); |
| 405 | 532 | ||
| 406 | ws->peer_id = NULL; | 533 | ws->peer_id = NULL; |
| 407 | ws->hash = NULL; | 534 | ws->hash = NULL; |
| 408 | 535 | ||
| 409 | OT_SETPORT( &ws->peer, &port ); | 536 | OT_SETPORT(ws->peer, &port); |
| 410 | OT_PEERFLAG( &ws->peer ) = 0; | 537 | OT_PEERFLAG(ws->peer) = 0; |
| 411 | numwant = 50; | 538 | numwant = 50; |
| 412 | scanon = 1; | 539 | scanon = 1; |
| 413 | 540 | ||
| 414 | while( scanon ) { | 541 | while (scanon) { |
| 415 | switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { | 542 | switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { |
| 416 | case -2: scanon = 0; break; /* TERMINATOR */ | 543 | case -2: |
| 417 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 544 | scanon = 0; |
| 418 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; | 545 | break; /* TERMINATOR */ |
| 546 | case -1: | ||
| 547 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | ||
| 548 | case -3: | ||
| 549 | scan_urlencoded_skipvalue(&read_ptr); | ||
| 550 | break; | ||
| 419 | case 1: /* matched "port" */ | 551 | case 1: /* matched "port" */ |
| 420 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | 552 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
| 421 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; | 553 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff)) |
| 422 | port = htons( tmp ); OT_SETPORT( &ws->peer, &port ); | 554 | HTTPERROR_400_PARAM; |
| 555 | port = htons(tmp); | ||
| 556 | OT_SETPORT(&ws->peer, &port); | ||
| 423 | break; | 557 | break; |
| 424 | case 2: /* matched "left" */ | 558 | case 2: /* matched "left" */ |
| 425 | if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; | 559 | if ((len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) |
| 426 | if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; | 560 | HTTPERROR_400_PARAM; |
| 427 | if( !tmp ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; | 561 | if (scan_fixed_int(write_ptr, len, &tmp)) |
| 562 | tmp = 0; | ||
| 563 | if (!tmp) | ||
| 564 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; | ||
| 428 | break; | 565 | break; |
| 429 | case 3: /* matched "event" */ | 566 | case 3: /* matched "event" */ |
| 430 | switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { | 567 | switch (scan_find_keywords(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { |
| 431 | case -1: HTTPERROR_400_PARAM; | 568 | case -1: |
| 432 | case 1: /* matched "completed" */ | 569 | HTTPERROR_400_PARAM; |
| 433 | OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; | 570 | case 1: /* matched "completed" */ |
| 434 | break; | 571 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_COMPLETED; |
| 435 | case 2: /* matched "stopped" */ | 572 | break; |
| 436 | OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; | 573 | case 2: /* matched "stopped" */ |
| 437 | break; | 574 | OT_PEERFLAG(&ws->peer) |= PEER_FLAG_STOPPED; |
| 438 | default: | 575 | break; |
| 439 | break; | 576 | default: |
| 577 | break; | ||
| 440 | } | 578 | } |
| 441 | break; | 579 | break; |
| 442 | case 4: /* matched "numwant" */ | 580 | case 4: /* matched "numwant" */ |
| 443 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | 581 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
| 444 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; | 582 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) |
| 445 | if( numwant < 0 ) numwant = 50; | 583 | HTTPERROR_400_PARAM; |
| 446 | if( numwant > 200 ) numwant = 200; | 584 | if (tmp < 0) |
| 585 | tmp = 50; | ||
| 586 | if (tmp > 200) | ||
| 587 | tmp = 200; | ||
| 588 | numwant = tmp; | ||
| 447 | break; | 589 | break; |
| 448 | case 5: /* matched "compact" */ | 590 | case 5: /* matched "compact" */ |
| 449 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | 591 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); |
| 450 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; | 592 | if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) |
| 451 | if( !tmp ) HTTPERROR_400_COMPACT; | 593 | HTTPERROR_400_PARAM; |
| 594 | if (!tmp) | ||
| 595 | HTTPERROR_400_COMPACT; | ||
| 452 | break; | 596 | break; |
| 453 | case 6: /* matched "info_hash" */ | 597 | case 6: /* matched "info_hash" */ |
| 454 | if( ws->hash ) HTTPERROR_400_DOUBLEHASH; | 598 | if (ws->hash) |
| 599 | HTTPERROR_400_DOUBLEHASH; | ||
| 455 | /* ignore this, when we have less than 20 bytes */ | 600 | /* ignore this, when we have less than 20 bytes */ |
| 456 | if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; | 601 | if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) |
| 457 | ws->hash = (ot_hash*)write_ptr; | 602 | HTTPERROR_400_PARAM; |
| 603 | ws->hash = (ot_hash *)write_ptr; | ||
| 458 | break; | 604 | break; |
| 459 | #ifdef WANT_IP_FROM_QUERY_STRING | 605 | #ifdef WANT_IP_FROM_QUERY_STRING |
| 460 | case 7: /* matched "ip" */ | 606 | case 7: /* matched "ip" */ |
| 461 | { | 607 | { |
| 462 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16; | 608 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; |
| 463 | len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE ); | 609 | len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); |
| 464 | tmp_buf2[len] = 0; | 610 | tmp_buf2[len] = 0; |
| 465 | if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM; | 611 | if ((len <= 0) || !scan_ip6(tmp_buf2, tmp_buf1)) |
| 466 | OT_SETIP( &ws->peer, tmp_buf1 ); | 612 | HTTPERROR_400_PARAM; |
| 467 | } | 613 | OT_SETIP(&ws->peer, tmp_buf1); |
| 468 | break; | 614 | } break; |
| 469 | #endif | 615 | #endif |
| 470 | #ifdef WANT_FULLLOG_NETWORKS | 616 | #ifdef WANT_FULLLOG_NETWORKS |
| 471 | case 8: /* matched "lognet" */ | 617 | case 8: /* matched "lognet" */ |
| 472 | { | 618 | { |
| 473 | //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { | 619 | // if( accesslist_is_blessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { |
| 474 | char *tmp_buf = ws->reply; | 620 | char *tmp_buf = ws->reply; |
| 475 | ot_net net; | 621 | ot_net net; |
| 476 | signed short parsed, bits; | 622 | signed short parsed, bits; |
| 477 | 623 | ||
| 478 | len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE ); | 624 | len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE); |
| 479 | tmp_buf[len] = 0; | 625 | tmp_buf[len] = 0; |
| 480 | if( len <= 0 ) HTTPERROR_400_PARAM; | 626 | if (len <= 0) |
| 481 | if( *tmp_buf == '-' ) { | 627 | HTTPERROR_400_PARAM; |
| 482 | loglist_reset( ); | 628 | if (*tmp_buf == '-') { |
| 483 | return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" ); | 629 | loglist_reset(); |
| 484 | } | 630 | return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); |
| 485 | parsed = scan_ip6( tmp_buf, net.address ); | ||
| 486 | if( !parsed ) HTTPERROR_400_PARAM; | ||
| 487 | if( tmp_buf[parsed++] != '/' ) | ||
| 488 | bits = 128; | ||
| 489 | else { | ||
| 490 | parsed = scan_short( tmp_buf + parsed, &bits ); | ||
| 491 | if( !parsed ) HTTPERROR_400_PARAM; | ||
| 492 | if( ip6_isv4mapped( net.address ) ) | ||
| 493 | bits += 96; | ||
| 494 | } | ||
| 495 | net.bits = bits; | ||
| 496 | loglist_add_network( &net ); | ||
| 497 | return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" ); | ||
| 498 | //} | ||
| 499 | } | 631 | } |
| 632 | parsed = scan_ip6(tmp_buf, net.address); | ||
| 633 | if (!parsed) | ||
| 634 | HTTPERROR_400_PARAM; | ||
| 635 | if (tmp_buf[parsed++] != '/') | ||
| 636 | bits = 128; | ||
| 637 | else { | ||
| 638 | parsed = scan_short(tmp_buf + parsed, &bits); | ||
| 639 | if (!parsed) | ||
| 640 | HTTPERROR_400_PARAM; | ||
| 641 | if (ip6_isv4mapped(net.address)) | ||
| 642 | bits += 96; | ||
| 643 | } | ||
| 644 | net.bits = bits; | ||
| 645 | loglist_add_network(&net); | ||
| 646 | return ws->reply_size = sprintf(ws->reply, "Successfully added.\n"); | ||
| 647 | //} | ||
| 648 | } break; | ||
| 500 | #endif | 649 | #endif |
| 501 | break; | 650 | case 9: /* matched "peer_id" */ |
| 502 | case 9: /* matched "peer_id" */ | 651 | /* ignore this, when we have less than 20 bytes */ |
| 503 | /* ignore this, when we have less than 20 bytes */ | 652 | if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) |
| 504 | if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; | 653 | HTTPERROR_400_PARAM; |
| 505 | ws->peer_id = write_ptr; | 654 | ws->peer_id = write_ptr; |
| 506 | break; | 655 | break; |
| 507 | } | 656 | } |
| 508 | } | 657 | } |
| 509 | 658 | ||
| @@ -516,100 +665,107 @@ static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, | |||
| 516 | */ | 665 | */ |
| 517 | 666 | ||
| 518 | /* Scanned whole query string */ | 667 | /* Scanned whole query string */ |
| 519 | if( !ws->hash ) | 668 | if (!ws->hash) |
| 520 | 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" ); | 669 | 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"); |
| 521 | 670 | ||
| 522 | if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) | 671 | if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED) |
| 523 | ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws ); | 672 | ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws); |
| 524 | else | 673 | else |
| 525 | ws->reply_size = add_peer_to_torrent_and_return_peers( FLAG_TCP, ws, numwant ); | 674 | ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, numwant); |
| 526 | 675 | ||
| 527 | stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); | 676 | stats_issue_event(EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); |
| 528 | return ws->reply_size; | 677 | return ws->reply_size; |
| 529 | } | 678 | } |
| 530 | 679 | ||
| 531 | ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { | 680 | ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) { |
| 532 | ssize_t reply_off, len; | 681 | ssize_t reply_off, len; |
| 533 | char *read_ptr = ws->request, *write_ptr; | 682 | char *read_ptr = ws->request, *write_ptr; |
| 534 | 683 | ||
| 535 | #ifdef WANT_FULLLOG_NETWORKS | 684 | #ifdef WANT_FULLLOG_NETWORKS |
| 536 | struct http_data *cookie = io_getcookie( sock ); | 685 | struct http_data *cookie = io_getcookie(sock); |
| 537 | if( loglist_check_address( cookie->ip ) ) { | 686 | if (loglist_check_address(cookie->ip)) { |
| 538 | ot_log *log = malloc( sizeof( ot_log ) ); | 687 | ot_log *log = malloc(sizeof(ot_log)); |
| 539 | if( log ) { | 688 | if (log) { |
| 540 | log->size = ws->request_size; | 689 | log->size = ws->request_size; |
| 541 | log->data = malloc( ws->request_size ); | 690 | log->data = malloc(ws->request_size); |
| 542 | log->next = 0; | 691 | log->next = 0; |
| 543 | log->time = g_now_seconds; | 692 | log->time = g_now_seconds; |
| 544 | memcpy( log->ip, cookie->ip, sizeof(ot_ip6)); | 693 | memcpy(log->ip, cookie->ip, sizeof(ot_ip6)); |
| 545 | if( log->data ) { | 694 | if (log->data) { |
| 546 | memcpy( log->data, ws->request, ws->request_size ); | 695 | memcpy(log->data, ws->request, ws->request_size); |
| 547 | if( !g_logchain_first ) | 696 | if (!g_logchain_first) |
| 548 | g_logchain_first = g_logchain_last = log; | 697 | g_logchain_first = g_logchain_last = log; |
| 549 | else { | 698 | else { |
| 550 | g_logchain_last->next = log; | 699 | g_logchain_last->next = log; |
| 551 | g_logchain_last = log; | 700 | g_logchain_last = log; |
| 552 | } | 701 | } |
| 553 | } else | 702 | } else |
| 554 | free( log ); | 703 | free(log); |
| 555 | } | 704 | } |
| 556 | } | 705 | } |
| 557 | #endif | 706 | #endif |
| 558 | 707 | ||
| 559 | #ifdef _DEBUG_HTTPERROR | 708 | #ifdef _DEBUG_HTTPERROR |
| 560 | reply_off = ws->request_size; | 709 | reply_off = ws->request_size; |
| 561 | if( ws->request_size >= G_DEBUGBUF_SIZE ) | 710 | if (ws->request_size >= G_DEBUGBUF_SIZE) |
| 562 | reply_off = G_DEBUGBUF_SIZE - 1; | 711 | reply_off = G_DEBUGBUF_SIZE - 1; |
| 563 | memcpy( ws->debugbuf, ws->request, reply_off ); | 712 | memcpy(ws->debugbuf, ws->request, reply_off); |
| 564 | ws->debugbuf[ reply_off ] = 0; | 713 | ws->debugbuf[reply_off] = 0; |
| 565 | #endif | 714 | #endif |
| 566 | 715 | ||
| 567 | /* Tell subroutines where to put reply data */ | 716 | /* Tell subroutines where to put reply data */ |
| 568 | ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; | 717 | ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; |
| 569 | 718 | ||
| 570 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ | 719 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ |
| 571 | if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400; | 720 | if (memcmp(read_ptr, "GET /", 5)) |
| 721 | HTTPERROR_400; | ||
| 572 | 722 | ||
| 573 | /* Skip leading '/' */ | 723 | /* Skip leading '/' */ |
| 574 | for( read_ptr+=4; *read_ptr == '/'; ++read_ptr); | 724 | for (read_ptr += 4; *read_ptr == '/'; ++read_ptr) |
| 725 | ; | ||
| 575 | 726 | ||
| 576 | /* Try to parse the request. | 727 | /* Try to parse the request. |
| 577 | In reality we abandoned requiring the url to be correct. This now | 728 | In reality we abandoned requiring the url to be correct. This now |
| 578 | only decodes url encoded characters, we check for announces and | 729 | only decodes url encoded characters, we check for announces and |
| 579 | scrapes by looking for "a*" or "sc" */ | 730 | scrapes by looking for "a*" or "sc" */ |
| 580 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH ); | 731 | len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_PATH); |
| 581 | 732 | ||
| 582 | /* If parsing returned an error, leave with not found */ | 733 | /* If parsing returned an error, leave with not found */ |
| 583 | if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; | 734 | if (g_redirecturl && (len == -2)) |
| 584 | if( len <= 0 ) HTTPERROR_404; | 735 | HTTPERROR_302; |
| 736 | if (len <= 0) | ||
| 737 | HTTPERROR_404; | ||
| 585 | 738 | ||
| 586 | /* This is the hardcore match for announce*/ | 739 | /* This is the hardcore match for announce*/ |
| 587 | if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) ) | 740 | if ((*write_ptr == 'a') || (*write_ptr == '?')) |
| 588 | http_handle_announce( sock, ws, read_ptr ); | 741 | http_handle_announce(sock, ws, read_ptr); |
| 589 | #ifdef WANT_FULLSCRAPE | 742 | #ifdef WANT_FULLSCRAPE |
| 590 | else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) ) | 743 | else if (!memcmp(write_ptr, "scrape HTTP/", 12)) |
| 591 | http_handle_fullscrape( sock, ws ); | 744 | http_handle_fullscrape(sock, ws); |
| 592 | #endif | 745 | #endif |
| 593 | /* This is the hardcore match for scrape */ | 746 | /* This is the hardcore match for scrape */ |
| 594 | else if( !memcmp( write_ptr, "sc", 2 ) ) | 747 | else if (!memcmp(write_ptr, "sc", 2)) |
| 595 | http_handle_scrape( sock, ws, read_ptr ); | 748 | http_handle_scrape(sock, ws, read_ptr); |
| 596 | /* All the rest is matched the standard way */ | 749 | /* All the rest is matched the standard way */ |
| 597 | else if( len == g_stats_path_len && !memcmp( write_ptr, g_stats_path, len ) ) | 750 | else if (len == g_stats_path_len && !memcmp(write_ptr, g_stats_path, len)) |
| 598 | http_handle_stats( sock, ws, read_ptr ); | 751 | http_handle_stats(sock, ws, read_ptr); |
| 599 | else | 752 | else |
| 600 | HTTPERROR_404; | 753 | HTTPERROR_404; |
| 601 | 754 | ||
| 602 | /* Find out if the client wants to keep this connection alive */ | 755 | /* Find out if the client wants to keep this connection alive */ |
| 603 | ws->keep_alive = 0; | 756 | ws->keep_alive = 0; |
| 604 | #ifdef WANT_KEEPALIVE | 757 | #ifdef WANT_KEEPALIVE |
| 605 | read_ptr=http_header( ws->request, ws->header_size, "connection"); | 758 | read_ptr = http_header(ws->request, ws->header_size, "connection"); |
| 606 | if( read_ptr && ( *read_ptr == 'K' || *read_ptr == 'k' ) ) ws->keep_alive = 1; | 759 | if (read_ptr && (*read_ptr == 'K' || *read_ptr == 'k')) |
| 760 | ws->keep_alive = 1; | ||
| 607 | #endif | 761 | #endif |
| 608 | 762 | ||
| 609 | /* If routines handled sending themselves, just return */ | 763 | /* If routines handled sending themselves, just return */ |
| 610 | if( ws->reply_size == -2 ) return 0; | 764 | if (ws->reply_size == -2) |
| 765 | return 0; | ||
| 611 | /* If routine failed, let http error take over */ | 766 | /* If routine failed, let http error take over */ |
| 612 | if( ws->reply_size <= 0 ) HTTPERROR_500; | 767 | if (ws->reply_size <= 0) |
| 768 | HTTPERROR_500; | ||
| 613 | 769 | ||
| 614 | /* This one is rather ugly, so I take you step by step through it. | 770 | /* This one is rather ugly, so I take you step by step through it. |
| 615 | 771 | ||
| @@ -618,18 +774,16 @@ ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) { | |||
| 618 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate | 774 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate |
| 619 | the space NOT needed to expand in reply_off | 775 | the space NOT needed to expand in reply_off |
| 620 | */ | 776 | */ |
| 621 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size ); | 777 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size); |
| 622 | ws->reply = ws->outbuf + reply_off; | 778 | ws->reply = ws->outbuf + reply_off; |
| 623 | 779 | ||
| 624 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 780 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete |
| 625 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 781 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ |
| 626 | ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size ); | 782 | ws->reply_size += 1 + sprintf(ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size); |
| 627 | 783 | ||
| 628 | /* 3. Finally we join both blocks neatly */ | 784 | /* 3. Finally we join both blocks neatly */ |
| 629 | ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; | 785 | ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n'; |
| 630 | 786 | ||
| 631 | http_senddata( sock, ws ); | 787 | http_senddata(sock, ws); |
| 632 | return ws->reply_size; | 788 | return ws->reply_size; |
| 633 | } | 789 | } |
| 634 | |||
| 635 | const char *g_version_http_c = "$Source$: $Revision$\n"; | ||
