}
LocalHolders holders;
+ std::unordered_set<std::string> paths;
h2o_globalconf_t h2o_config;
h2o_context_t h2o_ctx;
DOHAcceptContext* accept_ctx{nullptr};
}
}
+ if (df.d_sendCacheControlHeaders && !response.empty()) {
+ uint32_t minTTL = getDNSPacketMinTTL(response.data(), response.size());
+ if (minTTL != std::numeric_limits<uint32_t>::max()) {
+ std::string cacheControlValue = "max-age=" + std::to_string(minTTL);
+ /* we need to duplicate the header content because h2o keeps a pointer and we will be deleted before the response has been sent */
+ h2o_iovec_t ccv = h2o_strdup(&req->pool, cacheControlValue.c_str(), cacheControlValue.size());
+ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CACHE_CONTROL, nullptr, ccv.base, ccv.len);
+ }
+ }
+
req->res.content_length = response.size();
h2o_send_inline(req, response.c_str(), response.size());
}
string path(req->path.base, req->path.len);
+ string pathOnly(req->path_normalized.base, req->path_normalized.len);
+ if (dsc->paths.count(pathOnly) == 0) {
+ h2o_send_error_404(req, "Not Found", "there is no endpoint configured for this path", 0);
+ return 0;
+ }
+
for (const auto& entry : dsc->df->d_responsesMap) {
if (entry->matches(path)) {
const auto& customHeaders = entry->getHeaders();
for(const auto& url : df->d_urls) {
register_handler(hostconf, url.c_str(), doh_handler);
+ dsc->paths.insert(url);
}
h2o_context_init(&dsc->h2o_ctx, h2o_evloop_create(), &dsc->h2o_config);
import base64
import dns
import os
+import re
+import time
import unittest
import clientsubnetoption
from dnsdisttests import DNSDistTest
cls._response_headers = response_headers.getvalue()
return (receivedQuery, message)
+ def getHeaderValue(self, name):
+ for header in self._response_headers.decode().splitlines(False):
+ values = header.split(':')
+ key = values[0]
+ if key.lower() == name.lower():
+ return values[1].strip()
+ return None
+
+ def checkHasHeader(self, name, value):
+ got = self.getHeaderValue(name)
+ self.assertEquals(got, value)
+
+ def checkNoHeader(self, name):
+ self.checkHasHeader(name, None)
+
@classmethod
def setUpClass(cls):
_config_template = """
newServer{address="127.0.0.1:%s"}
- addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/" }, {customResponseHeaders={["access-control-allow-origin"]="*",["user-agent"]="derp",["UPPERCASE"]="VaLuE"}})
+ addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/", "/coffee", "/PowerDNS", "/PowerDNS2", "/PowerDNS-999" }, {customResponseHeaders={["access-control-allow-origin"]="*",["user-agent"]="derp",["UPPERCASE"]="VaLuE"}})
dohFE = getDOHFrontend(0)
dohFE:setResponsesMap({newDOHResponseMapEntry('^/coffee$', 418, 'C0FFEE', {['FoO']='bar'})})
self.assertTrue((self._customResponseHeader2) in self._response_headers.decode())
self.assertFalse(('UPPERCASE: VaLuE' in self._response_headers.decode()))
self.assertTrue(('uppercase: VaLuE' in self._response_headers.decode()))
+ self.assertTrue(('cache-control: max-age=3600' in self._response_headers.decode()))
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.checkHasHeader('cache-control', 'max-age=3600')
def testDOHSimplePOST(self):
"""
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ # this path is not in the URLs map and should lead to a 404
+ (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + "PowerDNS/something", query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, b'there is no endpoint configured for this path')
+ self.assertEquals(self._rcode, 404)
+
def testHTTPPathRegex(self):
"""
DOH: HTTPPathRegex
self.assertEquals(expectedQuery, receivedQuery)
self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.checkHasHeader('cache-control', 'max-age=3600')
for _ in range(numberOfQueries):
(_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
self.assertEquals(receivedResponse, response)
+ self.checkHasHeader('cache-control', 'max-age=' + str(receivedResponse.answer[0].ttl))
+
+ time.sleep(1)
+
+ (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, caFile=self._caCert, useQueue=False)
+ self.assertEquals(receivedResponse, response)
+ self.checkHasHeader('cache-control', 'max-age=' + str(receivedResponse.answer[0].ttl))
+
+class TestDOHWithoutCacheControl(DNSDistDOHTest):
+
+ _serverKey = 'server.key'
+ _serverCert = 'server.chain'
+ _serverName = 'tls.tests.dnsdist.org'
+ _caCert = 'ca.pem'
+ _dohServerPort = 8443
+ _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _config_template = """
+ newServer{address="127.0.0.1:%s"}
+
+ addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/" }, {sendCacheControlHeaders=false})
+ """
+ _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey']
+
+ def testDOHSimple(self):
+ """
+ DOH without cache-control
+ """
+ name = 'simple.doh.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query.id = 0
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery.id = 0
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.checkNoHeader('cache-control')
+ self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+ self.assertEquals(response, receivedResponse)