From: Ivana Krumlová Date: Mon, 24 Jun 2019 11:32:41 +0000 (+0200) Subject: modules/http: tests for DoH X-Git-Tag: v4.1.0~15^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ef22a9090516db034e9076f2b8eba05fa4b6ccb;p=thirdparty%2Fknot-resolver.git modules/http: tests for DoH --- diff --git a/modules/http/http_doh.test.lua b/modules/http/http_doh.test.lua index 00238b8d7..480df3a1b 100644 --- a/modules/http/http_doh.test.lua +++ b/modules/http/http_doh.test.lua @@ -111,7 +111,7 @@ else -- test a valid DNS query using POST - local function test_doh_servfail() + local function test_post_servfail() local desc = 'valid POST query which ends with SERVFAIL' local req = req_templ:clone() req.headers:upsert(':method', 'POST') @@ -126,12 +126,12 @@ else same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') end - local function test_doh_noerror() + local function test_post_noerror() local desc = 'valid POST query which ends with NOERROR' local req = req_templ:clone() - req.headers:upsert(':method', 'GET') - req.headers:upsert(':path', '/doh?dns=' -- noerror.test. A - .. 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- noerror.test. A + 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')) local headers, pkt = check_ok(req, desc) if not (headers and pkt) then return @@ -144,11 +144,11 @@ else same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') end - local function test_doh_nxdomain() + local function test_post_nxdomain() local desc = 'valid POST query which ends with NXDOMAIN' local req = req_templ:clone() req.headers:upsert(':method', 'POST') - req:set_body(basexx.from_base64( -- servfail.test. A + req:set_body(basexx.from_base64( -- nxdomain.test. A 'viABAAABAAAAAAAACG54ZG9tYWluBHRlc3QAAAEAAQ==')) local headers, pkt = check_ok(req, desc) if not (headers and pkt) then @@ -159,13 +159,21 @@ else same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') 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') + -- RFC 8484 section 6 explicitly allows huge answers over HTTP + local function test_huge_answer() + policy.add(policy.suffix(gen_huge_answer, policy.todnames({'huge.test'}))) + local desc = 'POST query for a huge answer' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- huge.test. URI, no EDNS + 'HHwBAAABAAAAAAAABGh1Z2UEdGVzdAABAAAB')) + local _, pkt = check_ok(req, desc) + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode NOERROR') + same(pkt:tc(), false, desc .. ': no TC bit') + same(pkt:ancount(), 2, desc .. ': ANSWER contains both RRs') 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') @@ -180,13 +188,6 @@ else check_err(req, '413', 'too long POST finishes with 413') end - 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_post_unparseable_input() local req = assert(req_templ:clone()) req.headers:upsert(':method', 'POST') @@ -202,6 +203,124 @@ else check_err(req, '415', 'unsupported request content type finishes with 415') end + -- test a valid DNS query using GET + local function test_get_servfail() + local desc = 'valid GET query which ends with SERVFAIL' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- servfail.test. A + .. 'FZUBAAABAAAAAAAACHNlcnZmYWlsBHRlc3QAAAEAAQ') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- uncacheable + same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') + end + + local function test_get_noerror() + local desc = 'valid GET query which ends with NOERROR' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- noerror.test. A + .. 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- HTTP TTL is minimum from all RRs in the answer + same(headers:get('cache-control'), 'max-age=300', desc .. ': TTL 900') + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode matches') + same(pkt:ancount(), 3, desc .. ': ANSWER is present') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') + end + + local function test_get_nxdomain() + local desc = 'valid GET query which ends with NXDOMAIN' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- nxdomain.test. A + .. 'viABAAABAAAAAAAACG54ZG9tYWluBHRlc3QAAAEAAQ') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + same(headers:get('cache-control'), 'max-age=10800', desc .. ': TTL 10800') + same(pkt:rcode(), kres.rcode.NXDOMAIN, desc .. ': rcode matches') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + end + + local function test_get_other_params_before_dns() + local desc = 'GET query with other parameters before dns is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?other=something&another=something&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + check_ok(req, desc) + end + + local function test_get_other_params_after_dns() + local desc = 'GET query with other parameters after dns is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB&other=something&another=something') + check_ok(req, desc) + end + + local function test_get_other_params() + local desc = 'GET query with other parameters than dns on both sides is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?other=something&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB&another=something') + 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 + local function test_dstaddr() local triggered = false local exp_dstaddr = ffi.gc(ffi.C.kr_straddr_socket(host, port, nil), ffi.C.free) @@ -240,56 +359,6 @@ else modules.unload('view') end - -- RFC 8484 section 6 explicitly allows huge answers over HTTP - local function test_huge_answer() - policy.add(policy.suffix(gen_huge_answer, policy.todnames({'huge.test'}))) - local desc = 'POST query for a huge answer' - local req = req_templ:clone() - req.headers:upsert(':method', 'POST') - req:set_body(basexx.from_base64( -- huge.test. URI, no EDNS - 'HHwBAAABAAAAAAAABGh1Z2UEdGVzdAABAAAB')) - local _, pkt = check_ok(req, desc) - same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode NOERROR') - same(pkt:tc(), false, desc .. ': no TC bit') - same(pkt:ancount(), 2, desc .. ': ANSWER contains both RRs') - 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_unparseble_get() - 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', 'unparseble GET finishes with 400') - end - - local function test_get_other_params() - local desc = 'valid GET query with other parameters than dns finishes with 200' - local req = req_templ:clone() - req.headers:upsert(':method', 'GET') - req.headers:upsert(':path', '/doh?other=something&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB&another=something') - local headers, pkt = check_ok(req, desc) - 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 - -- not implemented -- local function test_post_unsupp_accept() -- local req = assert(req_templ:clone()) @@ -302,23 +371,28 @@ else -- plan tests local tests = { start_server, - test_unsupp_method, + test_post_servfail, + test_post_noerror, + test_post_nxdomain, + test_huge_answer, test_post_short_input, test_post_long_input, - test_get_long_input, test_post_unparseable_input, test_post_unsupp_type, - test_doh_servfail, - test_doh_nxdomain, - test_doh_noerror, - test_dstaddr, - test_srcaddr, - test_huge_answer, - test_get_no_dns_param, - test_unparseble_get, + 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_get_invalid_chars, + test_unsupp_method, + test_dstaddr, + test_srcaddr } return tests