blob: aa1246a5ee8b42e54eb197e379052512d213ce6a [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <inttypes.h>
#include "htparse.h"
#define ADD_DATA_BUF(buf, name, data, len) do { \
strcat(buf, name ": '"); \
strncat(buf, data, len); \
strcat(buf, "'\n"); \
} while (0)
struct testobj {
char * name;
char * data;
htp_type type;
};
static int
_msg_begin(htparser * p) {
char * buf = (char *)htparser_get_userdata(p);
strcat(buf, "START\n");
return 0;
}
static int
_method(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
if (htparser_get_method(p) == htp_method_UNKNOWN) {
ADD_DATA_BUF(buf, "METHOD_UNKNOWN", b, s);
return htp_method_UNKNOWN;
} else {
ADD_DATA_BUF(buf, "METHOD", b, s);
}
return 0;
}
static int
_scheme(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "SCHEME", b, s);
return 0;
}
static int
_host(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "HOST", b, s);
return 0;
}
static int
_port(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "PORT", b, s);
return 0;
}
static int
_path(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "PATH", b, s);
return 0;
}
static int
_args(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "ARGS", b, s);
return 0;
}
static int
_uri(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "URI", b, s);
return 0;
}
static int
_hdrs_begin(htparser * p) {
char * buf = (char *)htparser_get_userdata(p);
strcat(buf, "HDRS_BEGIN\n");
return 0;
}
static int
_hdr_key(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "HDR_KEY", b, s);
return 0;
}
static int
_hdr_val(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "HDR_VAL", b, s);
return 0;
}
static int
_hostname(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "HOSTNAME", b, s);
return 0;
}
static int
_hdrs_complete(htparser * p) {
char * buf = (char *)htparser_get_userdata(p);
strcat(buf, "HDRS_COMPLETE\n");
return 0;
}
static int
_new_chunk(htparser * p) {
char * buf = (char *)htparser_get_userdata(p);
char tbuf[1024] = { 0 };
sprintf(tbuf, "NEW_CHUNK: %" PRIu64 "\n", htparser_get_content_length(p));
strcat(buf, tbuf);
return 0;
}
static int
_chunk_complete(htparser * p) {
char * buf = (char *)htparser_get_userdata(p);
strcat(buf, "END_CHUNK\n");
return 0;
}
static int
_chunks_complete(htparser * p) {
char * buf = (char *)htparser_get_userdata(p);
strcat(buf, "END_CHUNKS\n");
return 0;
}
static int
_body(htparser * p, const char * b, size_t s) {
char * buf = (char *)htparser_get_userdata(p);
ADD_DATA_BUF(buf, "BODY", b, s);
return 0;
}
static int
_msg_complete(htparser * p) {
char * buf = (char *)htparser_get_userdata(p);
strcat(buf, "MSG_COMPLETE\n");
return 0;
}
htparse_hooks hooks = {
.on_msg_begin = _msg_begin,
.method = _method,
.scheme = _scheme,
.host = _host,
.port = _port,
.path = _path,
.args = _args,
.uri = _uri,
.on_hdrs_begin = _hdrs_begin,
.hdr_key = _hdr_key,
.hdr_val = _hdr_val,
.hostname = _hostname,
.on_hdrs_complete = _hdrs_complete,
.on_new_chunk = _new_chunk,
.on_chunk_complete = _chunk_complete,
.on_chunks_complete = _chunks_complete,
.body = _body,
.on_msg_complete = _msg_complete
};
struct testobj t1 = {
.name = "small GET request",
.type = htp_type_request,
.data = "GET / HTTP/1.0\r\n\r\n"
};
struct testobj t2 = {
.name = "GET request with arguments",
.type = htp_type_request,
.data = "GET /test?a=b&c=d HTTP/1.1\r\n\r\n"
};
struct testobj t3 = {
.name = "POST request with 4 bytes of data",
.type = htp_type_request,
.data = "POST /foo/bar HTTP/1.0\r\n"
"Content-Length: 4\r\n\r\n"
"abcd"
};
struct testobj t4 = {
.name = "Simple POST with chunked data",
.type = htp_type_request,
.data = "POST /test/ HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"1e\r\nall your base are belong to us\r\n"
"0\r\n"
"\r\n"
};
struct testobj t5 = {
.name = "POST request with multiple chunks",
.type = htp_type_request,
.data = "POST /test/ HTTP/1.1\r\n"
"Connection: Keep-Alive\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"23\r\n"
"This is the data in the first chunk"
"\r\n"
"1A\r\n"
"and this is the second one"
"\r\n"
"3\r\n"
"foo\r\n"
"6\r\n"
"barbaz\r\n"
"0\r\n\r\n"
};
struct testobj t6 = {
.name = "GET request with a host header",
.type = htp_type_request,
.data = "GET /test/ HTTP/1.0\r\n"
"Host: ieatfood.net\r\n\r\n"
};
struct testobj t7 = {
.name = "GET request with an empty header value",
.type = htp_type_request,
.data = "GET /test/ HTTP/1.0\r\n"
"Header1: value1\r\n"
"Header2: \r\n"
"Header3: value3\r\n\r\n"
};
struct testobj t8 = {
.name = "GET request with a multi-line header value",
.type = htp_type_request,
.data = "GET /test/ HTTP/1.1\r\n"
"Header1: value1\r\n"
"Header2: val\r\n"
"\tue\r\n"
"\t2\r\n"
"Header3: value3\r\n\r\n"
};
struct testobj t9 = {
.name = "[FAILURE TEST] GET REQUEST with LF instead of CRLF on header value",
.type = htp_type_request,
.data = "GET /test/ HTTP/1.1\r\n"
"Header: value\n\n"
};
struct testobj t10 = {
.name = "[FAILURE TEST] GET request with invalid protocol",
.type = htp_type_request,
.data = "GET /test/ fdasfs\r\n\r\n"
};
struct testobj t11 = {
.name = "[FALURE TEST] POST request with invalid chunk length",
.type = htp_type_request,
.data = "POST /test/ HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"3\r\n"
"foo"
"\r\n"
"A\r\n"
"foobar\r\n"
"3\r\n"
"baz\r\n"
"0\r\n\r\n"
};
struct testobj t12 = {
.name = "Simple GET on a FTP scheme",
.type = htp_type_request,
.data = "GET ftp://test.com/foo/bar HTTP/1.1\r\n\r\n"
};
struct testobj t13 = {
.name = "Multiple GET requests in HTTP/1.1 request",
.type = htp_type_request,
.data = "GET /request1 HTTP/1.1\r\n\r\n"
"GET /request2 HTTP/1.0\r\n"
"Connection: close\r\n\r\n"
};
struct testobj t14 = {
.name = "[FAILURE TEST] invalid request type",
.type = htp_type_request,
.data = "DERP /test HTTP/1.1\r\n\r\n"
};
struct testobj t15 = {
.name = "http SCHEME request with port / args / headers",
.type = htp_type_request,
.data = "GET http://ieatfood.net:80/index.html?foo=bar&baz=buz HTTP/1.0\r\n"
"Host: ieatfood.net\r\n"
"Header: value\r\n\r\n"
};
struct testobj t16 = {
.name = "GET request which should run all callbacks minus scheme stuff, this includes multiple requests",
.type = htp_type_request,
.data = "GET /test1?a=b&c=d&e=f HTTP/1.1\r\n"
"Content-Length: 6\r\n\r\n"
"foobar"
"GET /test2 HTTP/1.1\r\n"
"Header: test2\r\n\r\n"
"POST /test/ HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"23\r\n"
"This is the data in the first chunk"
"\r\n"
"1A\r\n"
"and this is the second one"
"\r\n"
"3\r\n"
"foo\r\n"
"6\r\n"
"barbaz\r\n"
"0\r\n\r\n"
"GET /test/ HTTP/1.1\r\n"
"Host: ieatfood.net\r\n"
"Connection: close\r\n\r\n"
};
struct testobj t17 = {
.name = "Like the last test, but with scheme requests",
.type = htp_type_request,
.data = "GET http://ieatfood.net/test1?a=b&c=d&e=f HTTP/1.1\r\n"
"Content-Length: 6\r\n\r\n"
"foobar"
"GET https://ieatfood.net:443/test2 HTTP/1.1\r\n"
"Header: test2\r\n\r\n"
"POST /test/ HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"23\r\n"
"This is the data in the first chunk"
"\r\n"
"1A\r\n"
"and this is the second one"
"\r\n"
"3\r\n"
"foo\r\n"
"6\r\n"
"barbaz\r\n"
"0\r\n\r\n"
"GET ftp://ackers.net:21/test/ HTTP/1.1\r\n"
"Host: ackers.net\r\n"
"Connection: close\r\n\r\n"
};
struct testobj t18 = {
.name = "scheme request with empty path",
.type = htp_type_request,
.data = "GET http://ackers.net HTTP/1.0\r\n\r\n"
};
struct testobj t19 = {
.name = "basic HTTP RESPONSE",
.type = htp_type_response,
.data = "HTTP/1.1 200 OK\r\n\r\n"
};
struct testobj t20 = {
.name = "HTTP RESPONSE with body",
.type = htp_type_response,
.data = "HTTP/1.1 200 OK\r\n"
"Content-Length: 6\r\n\r\n"
"foobar"
};
struct testobj t21 = {
.name = "HTTP RESPONSE with chunked data",
.type = htp_type_response,
.data = "HTTP/1.1 200 OK\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"23\r\n"
"This is the data in the first chunk"
"\r\n"
"1A\r\n"
"and this is the second one"
"\r\n"
"3\r\n"
"foo\r\n"
"6\r\n"
"barbaz\r\n"
"0\r\n\r\n"
};
struct testobj t22 = {
.name = "Header key with no value",
.type = htp_type_request,
.data = "GET / HTTP/1.1\r\n"
"Accept\r\n\r\n"
};
static int
_run_test(htparser * p, struct testobj * obj) {
size_t data_sz;
size_t parsed_sz;
char result_buf[5000] = { 0 };
htparser_init(p, obj->type);
htparser_set_userdata(p, result_buf);
data_sz = strlen(obj->data);
parsed_sz = htparser_run(p, &hooks, obj->data, data_sz);
strcat(result_buf, "ERROR_STR: ");
strcat(result_buf, htparser_get_strerror(p));
strcat(result_buf, "\n");
printf("%s\n", obj->name);
printf("-----------------\n");
printf("%s", result_buf);
printf("\n");
return 0;
}
int
main(int argc, char **argv) {
htparser * parser;
parser = htparser_new();
assert(parser != NULL);
_run_test(parser, &t1);
_run_test(parser, &t2);
_run_test(parser, &t3);
_run_test(parser, &t4);
_run_test(parser, &t5);
_run_test(parser, &t6);
_run_test(parser, &t7);
_run_test(parser, &t8);
_run_test(parser, &t9);
_run_test(parser, &t10);
_run_test(parser, &t11);
_run_test(parser, &t12);
_run_test(parser, &t13);
_run_test(parser, &t14);
_run_test(parser, &t15);
_run_test(parser, &t16);
_run_test(parser, &t17);
_run_test(parser, &t18);
_run_test(parser, &t19);
_run_test(parser, &t20);
_run_test(parser, &t21);
_run_test(parser, &t22);
return 0;
}