summaryrefslogtreecommitdiff
path: root/ot_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_http.c')
-rw-r--r--ot_http.c78
1 files changed, 58 insertions, 20 deletions
diff --git a/ot_http.c b/ot_http.c
index e1798dc..2216453 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -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
421static int compare_hash(const void *left, const void *right) { return memcmp(left, right, sizeof(ot_hash)); }
422
417static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { 423static 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}};
495static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; 511static 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 */
515static 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
496static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { 522static 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';