Skip to content
Snippets Groups Projects
Unverified Commit c2358a4e authored by Lukas Jezek's avatar Lukas Jezek
Browse files

doh2: send HTTP error status code

parent a900fdbf
No related branches found
No related tags found
1 merge request!1102doh2: send HTTP error status code
Knot Resolver 5.y.z (2021-0m-dd)
================================
Improvements
------------
- doh2: send HTTP error status codes (#618, !1102)
Knot Resolver 5.3.0 (2021-02-25)
================================
......
......@@ -131,10 +131,189 @@ static int send_data_callback(nghttp2_session *h2, nghttp2_frame *frame, const u
return 0;
}
/*
* Provide data from buffer to HTTP/2 library.
*
* To avoid copying the packet wire buffer, we use NGHTTP2_DATA_FLAG_NO_COPY
* and take care of sending entire DATA frames ourselves with nghttp2_send_data_callback.
*
* See https://www.nghttp2.org/documentation/types.html#c.nghttp2_data_source_read_callback
*/
static ssize_t read_callback(nghttp2_session *h2, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data)
{
struct http_data *data;
size_t avail;
size_t send;
data = (struct http_data*)source->ptr;
avail = data->len - data->pos;
send = MIN(avail, length);
if (avail == send)
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
return send;
}
/*
* Get pointer to stream status.
*/
static struct http_stream_status * http_status_get(struct http_ctx *ctx, int32_t stream_id)
{
assert(ctx);
struct http_stream_status *stat = NULL;
if (stream_id == ctx->incomplete_stream)
//return ctx->current_stream_index;
return ctx->current_stream;
for (size_t idx = 0; idx < ctx->stream_status.len; ++idx) {
stat = ctx->stream_status.at[idx];
if (stat->stream_id == stream_id)
return stat;
}
return NULL;
}
/*
* Remove error stream status from list
*/
static int http_status_remove(struct http_ctx *ctx, struct http_stream_status * stat)
{
if (!stat)
return 0;
//assert(array_del(ctx->stream_status, idx) == 0);
// TODO
ctx->stream_status.len -= 1;
if (stat->err_msg)
free(stat->err_msg);
stat = ctx->stream_status.at[ctx->stream_status.len];
//free(stat);
return 0;
}
/*
* Send http error status code.
*/
static int send_err_status(struct http_ctx *ctx, int32_t stream_id)
{
int ret;
int status_len;
nghttp2_data_provider prov;
struct http_stream_status *stat = NULL;
stat = http_status_get(ctx, stream_id);
assert(stat);
if (stat->err_status == 200) {
http_status_remove(ctx, stat);
return 0;
}
prov.source.ptr = NULL;
prov.read_callback = NULL;
char status_str[MAX_DECIMAL_LENGTH(stat->err_status)] = { 0 };
status_len = snprintf(status_str, MAX_DECIMAL_LENGTH(stat->err_status), "%u", stat->err_status);
nghttp2_nv hdrs_err[] = {
MAKE_NV(":status", 7, status_str, status_len),
};
if (stat->err_msg) {
struct http_data *data = malloc(sizeof(struct http_data));
if (!data)
return kr_error(ENOMEM);
data->buf = (uint8_t *)stat->err_msg;
data->len = strlen(stat->err_msg);
data->pos = 0;
data->on_write = NULL;
data->req = NULL;
data->ttl = 0;
prov.source.ptr = data;
prov.read_callback = read_callback;
}
ret = nghttp2_submit_response(ctx->h2, stream_id, hdrs_err, sizeof(hdrs_err)/sizeof(*hdrs_err), &prov);
if (ret != 0)
return kr_error(EIO);
http_status_remove(ctx, stat);
return 0;
}
/*
* Set error status for particural stream_id and return array index or error
*
* status_msg is optional and define error message.
*/
static struct http_stream_status * set_error_status(struct http_ctx *ctx, int32_t stream_id, int status, const char *const status_msg)
{
struct http_stream_status *stat = http_status_get(ctx, stream_id);
if (stat && stat->err_status != 200)
return stat;
// add new item to array
if (!stat) {
stat = malloc(sizeof(*stat));
if (!stat)
return NULL;
if (array_push(ctx->stream_status, stat) < 0) {
free(stat);
return NULL;
}
stat->err_msg = NULL;
}
stat->stream_id = stream_id;
stat->err_status = status;
if (!status_msg) {
if (stat->err_msg) { // remove previous message
free(stat->err_msg);
stat->err_msg = NULL;
}
return stat;
}
stat->err_msg = realloc(stat->err_msg, sizeof(*stat->err_msg) * (strlen(status_msg) + 1));
if (!stat->err_msg) {
return stat;
}
memcpy(stat->err_msg, status_msg, strlen(status_msg));
stat->err_msg[strlen(status_msg)] = '\0';
return stat;
}
/*
* Reinit temporaly data of current stream
*/
static void http_status_reinit(struct http_ctx *ctx)
{
ctx->incomplete_stream = -1;
ctx->current_method = HTTP_METHOD_NONE;
ctx->current_stream = NULL;
if (ctx->content_type) {
free(ctx->content_type);
ctx->content_type = NULL;
}
}
/*
* Check endpoint and uri path
*/
static int check_uri(const char* uri_path)
static int check_uri(struct http_ctx *ctx, int32_t stream_id, const char* uri_path)
{
static const char key[] = "dns=";
static const char *delim = "&";
......@@ -143,6 +322,7 @@ static int check_uri(const char* uri_path)
char *end_prev;
ssize_t endpoint_len;
ssize_t ret;
struct http_stream_status *stat;
if (!uri_path)
return kr_error(EINVAL);
......@@ -170,8 +350,10 @@ static int check_uri(const char* uri_path)
break;
}
if (ret) /* no endpoint found */
return -1;
if (ret) { /* no endpoint found */
stat = set_error_status(ctx, stream_id, 400, "missing endpoint");
return stat ? kr_error(EINVAL) : kr_error(ENOMEM);
}
if (endpoint_len == strlen(path) - 1) /* done for POST method */
return 0;
......@@ -182,37 +364,43 @@ static int check_uri(const char* uri_path)
if (!strncmp(beg, key, 4)) { /* dns variable in path found */
break;
}
end_prev = beg + strlen(beg);
end_prev = beg + strlen(beg) - 1;
beg = strtok(NULL, delim);
if (beg-1 != end_prev) { /* detect && */
return -1;
if (beg && beg-1 != end_prev+1) { /* detect && */
stat = set_error_status(ctx, stream_id, 400, "invalid uri path");
return stat ? kr_error(EINVAL) : kr_error(ENOMEM);
}
}
if (!beg) { /* no dns variable in path */
return -1;
stat = set_error_status(ctx, stream_id, 400, "'dns' key in path not found");
return stat ? kr_error(EINVAL) : kr_error(ENOMEM);
}
}
return 0;
}
/*
* Process a query from URI path if there's base64url encoded dns variable.
*/
static int process_uri_path(struct http_ctx *ctx, const char* path, int32_t stream_id)
static int process_uri_path(struct http_ctx *ctx, int32_t stream_id)
{
if (!ctx || !path)
return kr_error(EINVAL);
static const char key[] = "dns=";
char *beg = strstr(path, key);
char *end;
char *beg, *end;
size_t remaining;
ssize_t ret;
uint8_t *dest;
struct http_stream_status *stat;
if (!beg) /* No dns variable in path. */
if (!ctx || !ctx->uri_path) {
stat = set_error_status(ctx, stream_id, 400, "invalid uri path");
return stat ? 0 : kr_error(ENOMEM);
}
beg = strstr(ctx->uri_path, key);
if (!beg) /* No dns variable in ctx->uri_path. */
return 0;
beg += sizeof(key) - 1;
......@@ -227,9 +415,13 @@ static int process_uri_path(struct http_ctx *ctx, const char* path, int32_t stre
ret = kr_base64url_decode((uint8_t*)beg, end - beg, dest, remaining);
if (ret < 0) {
ctx->buf_pos = 0;
kr_log_verbose("[http] base64url decode failed %s\n",
strerror(ret));
return ret;
kr_log_verbose("[http] base64url decode failed %s\n", strerror(ret));
if (ret == KNOT_ERANGE) {
stat = set_error_status(ctx, stream_id, 414, NULL);// ? ;
} else {
stat = set_error_status(ctx, stream_id, 400, NULL);
}
return stat ? 0 : kr_error(ENOMEM);
}
ctx->buf_pos += ret;
......@@ -237,12 +429,6 @@ static int process_uri_path(struct http_ctx *ctx, const char* path, int32_t stre
return 0;
}
static void refuse_stream(nghttp2_session *h2, int32_t stream_id)
{
nghttp2_submit_rst_stream(
h2, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_REFUSED_STREAM);
}
/*
* Save stream id from first header's frame.
*
......@@ -263,10 +449,12 @@ static int begin_headers_callback(nghttp2_session *h2, const nghttp2_frame *fram
if (ctx->incomplete_stream != -1) {
kr_log_verbose(
"[http] stream %d incomplete, refusing\n", ctx->incomplete_stream);
refuse_stream(h2, stream_id);
"[http] stream %d incomplete\n", ctx->incomplete_stream);
if (!set_error_status(ctx, stream_id, 501, "incomplete stream"))
return NGHTTP2_ERR_CALLBACK_FAILURE;
} else {
ctx->incomplete_stream = stream_id;
ctx->current_stream = set_error_status(ctx, stream_id, 200, NULL);
}
return 0;
}
......@@ -289,22 +477,24 @@ static int header_callback(nghttp2_session *h2, const nghttp2_frame *frame,
if (ctx->incomplete_stream != stream_id) {
kr_log_verbose(
"[http] stream %d incomplete, refusing\n", ctx->incomplete_stream);
refuse_stream(h2, stream_id);
"[http] stream %d incomplete\n", ctx->incomplete_stream);
if (!set_error_status(ctx, stream_id, 501, "incomplete stream"))
return NGHTTP2_ERR_CALLBACK_FAILURE;
return 0;
}
if (!strcasecmp(":path", (const char *)name)) {
if (check_uri((const char *)value) < 0) {
refuse_stream(h2, stream_id);
return 0;
int rc = check_uri(ctx, stream_id, (const char *)value);
if (rc < 0) {
if (rc == kr_error(ENOMEM))
return NGHTTP2_ERR_CALLBACK_FAILURE;
} else {
ctx->uri_path = malloc(sizeof(*ctx->uri_path) * (valuelen + 1));
if (!ctx->uri_path)
return NGHTTP2_ERR_CALLBACK_FAILURE;
memcpy(ctx->uri_path, value, valuelen);
ctx->uri_path[valuelen] = '\0';
}
ctx->uri_path = malloc(sizeof(*ctx->uri_path) * (valuelen + 1));
if (!ctx->uri_path)
return kr_error(ENOMEM);
memcpy(ctx->uri_path, value, valuelen);
ctx->uri_path[valuelen] = '\0';
}
if (!strcasecmp(":method", (const char *)name)) {
......@@ -317,6 +507,14 @@ static int header_callback(nghttp2_session *h2, const nghttp2_frame *frame,
}
}
if (!strcasecmp("content-type", (const char *)name)) {
ctx->content_type = malloc(sizeof(*ctx->content_type) * valuelen+1);
if (!ctx->content_type)
return NGHTTP2_ERR_CALLBACK_FAILURE;
memcpy(ctx->content_type, value, valuelen);
ctx->content_type[valuelen] = '\0';
}
return 0;
}
......@@ -338,10 +536,10 @@ static int data_chunk_recv_callback(nghttp2_session *h2, uint8_t flags, int32_t
if (ctx->incomplete_stream != stream_id) {
kr_log_verbose(
"[http] stream %d incomplete, refusing\n",
"[http] stream %d incomplete\n",
ctx->incomplete_stream);
refuse_stream(h2, stream_id);
ctx->incomplete_stream = -1;
if (!set_error_status(ctx, stream_id, 501, "incomplete stream"))
return NGHTTP2_ERR_CALLBACK_FAILURE;
return 0;
}
......@@ -353,8 +551,11 @@ static int data_chunk_recv_callback(nghttp2_session *h2, uint8_t flags, int32_t
if (required > remaining) {
kr_log_error("[http] insufficient space in buffer\n");
ctx->incomplete_stream = -1;
return NGHTTP2_ERR_CALLBACK_FAILURE;
if (!set_error_status(ctx, stream_id, 413, NULL)) {
http_status_reinit(ctx);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
if (is_first) {
......@@ -382,27 +583,72 @@ static int on_frame_recv_callback(nghttp2_session *h2, const nghttp2_frame *fram
int32_t stream_id = frame->hd.stream_id;
assert(stream_id != -1);
if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) && ctx->incomplete_stream == stream_id) {
if (ctx->current_method == HTTP_METHOD_GET) {
if (process_uri_path(ctx, ctx->uri_path, stream_id) < 0) {
refuse_stream(h2, stream_id);
}
if (stream_id == 0)
return 0;
if (ctx->current_method == HTTP_METHOD_NONE) {
kr_log_verbose("[http] unsupported HTTP method\n");
if (!set_error_status(ctx, stream_id, 405, "only HTTP POST and GET are supported\n")) {
http_status_reinit(ctx);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
ctx->incomplete_stream = -1;
ctx->current_method = HTTP_METHOD_NONE;
free(ctx->uri_path);
ctx->uri_path = NULL;
len = ctx->buf_pos - sizeof(uint16_t);
if (len <= 0 || len > KNOT_WIRE_MAX_PKTSIZE) {
kr_log_verbose("[http] invalid dnsmsg size: %zd B\n", len);
}
if (ctx->content_type && strcasecmp("application/dns-message", (const char *)ctx->content_type)) {
kr_log_verbose("[http] unsupported content-type %s\n", ctx->content_type);
if (!set_error_status(ctx, stream_id, 415, "only Content-Type: application/dns-message is supported\n")) {
http_status_reinit(ctx);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
knot_wire_write_u16(ctx->buf, len);
ctx->submitted += ctx->buf_pos;
ctx->buf += ctx->buf_pos;
ctx->buf_pos = 0;
if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
struct http_stream_status *stat = ctx->current_stream;
if (ctx->incomplete_stream == stream_id) {
if (ctx->current_method == HTTP_METHOD_GET) {
if (process_uri_path(ctx, stream_id) < 0) {
http_status_reinit(ctx);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
free(ctx->uri_path);
ctx->uri_path = NULL;
if (ctx->buf_pos) {
len = ctx->buf_pos - sizeof(uint16_t);
if (len <= 0 || len > KNOT_WIRE_MAX_PKTSIZE) {
kr_log_verbose("[http] invalid dnsmsg size: %zd B\n", len);
http_status_reinit(ctx);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
if (len < 12) {
if (!set_error_status(ctx, stream_id, 400, "input too short\n")) {
http_status_reinit(ctx);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
if (stat->err_status == 200) {
knot_wire_write_u16(ctx->buf, len);
ctx->submitted += ctx->buf_pos;
ctx->buf += ctx->buf_pos;
}
}
if (stat->err_status != 200) {
if (send_err_status(ctx, stream_id) < 0)
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
http_status_reinit(ctx);
ctx->buf_pos = 0;
} else {
/* send error for non-processed stream */
if (send_err_status(ctx, stream_id) < 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
}
return 0;
......@@ -473,9 +719,13 @@ struct http_ctx* http_new(struct session *session, http_send_callback send_cb)
ctx->session = session;
queue_init(ctx->streams);
ctx->incomplete_stream = -1;
ctx->current_stream = NULL;
ctx->submitted = 0;
ctx->current_method = HTTP_METHOD_NONE;
ctx->uri_path = NULL;
ctx->content_type = NULL;
array_init(ctx->stream_status);
nghttp2_session_server_new(&ctx->h2, callbacks, ctx);
nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
......@@ -522,33 +772,6 @@ ssize_t http_process_input_data(struct session *session, const uint8_t *buf,
return ctx->submitted;
}
/*
* Provide data from buffer to HTTP/2 library.
*
* To avoid copying the packet wire buffer, we use NGHTTP2_DATA_FLAG_NO_COPY
* and take care of sending entire DATA frames ourselves with nghttp2_send_data_callback.
*
* See https://www.nghttp2.org/documentation/types.html#c.nghttp2_data_source_read_callback
*/
static ssize_t read_callback(nghttp2_session *h2, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data)
{
struct http_data *data;
size_t avail;
size_t send;
data = (struct http_data*)source->ptr;
avail = data->len - data->pos;
send = MIN(avail, length);
if (avail == send)
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
return send;
}
/*
* Send dns response provided by the HTTTP/2 data provider.
*
......@@ -663,7 +886,12 @@ void http_free(struct http_ctx *ctx)
if (!ctx)
return;
// TODO
// while(ctx->stream_status.len)
// http_status_remove(ctx, 0);
queue_deinit(ctx->streams);
nghttp2_session_del(ctx->h2);
free(ctx->content_type);
free(ctx);
}
......@@ -32,6 +32,12 @@ typedef enum {
HTTP_METHOD_POST = 2,
} http_method_t;
struct http_stream_status {
int32_t stream_id;
int err_status;
char *err_msg;
};
struct http_ctx {
struct nghttp2_session *h2;
http_send_callback send_cb;
......@@ -41,9 +47,12 @@ struct http_ctx {
ssize_t submitted;
http_method_t current_method;
char *uri_path;
char *content_type;
uint8_t *buf; /* Part of the wire_buf that belongs to current HTTP/2 stream. */
ssize_t buf_pos;
ssize_t buf_size;
array_t(struct http_stream_status*) stream_status;
struct http_stream_status *current_stream;
};
#if ENABLE_DOH2
......
......@@ -63,15 +63,15 @@ local function check_ok(req, desc)
return headers, pkt
end
--local function check_err(req, exp_status, desc)
-- local headers, errmsg, errno = req:go(8) -- randomly chosen timeout by tkrizek
-- if errno then
-- nok(errmsg, desc .. ': ' .. errmsg)
-- return
-- end
-- local got_status = headers:get(':status')
-- same(got_status, exp_status, desc)
--end
local function check_err(req, exp_status, desc)
local headers, errmsg, errno = req:go(8) -- randomly chosen timeout by tkrizek
if errno then
nok(errmsg, desc .. ': ' .. errmsg)
return
end
local got_status = headers:get(':status')
same(got_status, exp_status, desc)
end
-- check prerequisites
local bound, port
......@@ -168,34 +168,36 @@ else
end
-- test an invalid DNS query using POST
-- local function test_post_short_input()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'POST')
-- req:set_body(string.rep('0', 11)) -- 11 bytes < DNS msg header
-- check_err(req, '400', 'too short POST finishes with 400')
-- end
--
-- local function test_post_long_input()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'POST')
-- req:set_body(string.rep('s', 1025)) -- > DNS msg over UDP
-- check_err(req, '413', 'too long POST finishes with 413')
-- end
--
-- local function test_post_unparseable_input()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'POST')
-- req:set_body(string.rep('\0', 1024)) -- garbage
-- check_err(req, '400', 'unparseable DNS message finishes with 400')
-- end
--
-- local function test_post_unsupp_type()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'POST')
-- req.headers:upsert('content-type', 'application/dns+json')
-- req:set_body(string.rep('\0', 12)) -- valid message
-- check_err(req, '415', 'unsupported request content type finishes with 415')
-- end
local function test_post_short_input()
local req = assert(req_templ:clone())
req.headers:upsert(':method', 'POST')
req:set_body(string.rep('0', 11)) -- 11 bytes < DNS msg header
check_err(req, '400', 'too short POST finishes with 400')
test_post_noerror()
end
local function test_post_unsupp_type()
local req = assert(req_templ:clone())
req.headers:upsert(':method', 'POST')
req.headers:upsert('content-type', 'application/dns+json')
req:set_body(string.rep('\0', 12)) -- valid message
check_err(req, '415', 'unsupported request content type finishes with 415')
test_post_noerror()
end
local function test_get_right_endpoints()
local desc = 'GET query with "doh" endpoint'
local req = req_templ:clone()
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/doh?dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')
check_ok(req, desc)
desc = 'GET query with "dns-query" endpoint'
req = req_templ:clone()
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/dns-query?dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')
check_ok(req, desc)
end
-- test a valid DNS query using GET
local function test_get_servfail()
......@@ -273,47 +275,75 @@ else
check_ok(req, desc)
end
-- -- test an invalid DNS query using GET
-- local function test_get_long_input()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'GET')
-- req.headers:upsert(':path', '/doh?dns=' .. basexx.to_url64(string.rep('\0', 1030)))
-- check_err(req, '414', 'too long GET finishes with 414')
-- end
--
-- local function test_get_no_dns_param()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'GET')
-- req.headers:upsert(':path', '/doh?notdns=' .. basexx.to_url64(string.rep('\0', 1024)))
-- check_err(req, '400', 'GET without dns paramter finishes with 400')
-- end
--
-- local function test_get_unparseable()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'GET')
-- req.headers:upsert(':path', '/doh??dns=' .. basexx.to_url64(string.rep('\0', 1024)))
-- check_err(req, '400', 'unparseable GET finishes with 400')
-- end
--
-- local function test_get_invalid_b64()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'GET')
-- req.headers:upsert(':path', '/doh?dns=thisisnotb64')
-- check_err(req, '400', 'GET with invalid base64 finishes with 400')
-- end
--
-- local function test_get_invalid_chars()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'GET')
-- req.headers:upsert(':path', '/doh?dns=' .. basexx.to_url64(string.rep('\0', 200)) .. '@#$%?!')
-- check_err(req, '400', 'GET with invalid characters in b64 finishes with 400')
-- end
--
-- local function test_unsupp_method()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'PUT')
-- check_err(req, '405', 'unsupported method finishes with 405')
-- end
-- test an invalid DNS query using GET
local function test_get_wrong_endpoints()
local req = req_templ:clone()
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/bad?dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')
check_err(req, '400', 'wrong "bad" endpoint finishes with 400')
test_get_other_params()
req = req_templ:clone()
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/dns?dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')
check_err(req, '400', 'wrong "dns" endpoint finishes with 400')
test_get_other_params()
end
local function test_get_no_dns_param()
local req = assert(req_templ:clone())
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/doh?notdns=' .. basexx.to_url64(string.rep('\0', 1024)))
check_err(req, '400', 'GET without dns parameter finishes with 400')
test_get_other_params()
end
local function test_get_unparseable()
local req = assert(req_templ:clone())
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/doh??dns=' .. basexx.to_url64(string.rep('\0', 1024)))
check_err(req, '400', 'unparseable GET finishes with 400')
test_get_other_params()
end
local function test_get_invalid_b64()
local req = assert(req_templ:clone())
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/doh?dns=thisisnotb64')
check_err(req, '400', 'GET with invalid base64 finishes with 400')
test_get_other_params()
end
local function test_get_invalid_chars()
local req = assert(req_templ:clone())
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path', '/doh?dns=' .. basexx.to_url64(string.rep('\0', 200)) .. '@#$%?!')
check_err(req, '400', 'GET with invalid characters in b64 finishes with 400')
test_get_other_params()
end
local function test_get_two_ampersands()
local req = req_templ:clone()
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path',
'/doh?other=something&&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')
check_err(req, '400', 'GET with two ampersands finishes with 400')
test_get_other_params()
req = req_templ:clone()
req.headers:upsert(':method', 'GET')
req.headers:upsert(':path',
'/doh?other=something&&nodns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')
check_err(req, '400', 'GET with two ampersands finishes with 400')
test_get_other_params()
end
local function test_unsupp_method()
local req = assert(req_templ:clone())
req.headers:upsert(':method', 'PUT')
check_err(req, '405', 'unsupported method finishes with 405')
test_get_other_params()
end
local function test_dstaddr()
local triggered = false
......@@ -353,39 +383,29 @@ else
modules.unload('view')
end
-- not implemented
-- local function test_post_unsupp_accept()
-- local req = assert(req_templ:clone())
-- req.headers:upsert(':method', 'POST')
-- req.headers:upsert('accept', 'application/dns+json')
-- req:set_body(string.rep('\0', 12)) -- valid message
-- check_err(req, '406', 'unsupported Accept type finishes with 406')
-- end
-- plan tests
-- TODO: implement (some) of the error status codes
local tests = {
start_server,
test_post_servfail,
test_post_noerror,
test_post_nxdomain,
test_huge_answer,
--test_post_short_input,
--test_post_long_input,
--test_post_unparseable_input,
--test_post_unsupp_type,
test_post_short_input,
test_post_unsupp_type,
test_get_right_endpoints,
test_get_servfail,
test_get_noerror,
test_get_nxdomain,
test_get_other_params_before_dns,
test_get_other_params_after_dns,
test_get_other_params,
--test_get_long_input,
--test_get_no_dns_param,
--test_get_unparseable,
--test_get_invalid_b64,
--test_get_invalid_chars,
--test_unsupp_method,
test_get_wrong_endpoints,
test_get_no_dns_param,
test_get_unparseable,
test_get_invalid_b64,
test_get_invalid_chars,
test_get_two_ampersands,
test_unsupp_method,
test_dstaddr,
test_srcaddr
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment