That was the default before 1.5.0 and is very convenient in some setups.
frontend->d_internalPipeBufferSize = boost::get<int>((*vars)["internalPipeBufferSize"]);
}
+ if (vars->count("exactPathMatching")) {
+ frontend->d_exactPathMatching = boost::get<bool>((*vars)["exactPathMatching"]);
+ }
+
parseTLSConfig(frontend->d_tlsConfig, "addDOHLocal", vars);
}
g_dohlocals.push_back(frontend);
.. versionchanged:: 1.5.0
``internalPipeBufferSize``, ``sendCacheControlHeaders``, ``sessionTimeout``, ``trustForwardedForHeader`` options added.
- ``url`` now defaults to ``/dns-query`` instead of ``/``. Added ``tcpListenQueueSize`` parameter.
+ ``url`` now defaults to ``/dns-query`` instead of ``/``, and does exact matching instead of accepting sub-paths. Added ``tcpListenQueueSize`` parameter.
+
+ .. versionchanged:: 1.6.0
+ ``exactPathMatching`` option added.
Listen on the specified address and TCP port for incoming DNS over HTTPS connections, presenting the specified X.509 certificate.
If no certificate (or key) files are specified, listen for incoming DNS over HTTP connections instead.
The default port is 443.
:param str certFile(s): The path to a X.509 certificate file in PEM format, or a list of paths to such files.
:param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones.
- :param str-or-list urls: The path part of a URL, or a list of paths, to accept queries on. Any query with a path under one of these will be treated as a DoH query. The default is /dns-query.
+ :param str-or-list urls: The path part of a URL, or a list of paths, to accept queries on. Any query with a path matching exactly one of these will be treated as a DoH query (sub-paths can be accepted by setting the ``exactPathMatching`` to false). The default is /dns-query.
:param table options: A table with key: value pairs with listen options.
Options:
* ``trustForwardedForHeader``: bool - Whether to parse any existing X-Forwarded-For header in the HTTP query and use the right-most value as the client source address and port, for ACL checks, rules, logging and so on. Default is false.
* ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
* ``internalPipeBufferSize=0``: int - Set the size in bytes of the internal buffer of the pipes used internally to pass queries and responses between threads. Requires support for ``F_SETPIPE_SZ`` which is present in Linux since 2.6.35. The actual size might be rounded up to a multiple of a page size. 0 means that the OS default size is used.
+ * ``exactPathMatching=true``: bool - Whether to do exact path matching of the query path against the paths configured in ``urls`` (true, the default since 1.5.0) or to accepts sub-paths (false, and was the default before 1.5.0).
.. function:: addTLSLocal(address, certFile(s), keyFile(s) [, options])
++dsc->cs->tlsUnknownqueries;
}
- // would be nice to be able to use a pdns_string_view there, but we would need heterogeneous lookups
- // (having string in the set and compare them to string_view, for example. Note that comparing
- // two boost::string_view uses the pointer, not the content).
- const std::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;
+ if (dsc->df->d_exactPathMatching) {
+ // would be nice to be able to use a pdns_string_view there, but we would need heterogeneous lookups
+ // (having string in the set and compare them to string_view, for example. Note that comparing
+ // two boost::string_view uses the pointer, not the content).
+ const std::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;
+ }
}
// would be nice to be able to use a pdns_string_view there,
dsc->df = cs->dohFrontend;
dsc->h2o_config.server_name = h2o_iovec_init(df->d_serverTokens.c_str(), df->d_serverTokens.size());
-
std::thread dnsdistThread(dnsdistclient, dsc->dohquerypair[1]);
dnsdistThread.detach(); // gets us better error reporting
uint32_t d_internalPipeBufferSize{0};
bool d_sendCacheControlHeaders{true};
bool d_trustForwardedForHeader{false};
+ /* whether we require tue query path to exactly match one of configured ones,
+ or accept everything below these paths. */
+ bool d_exactPathMatching{true};
time_t getTicketsKeyRotationDelay() const
{
self.assertIn('foo: bar', headers)
self.assertNotIn(self._customResponseHeader2, headers)
+class TestDOHSubPaths(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"}
+
+ addAction(AllRule(), SpoofAction("3.4.5.6"))
+
+ addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/PowerDNS" }, {exactPathMatching=false})
+ """
+ _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey']
+
+ def testSubPath(self):
+ """
+ DOH: sub-path
+ """
+ name = 'sub-path.doh.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ query.id = 0
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '3.4.5.6')
+ expectedResponse.answer.append(rrset)
+
+ # this path should match
+ (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + 'PowerDNS', caFile=self._caCert, query=query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery.id = 0
+ expectedQuery.flags &= ~dns.flags.RD
+ 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)
+
+ # this path is not in the URLs map and should lead to a 404
+ (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + "NotPowerDNS", query, caFile=self._caCert, useQueue=False, rawResponse=True)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, b'not found')
+ self.assertEquals(self._rcode, 404)
+
+ # this path is below one in the URLs map and exactPathMatching is false, so we should be good
+ (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + 'PowerDNS/something', caFile=self._caCert, query=query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
class TestDOHAddingECS(DNSDistDOHTest):
_serverKey = 'server.key'