/src/pdns/pdns/dnsdistdist/dnsdist-doh-common.hh
Line | Count | Source |
1 | | /* |
2 | | * This file is part of PowerDNS or dnsdist. |
3 | | * Copyright -- PowerDNS.COM B.V. and its contributors |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of version 2 of the GNU General Public License as |
7 | | * published by the Free Software Foundation. |
8 | | * |
9 | | * In addition, for the avoidance of any doubt, permission is granted to |
10 | | * link this program with OpenSSL and to (re)distribute the binaries |
11 | | * produced as the result of such linking. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
21 | | */ |
22 | | #pragma once |
23 | | |
24 | | #include <optional> |
25 | | #include <unordered_map> |
26 | | #include <set> |
27 | | #include <string_view> |
28 | | |
29 | | #include "config.h" |
30 | | #include "dolog.hh" |
31 | | #include "iputils.hh" |
32 | | #include "libssl.hh" |
33 | | #include "noinitvector.hh" |
34 | | #include "stat_t.hh" |
35 | | #include "tcpiohandler.hh" |
36 | | |
37 | | namespace dnsdist::doh |
38 | | { |
39 | | static constexpr uint32_t MAX_INCOMING_CONCURRENT_STREAMS{100U}; |
40 | | static constexpr size_t MAX_INCOMING_HTTP_HEADERS{256U}; |
41 | | |
42 | | std::optional<PacketBuffer> getPayloadFromPath(const std::string_view& path); |
43 | | } |
44 | | |
45 | | struct DOHServerConfig; |
46 | | |
47 | | class DOHResponseMapEntry |
48 | | { |
49 | | public: |
50 | | DOHResponseMapEntry(const std::string& regex, uint16_t status, const PacketBuffer& content, const std::optional<std::unordered_map<std::string, std::string>>& headers) : |
51 | | d_regex(regex), d_customHeaders(headers), d_content(content), d_status(status) |
52 | 0 | { |
53 | 0 | if (status >= 400 && !d_content.empty() && d_content.at(d_content.size() - 1) != 0) { |
54 | 0 | // we need to make sure it's null-terminated |
55 | 0 | d_content.push_back(0); |
56 | 0 | } |
57 | 0 | } |
58 | | |
59 | | bool matches(const std::string& path) const |
60 | 0 | { |
61 | 0 | return d_regex.match(path); |
62 | 0 | } |
63 | | |
64 | | uint16_t getStatusCode() const |
65 | 0 | { |
66 | 0 | return d_status; |
67 | 0 | } |
68 | | |
69 | | const PacketBuffer& getContent() const |
70 | 0 | { |
71 | 0 | return d_content; |
72 | 0 | } |
73 | | |
74 | | const std::optional<std::unordered_map<std::string, std::string>>& getHeaders() const |
75 | 0 | { |
76 | 0 | return d_customHeaders; |
77 | 0 | } |
78 | | |
79 | | private: |
80 | | Regex d_regex; |
81 | | std::optional<std::unordered_map<std::string, std::string>> d_customHeaders; |
82 | | PacketBuffer d_content; |
83 | | uint16_t d_status; |
84 | | }; |
85 | | |
86 | | struct DOHFrontend |
87 | | { |
88 | | DOHFrontend() : |
89 | | d_tlsContext(std::make_shared<TLSFrontend>(TLSFrontend::ALPN::DoH)) |
90 | 0 | { |
91 | 0 | } |
92 | | DOHFrontend(std::shared_ptr<TLSCtx> tlsCtx) : |
93 | | d_tlsContext(std::make_shared<TLSFrontend>(std::move(tlsCtx))) |
94 | 0 | { |
95 | 0 | } |
96 | | |
97 | | virtual ~DOHFrontend() |
98 | 0 | { |
99 | 0 | } |
100 | | |
101 | | std::shared_ptr<DOHServerConfig> d_dsc{nullptr}; |
102 | | std::shared_ptr<std::vector<std::shared_ptr<DOHResponseMapEntry>>> d_responsesMap; |
103 | | std::shared_ptr<TLSFrontend> d_tlsContext; |
104 | | std::shared_ptr<const Logr::Logger> d_logger; |
105 | | std::string d_serverTokens{"dnsdist"}; |
106 | | std::unordered_map<std::string, std::string> d_customResponseHeaders; |
107 | | std::string d_library; |
108 | | |
109 | | uint32_t d_idleTimeout{30}; // HTTP idle timeout in seconds |
110 | | std::set<std::string, std::less<>> d_urls; |
111 | | |
112 | | pdns::stat_t d_httpconnects{0}; // number of TCP/IP connections established |
113 | | pdns::stat_t d_getqueries{0}; // valid DNS queries received via GET |
114 | | pdns::stat_t d_postqueries{0}; // valid DNS queries received via POST |
115 | | pdns::stat_t d_badrequests{0}; // request could not be converted to dns query |
116 | | pdns::stat_t d_errorresponses{0}; // dnsdist set 'error' on response |
117 | | pdns::stat_t d_redirectresponses{0}; // dnsdist set 'redirect' on response |
118 | | pdns::stat_t d_validresponses{0}; // valid responses sent out |
119 | | |
120 | | struct HTTPVersionStats |
121 | | { |
122 | | pdns::stat_t d_nbQueries{0}; // valid DNS queries received |
123 | | pdns::stat_t d_nb200Responses{0}; |
124 | | pdns::stat_t d_nb400Responses{0}; |
125 | | pdns::stat_t d_nb403Responses{0}; |
126 | | pdns::stat_t d_nb500Responses{0}; |
127 | | pdns::stat_t d_nb502Responses{0}; |
128 | | pdns::stat_t d_nbOtherResponses{0}; |
129 | | }; |
130 | | |
131 | | HTTPVersionStats d_http1Stats; |
132 | | HTTPVersionStats d_http2Stats; |
133 | | #ifdef __linux__ |
134 | | // On Linux this gives us 128k pending queries (default is 8192 queries), |
135 | | // which should be enough to deal with huge spikes |
136 | | uint32_t d_internalPipeBufferSize{1024 * 1024}; |
137 | | #else |
138 | | uint32_t d_internalPipeBufferSize{0}; |
139 | | #endif |
140 | | bool d_sendCacheControlHeaders{true}; |
141 | | bool d_trustForwardedForHeader{false}; |
142 | | bool d_earlyACLDrop{true}; |
143 | | /* whether we require tue query path to exactly match one of configured ones, |
144 | | or accept everything below these paths. */ |
145 | | bool d_exactPathMatching{true}; |
146 | | bool d_keepIncomingHeaders{false}; |
147 | | |
148 | | time_t getTicketsKeyRotationDelay() const |
149 | 0 | { |
150 | 0 | return d_tlsContext->d_tlsConfig.d_ticketsKeyRotationDelay; |
151 | 0 | } |
152 | | |
153 | | bool isHTTPS() const |
154 | 0 | { |
155 | 0 | return !d_tlsContext->d_tlsConfig.d_certKeyPairs.empty(); |
156 | 0 | } |
157 | | |
158 | | const Logr::Logger& getLogger() const |
159 | 0 | { |
160 | 0 | return *d_logger; |
161 | 0 | } |
162 | | |
163 | | #ifndef HAVE_DNS_OVER_HTTPS |
164 | | virtual void setup() |
165 | 0 | { |
166 | 0 | } |
167 | | |
168 | | virtual void reloadCertificates() |
169 | 0 | { |
170 | 0 | } |
171 | | |
172 | | virtual void rotateTicketsKey(time_t /* now */) |
173 | 0 | { |
174 | 0 | } |
175 | | |
176 | | virtual void loadTicketsKeys(const std::string& /* keyFile */) |
177 | 0 | { |
178 | 0 | } |
179 | | |
180 | | virtual void loadTicketsKey(const std::string& /* key */) |
181 | 0 | { |
182 | 0 | } |
183 | | |
184 | | virtual void handleTicketsKeyRotation() |
185 | 0 | { |
186 | 0 | } |
187 | | |
188 | | virtual std::string getNextTicketsKeyRotation() |
189 | 0 | { |
190 | 0 | return std::string(); |
191 | 0 | } |
192 | | |
193 | | virtual size_t getTicketsKeysCount() const |
194 | 0 | { |
195 | 0 | size_t res = 0; |
196 | 0 | return res; |
197 | 0 | } |
198 | | |
199 | | #else |
200 | | virtual void setup(); |
201 | | virtual void reloadCertificates(); |
202 | | |
203 | | virtual void rotateTicketsKey(time_t now); |
204 | | virtual void loadTicketsKeys(const std::string& keyFile); |
205 | | virtual void loadTicketsKey(const std::string& key); |
206 | | virtual void handleTicketsKeyRotation(); |
207 | | virtual std::string getNextTicketsKeyRotation() const; |
208 | | virtual size_t getTicketsKeysCount(); |
209 | | #endif /* HAVE_DNS_OVER_HTTPS */ |
210 | | }; |
211 | | |
212 | | #include "dnsdist-idstate.hh" |
213 | | |
214 | | struct DownstreamState; |
215 | | |
216 | | class TCPQuerySender; |
217 | | |
218 | | #ifndef HAVE_DNS_OVER_HTTPS |
219 | | struct DOHUnitInterface |
220 | | { |
221 | | virtual ~DOHUnitInterface() |
222 | 0 | { |
223 | 0 | } |
224 | | |
225 | | virtual std::shared_ptr<TCPQuerySender> getQuerySender() const |
226 | 0 | { |
227 | 0 | return nullptr; |
228 | 0 | } |
229 | | |
230 | | static void handleTimeout(std::unique_ptr<DOHUnitInterface>) |
231 | 0 | { |
232 | 0 | } |
233 | | |
234 | | static void handleUDPResponse(std::unique_ptr<DOHUnitInterface>, PacketBuffer&&, InternalQueryState&&, const std::shared_ptr<DownstreamState>&) |
235 | 0 | { |
236 | 0 | } |
237 | | }; |
238 | | #else /* HAVE_DNS_OVER_HTTPS */ |
239 | | struct DOHUnitInterface |
240 | | { |
241 | | virtual ~DOHUnitInterface() |
242 | | { |
243 | | } |
244 | | |
245 | | virtual std::string getHTTPPath() const = 0; |
246 | | virtual std::string getHTTPQueryString() const = 0; |
247 | | virtual const std::string& getHTTPHost() const = 0; |
248 | | virtual const std::string& getHTTPScheme() const = 0; |
249 | | virtual const std::unordered_map<std::string, std::string>& getHTTPHeaders() const = 0; |
250 | | virtual std::shared_ptr<TCPQuerySender> getQuerySender() const = 0; |
251 | | virtual void setHTTPResponse(uint16_t statusCode, PacketBuffer&& body, const std::string& contentType = "") = 0; |
252 | | virtual void handleTimeout() = 0; |
253 | | virtual void handleUDPResponse(PacketBuffer&& response, InternalQueryState&& state, const std::shared_ptr<DownstreamState>&) = 0; |
254 | | |
255 | | static void handleTimeout(std::unique_ptr<DOHUnitInterface> unit) |
256 | | { |
257 | | if (unit) { |
258 | | auto* ptr = unit.release(); |
259 | | ptr->handleTimeout(); |
260 | | } |
261 | | } |
262 | | |
263 | | static void handleUDPResponse(std::unique_ptr<DOHUnitInterface> unit, PacketBuffer&& response, InternalQueryState&& state, const std::shared_ptr<DownstreamState>& ds) |
264 | | { |
265 | | if (unit) { |
266 | | auto* ptr = unit.release(); |
267 | | ptr->handleUDPResponse(std::move(response), std::move(state), ds); |
268 | | } |
269 | | } |
270 | | |
271 | | std::shared_ptr<DownstreamState> downstream{nullptr}; |
272 | | }; |
273 | | #endif /* HAVE_DNS_OVER_HTTPS */ |