summaryrefslogtreecommitdiffstats
path: root/doh.hh
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--doh.hh286
1 files changed, 286 insertions, 0 deletions
diff --git a/doh.hh b/doh.hh
new file mode 100644
index 0000000..96e65f1
--- /dev/null
+++ b/doh.hh
@@ -0,0 +1,286 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include <unordered_map>
+
+#include "iputils.hh"
+#include "libssl.hh"
+#include "noinitvector.hh"
+#include "stat_t.hh"
+
+struct DOHServerConfig;
+
+class DOHResponseMapEntry
+{
+public:
+ DOHResponseMapEntry(const std::string& regex, uint16_t status, const PacketBuffer& content, const boost::optional<std::unordered_map<std::string, std::string>>& headers): d_regex(regex), d_customHeaders(headers), d_content(content), d_status(status)
+ {
+ if (status >= 400 && !d_content.empty() && d_content.at(d_content.size() -1) != 0) {
+ // we need to make sure it's null-terminated
+ d_content.push_back(0);
+ }
+ }
+
+ bool matches(const std::string& path) const
+ {
+ return d_regex.match(path);
+ }
+
+ uint16_t getStatusCode() const
+ {
+ return d_status;
+ }
+
+ const PacketBuffer& getContent() const
+ {
+ return d_content;
+ }
+
+ const boost::optional<std::unordered_map<std::string, std::string>>& getHeaders() const
+ {
+ return d_customHeaders;
+ }
+
+private:
+ Regex d_regex;
+ boost::optional<std::unordered_map<std::string, std::string>> d_customHeaders;
+ PacketBuffer d_content;
+ uint16_t d_status;
+};
+
+struct DOHFrontend
+{
+ DOHFrontend()
+ {
+ }
+
+ std::shared_ptr<DOHServerConfig> d_dsc{nullptr};
+ std::shared_ptr<std::vector<std::shared_ptr<DOHResponseMapEntry>>> d_responsesMap;
+ TLSConfig d_tlsConfig;
+ TLSErrorCounters d_tlsCounters;
+ std::string d_serverTokens{"h2o/dnsdist"};
+ std::unordered_map<std::string, std::string> d_customResponseHeaders;
+ ComboAddress d_local;
+
+ uint32_t d_idleTimeout{30}; // HTTP idle timeout in seconds
+ std::vector<std::string> d_urls;
+
+ pdns::stat_t d_httpconnects{0}; // number of TCP/IP connections established
+ pdns::stat_t d_getqueries{0}; // valid DNS queries received via GET
+ pdns::stat_t d_postqueries{0}; // valid DNS queries received via POST
+ pdns::stat_t d_badrequests{0}; // request could not be converted to dns query
+ pdns::stat_t d_errorresponses{0}; // dnsdist set 'error' on response
+ pdns::stat_t d_redirectresponses{0}; // dnsdist set 'redirect' on response
+ pdns::stat_t d_validresponses{0}; // valid responses sent out
+
+ struct HTTPVersionStats
+ {
+ pdns::stat_t d_nbQueries{0}; // valid DNS queries received
+ pdns::stat_t d_nb200Responses{0};
+ pdns::stat_t d_nb400Responses{0};
+ pdns::stat_t d_nb403Responses{0};
+ pdns::stat_t d_nb500Responses{0};
+ pdns::stat_t d_nb502Responses{0};
+ pdns::stat_t d_nbOtherResponses{0};
+ };
+
+ HTTPVersionStats d_http1Stats;
+ HTTPVersionStats d_http2Stats;
+#ifdef __linux__
+ // On Linux this gives us 128k pending queries (default is 8192 queries),
+ // which should be enough to deal with huge spikes
+ uint32_t d_internalPipeBufferSize{1024*1024};
+#else
+ uint32_t d_internalPipeBufferSize{0};
+#endif
+ 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};
+ bool d_keepIncomingHeaders{false};
+
+ time_t getTicketsKeyRotationDelay() const
+ {
+ return d_tlsConfig.d_ticketsKeyRotationDelay;
+ }
+
+ bool isHTTPS() const
+ {
+ return !d_tlsConfig.d_certKeyPairs.empty();
+ }
+
+#ifndef HAVE_DNS_OVER_HTTPS
+ void setup()
+ {
+ }
+
+ void reloadCertificates()
+ {
+ }
+
+ void rotateTicketsKey(time_t /* now */)
+ {
+ }
+
+ void loadTicketsKeys(const std::string& /* keyFile */)
+ {
+ }
+
+ void handleTicketsKeyRotation()
+ {
+ }
+
+ time_t getNextTicketsKeyRotation() const
+ {
+ return 0;
+ }
+
+ size_t getTicketsKeysCount() const
+ {
+ size_t res = 0;
+ return res;
+ }
+
+#else
+ void setup();
+ void reloadCertificates();
+
+ void rotateTicketsKey(time_t now);
+ void loadTicketsKeys(const std::string& keyFile);
+ void handleTicketsKeyRotation();
+ time_t getNextTicketsKeyRotation() const;
+ size_t getTicketsKeysCount() const;
+#endif /* HAVE_DNS_OVER_HTTPS */
+};
+
+#ifndef HAVE_DNS_OVER_HTTPS
+struct DOHUnit
+{
+ static void release(DOHUnit*)
+ {
+ }
+
+ void get()
+ {
+ }
+
+ void release()
+ {
+ }
+
+ size_t proxyProtocolPayloadSize{0};
+ uint16_t status_code{200};
+};
+
+#else /* HAVE_DNS_OVER_HTTPS */
+#include <unordered_map>
+
+#include "dnsdist-idstate.hh"
+
+struct st_h2o_req_t;
+struct DownstreamState;
+
+struct DOHUnit
+{
+ DOHUnit(PacketBuffer&& q, std::string&& p, std::string&& h): path(std::move(p)), host(std::move(h)), query(std::move(q))
+ {
+ ids.ednsAdded = false;
+ }
+
+ DOHUnit(const DOHUnit&) = delete;
+ DOHUnit& operator=(const DOHUnit&) = delete;
+
+ void get()
+ {
+ ++d_refcnt;
+ }
+
+ void release()
+ {
+ if (--d_refcnt == 0) {
+ if (self) {
+ *self = nullptr;
+ }
+
+ delete this;
+ }
+ }
+
+ static void release(DOHUnit* ptr)
+ {
+ if (ptr) {
+ ptr->release();
+ }
+ }
+
+ InternalQueryState ids;
+ std::string sni;
+ std::string path;
+ std::string scheme;
+ std::string host;
+ std::string contentType;
+ PacketBuffer query;
+ PacketBuffer response;
+ std::shared_ptr<DownstreamState> downstream{nullptr};
+ std::unique_ptr<std::unordered_map<std::string, std::string>> headers;
+ st_h2o_req_t* req{nullptr};
+ DOHUnit** self{nullptr};
+ DOHServerConfig* dsc{nullptr};
+ std::atomic<uint64_t> d_refcnt{1};
+ size_t query_at{0};
+ size_t proxyProtocolPayloadSize{0};
+ int rsock{-1};
+ /* the status_code is set from
+ processDOHQuery() (which is executed in
+ the DOH client thread) so that the correct
+ response can be sent in on_dnsdist(),
+ after the DOHUnit has been passed back to
+ the main DoH thread.
+ */
+ uint16_t status_code{200};
+ /* whether the query was re-sent to the backend over
+ TCP after receiving a truncated answer over UDP */
+ bool tcp{false};
+ bool truncated{false};
+
+ std::string getHTTPPath() const;
+ std::string getHTTPHost() const;
+ std::string getHTTPScheme() const;
+ std::string getHTTPQueryString() const;
+ std::unordered_map<std::string, std::string> getHTTPHeaders() const;
+ void setHTTPResponse(uint16_t statusCode, PacketBuffer&& body, const std::string& contentType="");
+};
+
+void handleUDPResponseForDoH(std::unique_ptr<DOHUnit, void(*)(DOHUnit*)>&&, PacketBuffer&& response, InternalQueryState&& state);
+
+struct CrossProtocolQuery;
+struct DNSQuestion;
+
+std::unique_ptr<CrossProtocolQuery> getDoHCrossProtocolQueryFromDQ(DNSQuestion& dq, bool isResponse);
+
+#endif /* HAVE_DNS_OVER_HTTPS */
+
+using DOHUnitUniquePtr = std::unique_ptr<DOHUnit, void(*)(DOHUnit*)>;
+
+void handleDOHTimeout(DOHUnitUniquePtr&& oldDU);