|
@@ -0,0 +1,213 @@
|
|
|
+/* Copyright (c) 2017, The Tor Project, Inc. */
|
|
|
+/* See LICENSE for licensing information */
|
|
|
+
|
|
|
+/**
|
|
|
+ * \file test_proto_http.c
|
|
|
+ * \brief Tests for our HTTP protocol parser code
|
|
|
+ */
|
|
|
+
|
|
|
+#include "or.h"
|
|
|
+#include "test.h"
|
|
|
+#include "buffers.h"
|
|
|
+#include "proto_http.h"
|
|
|
+#include "log_test_helpers.h"
|
|
|
+
|
|
|
+#define S(str) str, sizeof(str)-1
|
|
|
+
|
|
|
+static void
|
|
|
+test_proto_http_peek(void *arg)
|
|
|
+{
|
|
|
+ (void) arg;
|
|
|
+ const struct {
|
|
|
+ int is_http;
|
|
|
+ const char *message;
|
|
|
+ size_t len;
|
|
|
+ } cases[] = {
|
|
|
+ { 1, S("GET /index HTTP/1.0\r\n") },
|
|
|
+ { 1, S("GET /index HTTP/1.1\r\n") },
|
|
|
+ { 1, S("GET ") },
|
|
|
+ { 0, S("GIT ") },
|
|
|
+ { 0, S("GET") },
|
|
|
+ { 0, S("get ") },
|
|
|
+ { 0, S("GETAWAY") },
|
|
|
+ };
|
|
|
+ unsigned i;
|
|
|
+ buf_t *buf = buf_new();
|
|
|
+ for (i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
|
|
+ TT_BLATHER(("Trying case %u", i));
|
|
|
+ buf_add(buf, cases[i].message, cases[i].len);
|
|
|
+ tt_int_op(cases[i].is_http, OP_EQ, peek_buf_has_http_command(buf));
|
|
|
+ buf_clear(buf);
|
|
|
+ }
|
|
|
+ done:
|
|
|
+ buf_free(buf);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+test_proto_http_valid(void *arg)
|
|
|
+{
|
|
|
+ (void) arg;
|
|
|
+ const struct {
|
|
|
+ const char *message;
|
|
|
+ size_t len;
|
|
|
+ const char *headers;
|
|
|
+ const char *body;
|
|
|
+ size_t bodylen;
|
|
|
+ int should_detect_truncated;
|
|
|
+ int bytes_left_over;
|
|
|
+ } cases[] = {
|
|
|
+ { S("GET /index.html HTTP/1.0\r\n\r\n"),
|
|
|
+ "GET /index.html HTTP/1.0\r\n\r\n",
|
|
|
+ S(""),
|
|
|
+ 1, 0,
|
|
|
+ },
|
|
|
+ { S("PUT /tor/foo HTTP/1.1\r\n"
|
|
|
+ "Content-Length: 51\r\n\r\n"
|
|
|
+ "this is a test of the http parsing system . test te"),
|
|
|
+ "PUT /tor/foo HTTP/1.1\r\n" "Content-Length: 51\r\n\r\n",
|
|
|
+ S("this is a test of the http parsing system . test te"),
|
|
|
+ 1, 0,
|
|
|
+ },
|
|
|
+ { S("PUT /tor/foo HTTP/1.1\r\n"
|
|
|
+ "Content-Length: 5\r\n\r\n"
|
|
|
+ "there are more than 5 characters in this body."),
|
|
|
+ "PUT /tor/foo HTTP/1.1\r\n" "Content-Length: 5\r\n\r\n",
|
|
|
+ S("there"),
|
|
|
+ 0, 41,
|
|
|
+ },
|
|
|
+ { S("PUT /tor/bar HTTP/1.1\r\n\r\n"
|
|
|
+ "this is another \x00test"),
|
|
|
+ "PUT /tor/bar HTTP/1.1\r\n\r\n",
|
|
|
+ S("this is another \x00test"),
|
|
|
+ 0, 0,
|
|
|
+ }
|
|
|
+ };
|
|
|
+ unsigned i;
|
|
|
+ buf_t *buf = buf_new();
|
|
|
+ char *h = NULL, *b = NULL;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
|
|
+ TT_BLATHER(("Trying case %u", i));
|
|
|
+ size_t bl = 0;
|
|
|
+ // truncate by 2 chars
|
|
|
+ buf_add(buf, cases[i].message, cases[i].len - 2);
|
|
|
+
|
|
|
+ if (cases[i].should_detect_truncated) {
|
|
|
+ tt_int_op(0, OP_EQ, fetch_from_buf_http(buf, &h, 1024*16,
|
|
|
+ &b, &bl, 1024*16, 0));
|
|
|
+ tt_ptr_op(h, OP_EQ, NULL);
|
|
|
+ tt_ptr_op(b, OP_EQ, NULL);
|
|
|
+ tt_u64_op(bl, OP_EQ, 0);
|
|
|
+ tt_int_op(buf_datalen(buf), OP_EQ, cases[i].len - 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ // add the rest.
|
|
|
+ buf_add(buf, cases[i].message+cases[i].len-2, 2);
|
|
|
+ tt_int_op(1, OP_EQ, fetch_from_buf_http(buf, &h, 1024*16,
|
|
|
+ &b, &bl, 1024*16, 0));
|
|
|
+ tt_str_op(h, OP_EQ, cases[i].headers);
|
|
|
+ tt_u64_op(bl, OP_EQ, cases[i].bodylen);
|
|
|
+ tt_mem_op(b, OP_EQ, cases[i].body, bl);
|
|
|
+ tt_int_op(buf_datalen(buf), OP_EQ, cases[i].bytes_left_over);
|
|
|
+
|
|
|
+ buf_clear(buf);
|
|
|
+ tor_free(h);
|
|
|
+ tor_free(b);
|
|
|
+ }
|
|
|
+ done:
|
|
|
+ tor_free(h);
|
|
|
+ tor_free(b);
|
|
|
+ buf_free(buf);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+test_proto_http_invalid(void *arg)
|
|
|
+{
|
|
|
+ (void) arg;
|
|
|
+ const struct {
|
|
|
+ const char *message;
|
|
|
+ size_t len;
|
|
|
+ const char *expect;
|
|
|
+ } cases[] = {
|
|
|
+ /* Overlong headers, headers not finished. */
|
|
|
+ { S("GET /index.xhml HTTP/1.0\r\n"
|
|
|
+ "X-My-headers-are-too-long: yes indeed they are. They might be\r\n"
|
|
|
+ "X-My-headers-are-too-long: normal under other circumstances, but\r\n"
|
|
|
+ "X-My-headers-are-too-long: the 128-byte limit makes them bad\r\n"),
|
|
|
+ "headers too long." },
|
|
|
+ /* Overlong finished headers. */
|
|
|
+ { S("GET /index.xhml HTTP/1.0\r\n"
|
|
|
+ "X-My-headers-are-too-long: yes indeed they are. They might be\r\n"
|
|
|
+ "X-My-headers-are-too-long: normal under other circumstances, but\r\n"
|
|
|
+ "X-My-headers-are-too-long: the 128-byte limit makes them bad\r\n"
|
|
|
+ "\r\n"),
|
|
|
+ "headers too long." },
|
|
|
+ /* Exactly too long finished headers. */
|
|
|
+ { S("GET /index.xhml HTTP/1.0\r\n"
|
|
|
+ "X-My-headers-are-too-long: yes indeed they are. They might be\r\n"
|
|
|
+ "X-My-headers-are-too-long: normal un\r\n\r\n"),
|
|
|
+ "headerlen 129 larger than 127. Failing." },
|
|
|
+ /* Body too long, with content-length */
|
|
|
+ { S("GET /index.html HTTP/1.0\r\n"
|
|
|
+ "Content-Length: 129\r\n\r\n"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
|
+ "xxxxxxxxxxxxxxxxxxx"),
|
|
|
+ "bodylen 129 larger than 127" },
|
|
|
+ /* Body too long, with content-length lying */
|
|
|
+ { S("GET /index.html HTTP/1.0\r\n"
|
|
|
+ "Content-Length: 99999\r\n\r\n"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
|
|
|
+ "bodylen 138 larger than 127" },
|
|
|
+ /* Body too long, no content-length. */
|
|
|
+ { S("GET /index.html HTTP/1.0\r\n\r\n"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
|
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxz"),
|
|
|
+ "bodylen 139 larger than 127" },
|
|
|
+ /* Content-Length is junk. */
|
|
|
+ { S("GET /index.html HTTP/1.0\r\n"
|
|
|
+ "Content-Length: Cheese\r\n\r\n"
|
|
|
+ "foo"),
|
|
|
+ "Content-Length is bogus; maybe someone is trying to crash us." },
|
|
|
+ };
|
|
|
+ unsigned i;
|
|
|
+ buf_t *buf = buf_new();
|
|
|
+ char *h = NULL, *b = NULL;
|
|
|
+ setup_capture_of_logs(LOG_DEBUG);
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
|
|
+ TT_BLATHER(("Trying case %u", i));
|
|
|
+ size_t bl = 0;
|
|
|
+ buf_add(buf, cases[i].message, cases[i].len);
|
|
|
+
|
|
|
+ /* Use low body limits here so we can force over-sized object warnings */
|
|
|
+ tt_int_op(-1, OP_EQ, fetch_from_buf_http(buf, &h, 128,
|
|
|
+ &b, &bl, 128, 0));
|
|
|
+ tt_ptr_op(h, OP_EQ, NULL);
|
|
|
+ tt_ptr_op(b, OP_EQ, NULL);
|
|
|
+ tt_u64_op(bl, OP_EQ, 0);
|
|
|
+ expect_log_msg_containing(cases[i].expect);
|
|
|
+
|
|
|
+ buf_clear(buf);
|
|
|
+ tor_free(h);
|
|
|
+ tor_free(b);
|
|
|
+ mock_clean_saved_logs();
|
|
|
+ }
|
|
|
+ done:
|
|
|
+ tor_free(h);
|
|
|
+ tor_free(b);
|
|
|
+ buf_free(buf);
|
|
|
+ teardown_capture_of_logs();
|
|
|
+}
|
|
|
+
|
|
|
+struct testcase_t proto_http_tests[] = {
|
|
|
+ { "peek", test_proto_http_peek, 0, NULL, NULL },
|
|
|
+ { "valid", test_proto_http_valid, 0, NULL, NULL },
|
|
|
+ { "invalid", test_proto_http_invalid, 0, NULL, NULL },
|
|
|
+
|
|
|
+ END_OF_TESTCASES
|
|
|
+};
|
|
|
+
|