tor/src/test/test_proto_http.c

214 lines
6.3 KiB
C

/* 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
};