diff options
Diffstat (limited to 'ot_http.c')
| -rw-r--r-- | ot_http.c | 78 |
1 files changed, 58 insertions, 20 deletions
| @@ -24,6 +24,8 @@ | |||
| 24 | #include "ot_scan.h" | 24 | #include "ot_scan.h" |
| 25 | #include "ot_event.h" | 25 | #include "ot_event.h" |
| 26 | #include "ot_fdstate.h" | 26 | #include "ot_fdstate.h" |
| 27 | #include "ot_vector.h" | ||
| 28 | #include "ot_fmt.h" | ||
| 27 | #include "ot_stats.h" | 29 | #include "ot_stats.h" |
| 28 | #include "ot_urlencoded.h" | 30 | #include "ot_urlencoded.h" |
| 29 | #include "trackerlogic.h" | 31 | #include "trackerlogic.h" |
| @@ -232,7 +234,6 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 232 | {"scrp", TASK_STATS_SCRAPE}, | 234 | {"scrp", TASK_STATS_SCRAPE}, |
| 233 | {"udp4", TASK_STATS_UDP}, | 235 | {"udp4", TASK_STATS_UDP}, |
| 234 | {"tcp4", TASK_STATS_TCP}, | 236 | {"tcp4", TASK_STATS_TCP}, |
| 235 | {"busy", TASK_STATS_BUSY_NETWORKS}, | ||
| 236 | {"torr", TASK_STATS_TORRENTS}, | 237 | {"torr", TASK_STATS_TORRENTS}, |
| 237 | {"fscr", TASK_STATS_FULLSCRAPE}, | 238 | {"fscr", TASK_STATS_FULLSCRAPE}, |
| 238 | {"s24s", TASK_STATS_SLASH24S}, | 239 | {"s24s", TASK_STATS_SLASH24S}, |
| @@ -257,6 +258,7 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 257 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; | 258 | {"txtp", TASK_FULLSCRAPE_TPB_ASCII_PLUS}, {NULL, -3}}; |
| 258 | 259 | ||
| 259 | struct http_data *cookie = fdstate_cookie(sock); | 260 | struct http_data *cookie = fdstate_cookie(sock); |
| 261 | char *reply; | ||
| 260 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; | 262 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; |
| 261 | 263 | ||
| 262 | /* A socket without one is a dead socket, which is what http_sendiovecdata | 264 | /* A socket without one is a dead socket, which is what http_sendiovecdata |
| @@ -333,7 +335,9 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c | |||
| 333 | } | 335 | } |
| 334 | 336 | ||
| 335 | /* Simple stats can be answerred immediately */ | 337 | /* Simple stats can be answerred immediately */ |
| 336 | return ws->reply_size = return_stats_for_tracker(ws->reply, mode, 0); | 338 | reply = ws->reply; |
| 339 | return_stats_for_tracker(&reply, ws->outbuf + G_OUTBUF_SIZE, mode, 0); | ||
| 340 | return ws->reply_size = reply - ws->reply; | ||
| 337 | } | 341 | } |
| 338 | 342 | ||
| 339 | #ifdef WANT_MODEST_FULLSCRAPES | 343 | #ifdef WANT_MODEST_FULLSCRAPES |
| @@ -414,12 +418,15 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 414 | } | 418 | } |
| 415 | #endif | 419 | #endif |
| 416 | 420 | ||
| 421 | static int compare_hash(const void *left, const void *right) { return memcmp(left, right, sizeof(ot_hash)); } | ||
| 422 | |||
| 417 | static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { | 423 | static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 418 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; | 424 | static const ot_keywords keywords_scrape[] = {{"info_hash", 1}, {NULL, -3}}; |
| 419 | 425 | ||
| 420 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; | 426 | ot_hash *multiscrape_buf = (ot_hash *)ws->request; |
| 421 | int scanon = 1; | 427 | char *reply = ws->reply; |
| 422 | size_t numwant = 0; | 428 | int scanon = 1; |
| 429 | size_t i, unique, numwant = 0; | ||
| 423 | 430 | ||
| 424 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ | 431 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ |
| 425 | if (read_ptr[-1] != '?') { | 432 | if (read_ptr[-1] != '?') { |
| @@ -456,8 +463,17 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, | |||
| 456 | if (numwant > OT_MAXMULTISCRAPE_COUNT) | 463 | if (numwant > OT_MAXMULTISCRAPE_COUNT) |
| 457 | numwant = OT_MAXMULTISCRAPE_COUNT; | 464 | numwant = OT_MAXMULTISCRAPE_COUNT; |
| 458 | 465 | ||
| 466 | /* A bencoded dictionary has its keys in order and names each one once, so | ||
| 467 | the answer cannot just follow the order the client asked in */ | ||
| 468 | qsort(multiscrape_buf, numwant, sizeof(ot_hash), compare_hash); | ||
| 469 | for (i = unique = 1; i < numwant; ++i) | ||
| 470 | if (memcmp(multiscrape_buf[i], multiscrape_buf[unique - 1], sizeof(ot_hash))) | ||
| 471 | memcpy(multiscrape_buf[unique++], multiscrape_buf[i], sizeof(ot_hash)); | ||
| 472 | numwant = unique; | ||
| 473 | |||
| 459 | /* Enough for http header + whole scrape string */ | 474 | /* Enough for http header + whole scrape string */ |
| 460 | ws->reply_size = return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, ws->reply); | 475 | return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, &reply, ws->outbuf + G_OUTBUF_SIZE); |
| 476 | ws->reply_size = reply - ws->reply; | ||
| 461 | stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size); | 477 | stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size); |
| 462 | return ws->reply_size; | 478 | return ws->reply_size; |
| 463 | } | 479 | } |
| @@ -493,6 +509,16 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event", | |||
| 493 | #endif | 509 | #endif |
| 494 | {"peer_id", 9}, {NULL, -3}}; | 510 | {"peer_id", 9}, {NULL, -3}}; |
| 495 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; | 511 | static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; |
| 512 | |||
| 513 | /* For the replies that are one fixed string and nothing else, where the only | ||
| 514 | thing the caller wants back is how long it turned out */ | ||
| 515 | static ssize_t reply_literal(struct ot_workstruct *ws, const char *text) { | ||
| 516 | char *reply = ws->reply; | ||
| 517 | |||
| 518 | fmt_bytes(&reply, ws->outbuf + G_OUTBUF_SIZE, text, strlen(text)); | ||
| 519 | return reply - ws->reply; | ||
| 520 | } | ||
| 521 | |||
| 496 | static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { | 522 | static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { |
| 497 | size_t numwant; | 523 | size_t numwant; |
| 498 | int64_t tmp; | 524 | int64_t tmp; |
| @@ -621,7 +647,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 621 | HTTPERROR_400_PARAM; | 647 | HTTPERROR_400_PARAM; |
| 622 | if (*tmp_buf == '-') { | 648 | if (*tmp_buf == '-') { |
| 623 | loglist_reset(); | 649 | loglist_reset(); |
| 624 | return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); | 650 | return ws->reply_size = reply_literal(ws, "Successfully removed.\n"); |
| 625 | } | 651 | } |
| 626 | parsed = ip_parse(tmp_buf, net.address); | 652 | parsed = ip_parse(tmp_buf, net.address); |
| 627 | if (!parsed) | 653 | if (!parsed) |
| @@ -638,7 +664,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 638 | } | 664 | } |
| 639 | net.bits = bits; | 665 | net.bits = bits; |
| 640 | loglist_add_network(&net); | 666 | loglist_add_network(&net); |
| 641 | return ws->reply_size = sprintf(ws->reply, "Successfully added.\n"); | 667 | return ws->reply_size = reply_literal(ws, "Successfully added.\n"); |
| 642 | //} | 668 | //} |
| 643 | } break; | 669 | } break; |
| 644 | #endif | 670 | #endif |
| @@ -661,7 +687,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 661 | 687 | ||
| 662 | /* Scanned whole query string */ | 688 | /* Scanned whole query string */ |
| 663 | if (!ws->hash) | 689 | if (!ws->hash) |
| 664 | 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"); | 690 | return ws->reply_size = reply_literal(ws, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e"); |
| 665 | 691 | ||
| 666 | if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED) | 692 | if (OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED) |
| 667 | ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws); | 693 | ws->reply_size = remove_peer_from_torrent(FLAG_TCP, ws); |
| @@ -711,6 +737,24 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { | |||
| 711 | /* Tell subroutines where to put reply data */ | 737 | /* Tell subroutines where to put reply data */ |
| 712 | ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; | 738 | ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; |
| 713 | 739 | ||
| 740 | /* Find out if the client wants to keep this connection alive, before any | ||
| 741 | subroutine gets to answer: an error reply finishes with the connection | ||
| 742 | on its way out, and ws outlives the connection it was serving */ | ||
| 743 | ws->keep_alive = 0; | ||
| 744 | #ifdef WANT_KEEPALIVE | ||
| 745 | if ((write_ptr = http_header(ws->request, ws->header_size, "connection")) && (*write_ptr == 'K' || *write_ptr == 'k')) | ||
| 746 | ws->keep_alive = 1; | ||
| 747 | #endif | ||
| 748 | { | ||
| 749 | struct http_data *connection = fdstate_cookie(sock); | ||
| 750 | if (connection) { | ||
| 751 | if (ws->keep_alive) | ||
| 752 | connection->flag |= STRUCT_HTTP_FLAG_KEEPALIVE; | ||
| 753 | else | ||
| 754 | connection->flag &= ~STRUCT_HTTP_FLAG_KEEPALIVE; | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 714 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ | 758 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ |
| 715 | if (memcmp(read_ptr, "GET /", 5)) | 759 | if (memcmp(read_ptr, "GET /", 5)) |
| 716 | HTTPERROR_400; | 760 | HTTPERROR_400; |
| @@ -747,14 +791,6 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { | |||
| 747 | else | 791 | else |
| 748 | HTTPERROR_404; | 792 | HTTPERROR_404; |
| 749 | 793 | ||
| 750 | /* Find out if the client wants to keep this connection alive */ | ||
| 751 | ws->keep_alive = 0; | ||
| 752 | #ifdef WANT_KEEPALIVE | ||
| 753 | read_ptr = http_header(ws->request, ws->header_size, "connection"); | ||
| 754 | if (read_ptr && (*read_ptr == 'K' || *read_ptr == 'k')) | ||
| 755 | ws->keep_alive = 1; | ||
| 756 | #endif | ||
| 757 | |||
| 758 | /* If routines handled sending themselves, just return */ | 794 | /* If routines handled sending themselves, just return */ |
| 759 | if (ws->reply_size == -2) | 795 | if (ws->reply_size == -2) |
| 760 | return 0; | 796 | return 0; |
| @@ -772,9 +808,11 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { | |||
| 772 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size); | 808 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf(ws->outbuf, 0, "%zd", ws->reply_size); |
| 773 | ws->reply = ws->outbuf + reply_off; | 809 | ws->reply = ws->outbuf + reply_off; |
| 774 | 810 | ||
| 775 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 811 | /* 2. Now we write the header so that its terminating '\0' lands exactly one byte before the content starts. The bound is the |
| 776 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 812 | space step 1 reserved, so the '\0' falls where step 3 expects it. Complete packet size is increased by size of header plus |
| 777 | 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); | 813 | one byte '\n', we will copy over '\0' in next step */ |
| 814 | ws->reply_size += 1 + snprintf(ws->reply, (size_t)(SUCCESS_HTTP_HEADER_LENGTH - reply_off), | ||
| 815 | "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size); | ||
| 778 | 816 | ||
| 779 | /* 3. Finally we join both blocks neatly */ | 817 | /* 3. Finally we join both blocks neatly */ |
| 780 | ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n'; | 818 | ws->outbuf[SUCCESS_HTTP_HEADER_LENGTH - 1] = '\n'; |
