aboutsummaryrefslogtreecommitdiff
path: root/src/test/test_proto_http.c
blob: 481d78b2c1e7f43e0896bddaf144ebac773c2652 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/* Copyright (c) 2017-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
 * \file test_proto_http.c
 * \brief Tests for our HTTP protocol parser code
 */

#include "core/or/or.h"
#include "test/test.h"
#include "lib/buf/buffers.h"
#include "core/proto/proto_http.h"
#include "test/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
};