diff options
Diffstat (limited to 'ot_http.c')
| -rw-r--r-- | ot_http.c | 81 |
1 files changed, 49 insertions, 32 deletions
| @@ -15,15 +15,14 @@ | |||
| 15 | #include <unistd.h> | 15 | #include <unistd.h> |
| 16 | 16 | ||
| 17 | /* Libowfat */ | 17 | /* Libowfat */ |
| 18 | #include "array.h" | ||
| 19 | #include "iob.h" | 18 | #include "iob.h" |
| 20 | #include "ip6.h" | ||
| 21 | 19 | ||
| 22 | /* Opentracker */ | 20 | /* Opentracker */ |
| 23 | #include "ot_accesslist.h" | 21 | #include "ot_accesslist.h" |
| 24 | #include "ot_fullscrape.h" | 22 | #include "ot_fullscrape.h" |
| 25 | #include "ot_http.h" | 23 | #include "ot_http.h" |
| 26 | #include "ot_iovec.h" | 24 | #include "ot_iovec.h" |
| 25 | #include "ot_ip.h" | ||
| 27 | #include "ot_mutex.h" | 26 | #include "ot_mutex.h" |
| 28 | #include "ot_scan.h" | 27 | #include "ot_scan.h" |
| 29 | #include "ot_stats.h" | 28 | #include "ot_stats.h" |
| @@ -45,7 +44,7 @@ ssize_t g_stats_path_len; | |||
| 45 | 44 | ||
| 46 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; | 45 | enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; |
| 47 | 46 | ||
| 48 | static void http_senddata(const int64 sock, struct ot_workstruct *ws) { | 47 | static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { |
| 49 | struct http_data *cookie = io_getcookie(sock); | 48 | struct http_data *cookie = io_getcookie(sock); |
| 50 | ssize_t written_size; | 49 | ssize_t written_size; |
| 51 | 50 | ||
| @@ -54,20 +53,25 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) { | |||
| 54 | return; | 53 | return; |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | /* whoever sends data is not interested in its input-array */ | 56 | /* whoever sends data is not interested in its input buffer */ |
| 58 | if (ws->keep_alive && ws->header_size != ws->request_size) { | 57 | if (ws->keep_alive && ws->header_size != ws->request_size) { |
| 59 | size_t rest = ws->request_size - ws->header_size; | 58 | size_t rest = ws->request_size - ws->header_size; |
| 60 | if (array_start(&cookie->request)) { | 59 | if (cookie->request.data) { |
| 61 | memmove(array_start(&cookie->request), ws->request + ws->header_size, rest); | 60 | memmove(cookie->request.data, ws->request + ws->header_size, rest); |
| 62 | array_truncate(&cookie->request, 1, rest); | 61 | buf_truncate(&cookie->request, rest); |
| 63 | } else | 62 | } else if (buf_cat(&cookie->request, ws->request + ws->header_size, rest)) { |
| 64 | array_catb(&cookie->request, ws->request + ws->header_size, rest); | 63 | /* Without room for what the client pipelined behind this request, answer |
| 64 | this one and let the connection close, rather than keeping it open | ||
| 65 | around a request we have silently dropped */ | ||
| 66 | buf_reset(&cookie->request); | ||
| 67 | ws->keep_alive = 0; | ||
| 68 | } | ||
| 65 | } else | 69 | } else |
| 66 | array_reset(&cookie->request); | 70 | buf_reset(&cookie->request); |
| 67 | 71 | ||
| 68 | written_size = write(sock, ws->reply, ws->reply_size); | 72 | written_size = write(sock, ws->reply, ws->reply_size); |
| 69 | if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) { | 73 | if ((written_size < 0) || ((written_size == ws->reply_size) && !ws->keep_alive)) { |
| 70 | array_reset(&cookie->request); | 74 | buf_reset(&cookie->request); |
| 71 | free(cookie); | 75 | free(cookie); |
| 72 | io_close(sock); | 76 | io_close(sock); |
| 73 | return; | 77 | return; |
| @@ -78,7 +82,7 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) { | |||
| 78 | tai6464 t; | 82 | tai6464 t; |
| 79 | 83 | ||
| 80 | if (!(outbuf = malloc(ws->reply_size - written_size))) { | 84 | if (!(outbuf = malloc(ws->reply_size - written_size))) { |
| 81 | array_reset(&cookie->request); | 85 | buf_reset(&cookie->request); |
| 82 | free(cookie); | 86 | free(cookie); |
| 83 | io_close(sock); | 87 | io_close(sock); |
| 84 | return; | 88 | return; |
| @@ -90,7 +94,7 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) { | |||
| 90 | if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) { | 94 | if (!cookie->batch || OT_IOB_INIT(cookie->batch) == -1) { |
| 91 | free(cookie->batch); | 95 | free(cookie->batch); |
| 92 | free(outbuf); | 96 | free(outbuf); |
| 93 | array_reset(&cookie->request); | 97 | buf_reset(&cookie->request); |
| 94 | free(cookie); | 98 | free(cookie); |
| 95 | io_close(sock); | 99 | io_close(sock); |
| 96 | return; | 100 | return; |
| @@ -120,7 +124,7 @@ static void http_senddata(const int64 sock, struct ot_workstruct *ws) { | |||
| 120 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) | 124 | #define HTTPERROR_403_IP return http_issue_error(sock, ws, CODE_HTTPERROR_403_IP) |
| 121 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) | 125 | #define HTTPERROR_404 return http_issue_error(sock, ws, CODE_HTTPERROR_404) |
| 122 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) | 126 | #define HTTPERROR_500 return http_issue_error(sock, ws, CODE_HTTPERROR_500) |
| 123 | ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) { | 127 | ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) { |
| 124 | char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", | 128 | char *error_code[] = {"302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", "402 Payment Required", |
| 125 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; | 129 | "403 Not Modest", "403 Access Denied", "404 Not Found", "500 Internal Server Error"}; |
| 126 | char *title = error_code[code]; | 130 | char *title = error_code[code]; |
| @@ -140,7 +144,7 @@ ssize_t http_issue_error(const int64 sock, struct ot_workstruct *ws, int code) { | |||
| 140 | return ws->reply_size = -2; | 144 | return ws->reply_size = -2; |
| 141 | } | 145 | } |
| 142 | 146 | ||
| 143 | ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) { | 147 | ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial) { |
| 144 | struct http_data *cookie = io_getcookie(sock); | 148 | struct http_data *cookie = io_getcookie(sock); |
| 145 | io_batch *current; | 149 | io_batch *current; |
| 146 | char *header; | 150 | char *header; |
| @@ -154,8 +158,15 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t io | |||
| 154 | HTTPERROR_500; | 158 | HTTPERROR_500; |
| 155 | } | 159 | } |
| 156 | 160 | ||
| 161 | /* Descriptor numbers get recycled, so a result can only belong to this | ||
| 162 | connection if this connection is in fact waiting for one. */ | ||
| 163 | if (!(cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))) { | ||
| 164 | iovec_free(&iovec_entries, &iovector); | ||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 157 | /* If this socket collected request in a buffer, free it now */ | 168 | /* If this socket collected request in a buffer, free it now */ |
| 158 | array_reset(&cookie->request); | 169 | buf_reset(&cookie->request); |
| 159 | 170 | ||
| 160 | /* If we came here, wait for the answer is over */ | 171 | /* If we came here, wait for the answer is over */ |
| 161 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { | 172 | if (cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK) { |
| @@ -232,7 +243,7 @@ ssize_t http_sendiovecdata(const int64 sock, struct ot_workstruct *ws, size_t io | |||
| 232 | return 0; | 243 | return 0; |
| 233 | } | 244 | } |
| 234 | 245 | ||
| 235 | static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { | 246 | static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 236 | static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}}; | 247 | static const ot_keywords keywords_main[] = {{"mode", 1}, {"format", 2}, {"info_hash", 3}, {NULL, -3}}; |
| 237 | static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS}, | 248 | static const ot_keywords keywords_mode[] = {{"peer", TASK_STATS_PEERS}, |
| 238 | {"conn", TASK_STATS_CONNS}, | 249 | {"conn", TASK_STATS_CONNS}, |
| @@ -263,12 +274,16 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha | |||
| 263 | {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, | 274 | {"url", TASK_FULLSCRAPE_TPB_URLENCODED}, {"txt", TASK_FULLSCRAPE_TPB_ASCII}, |
| 264 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; | 275 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; |
| 265 | 276 | ||
| 266 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; | ||
| 267 | |||
| 268 | #ifdef WANT_RESTRICT_STATS | ||
| 269 | struct http_data *cookie = io_getcookie(sock); | 277 | struct http_data *cookie = io_getcookie(sock); |
| 278 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; | ||
| 279 | |||
| 280 | /* A socket without one is a dead socket, which is what http_sendiovecdata | ||
| 281 | already answers 500 to. */ | ||
| 282 | if (!cookie) | ||
| 283 | HTTPERROR_500; | ||
| 270 | 284 | ||
| 271 | if (!cookie || !accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT)) | 285 | #ifdef WANT_RESTRICT_STATS |
| 286 | if (!accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_STAT)) | ||
| 272 | HTTPERROR_403_IP; | 287 | HTTPERROR_403_IP; |
| 273 | #endif | 288 | #endif |
| 274 | 289 | ||
| @@ -302,10 +317,9 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha | |||
| 302 | } | 317 | } |
| 303 | 318 | ||
| 304 | if (mode == TASK_STATS_TPB) { | 319 | if (mode == TASK_STATS_TPB) { |
| 305 | struct http_data *cookie = io_getcookie(sock); | 320 | tai6464 t; |
| 306 | tai6464 t; | ||
| 307 | #ifdef WANT_COMPRESSION_GZIP | 321 | #ifdef WANT_COMPRESSION_GZIP |
| 308 | ws->request[ws->request_size] = 0; | 322 | ws->request[ws->request_size - 1] = 0; |
| 309 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS | 323 | #ifndef WANT_COMPRESSION_GZIP_ALWAYS |
| 310 | if (strstr(read_ptr - 1, "gzip")) { | 324 | if (strstr(read_ptr - 1, "gzip")) { |
| 311 | #endif | 325 | #endif |
| @@ -330,6 +344,9 @@ static ssize_t http_handle_stats(const int64 sock, struct ot_workstruct *ws, cha | |||
| 330 | /* default format for now */ | 344 | /* default format for now */ |
| 331 | if ((mode & TASK_CLASS_MASK) == TASK_STATS) { | 345 | if ((mode & TASK_CLASS_MASK) == TASK_STATS) { |
| 332 | tai6464 t; | 346 | tai6464 t; |
| 347 | |||
| 348 | cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK; | ||
| 349 | |||
| 333 | /* Complex stats also include expensive memory debugging tools */ | 350 | /* Complex stats also include expensive memory debugging tools */ |
| 334 | taia_uint(&t, 0); | 351 | taia_uint(&t, 0); |
| 335 | io_timeout(sock, t); | 352 | io_timeout(sock, t); |
| @@ -351,7 +368,7 @@ typedef struct { | |||
| 351 | #endif | 368 | #endif |
| 352 | 369 | ||
| 353 | #ifdef WANT_FULLSCRAPE | 370 | #ifdef WANT_FULLSCRAPE |
| 354 | static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws) { | 371 | static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct *ws) { |
| 355 | struct http_data *cookie = io_getcookie(sock); | 372 | struct http_data *cookie = io_getcookie(sock); |
| 356 | int format = 0; | 373 | int format = 0; |
| 357 | tai6464 t; | 374 | tai6464 t; |
| @@ -421,7 +438,7 @@ static ssize_t http_handle_fullscrape(const int64 sock, struct ot_workstruct *ws | |||
| 421 | } | 438 | } |
| 422 | #endif | 439 | #endif |
| 423 | 440 | ||
| 424 | static ssize_t http_handle_scrape(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { | 441 | static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 425 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; | 442 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; |
| 426 | 443 | ||
| 427 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; | 444 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; |
| @@ -500,7 +517,7 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event", | |||
| 500 | #endif | 517 | #endif |
| 501 | {"peer_id", 9}, {NULL, -3}}; | 518 | {"peer_id", 9}, {NULL, -3}}; |
| 502 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; | 519 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; |
| 503 | static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct *ws, char *read_ptr) { | 520 | static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 504 | size_t numwant; | 521 | size_t numwant; |
| 505 | int tmp, scanon; | 522 | int tmp, scanon; |
| 506 | unsigned short port = 0; | 523 | unsigned short port = 0; |
| @@ -521,7 +538,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct * | |||
| 521 | if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { | 538 | if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { |
| 522 | ot_ip6 proxied_ip; | 539 | ot_ip6 proxied_ip; |
| 523 | char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); | 540 | char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); |
| 524 | if (fwd && scan_ip6(fwd, proxied_ip)) { | 541 | if (fwd && ip_scan(fwd, proxied_ip)) { |
| 525 | OT_SETIP(ws->peer, proxied_ip); | 542 | OT_SETIP(ws->peer, proxied_ip); |
| 526 | } else | 543 | } else |
| 527 | OT_SETIP(ws->peer, cookie->ip); | 544 | OT_SETIP(ws->peer, cookie->ip); |
| @@ -607,7 +624,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct * | |||
| 607 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; | 624 | char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; |
| 608 | len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); | 625 | len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); |
| 609 | tmp_buf2[len] = 0; | 626 | tmp_buf2[len] = 0; |
| 610 | if ((len <= 0) || !scan_ip6(tmp_buf2, tmp_buf1)) | 627 | if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1)) |
| 611 | HTTPERROR_400_PARAM; | 628 | HTTPERROR_400_PARAM; |
| 612 | OT_SETIP(&ws->peer, tmp_buf1); | 629 | OT_SETIP(&ws->peer, tmp_buf1); |
| 613 | } break; | 630 | } break; |
| @@ -629,7 +646,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct * | |||
| 629 | loglist_reset(); | 646 | loglist_reset(); |
| 630 | return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); | 647 | return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); |
| 631 | } | 648 | } |
| 632 | parsed = scan_ip6(tmp_buf, net.address); | 649 | parsed = ip_scan(tmp_buf, net.address); |
| 633 | if (!parsed) | 650 | if (!parsed) |
| 634 | HTTPERROR_400_PARAM; | 651 | HTTPERROR_400_PARAM; |
| 635 | if (tmp_buf[parsed++] != '/') | 652 | if (tmp_buf[parsed++] != '/') |
| @@ -639,7 +656,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct * | |||
| 639 | if (!parsed) | 656 | if (!parsed) |
| 640 | HTTPERROR_400_PARAM; | 657 | HTTPERROR_400_PARAM; |
| 641 | bits = (signed short)value; | 658 | bits = (signed short)value; |
| 642 | if (ip6_isv4mapped(net.address)) | 659 | if (ip_is_v4mapped(net.address)) |
| 643 | bits += 96; | 660 | bits += 96; |
| 644 | } | 661 | } |
| 645 | net.bits = bits; | 662 | net.bits = bits; |
| @@ -678,7 +695,7 @@ static ssize_t http_handle_announce(const int64 sock, struct ot_workstruct * | |||
| 678 | return ws->reply_size; | 695 | return ws->reply_size; |
| 679 | } | 696 | } |
| 680 | 697 | ||
| 681 | ssize_t http_handle_request(const int64 sock, struct ot_workstruct *ws) { | 698 | ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { |
| 682 | ssize_t reply_off, len; | 699 | ssize_t reply_off, len; |
| 683 | char *read_ptr = ws->request, *write_ptr; | 700 | char *read_ptr = ws->request, *write_ptr; |
| 684 | 701 | ||
