Line | Count | Source (jump to first uncovered line) |
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 <unordered_map> |
25 | | |
26 | | #include "iputils.hh" |
27 | | #include "libssl.hh" |
28 | | #include "noinitvector.hh" |
29 | | #include "stat_t.hh" |
30 | | |
31 | | struct DOHServerConfig; |
32 | | |
33 | | class DOHResponseMapEntry |
34 | | { |
35 | | public: |
36 | | 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) |
37 | 0 | { |
38 | 0 | if (status >= 400 && !d_content.empty() && d_content.at(d_content.size() -1) != 0) { |
39 | 0 | // we need to make sure it's null-terminated |
40 | 0 | d_content.push_back(0); |
41 | 0 | } |
42 | 0 | } |
43 | | |
44 | | bool matches(const std::string& path) const |
45 | 0 | { |
46 | 0 | return d_regex.match(path); |
47 | 0 | } |
48 | | |
49 | | uint16_t getStatusCode() const |
50 | 0 | { |
51 | 0 | return d_status; |
52 | 0 | } |
53 | | |
54 | | const PacketBuffer& getContent() const |
55 | 0 | { |
56 | 0 | return d_content; |
57 | 0 | } |
58 | | |
59 | | const boost::optional<std::unordered_map<std::string, std::string>>& getHeaders() const |
60 | 0 | { |
61 | 0 | return d_customHeaders; |
62 | 0 | } |
63 | | |
64 | | private: |
65 | | Regex d_regex; |
66 | | boost::optional<std::unordered_map<std::string, std::string>> d_customHeaders; |
67 | | PacketBuffer d_content; |
68 | | uint16_t d_status; |
69 | | }; |
70 | | |
71 | | struct DOHFrontend |
72 | | { |
73 | | DOHFrontend() |
74 | 0 | { |
75 | 0 | } |
76 | | |
77 | | std::shared_ptr<DOHServerConfig> d_dsc{nullptr}; |
78 | | std::shared_ptr<std::vector<std::shared_ptr<DOHResponseMapEntry>>> d_responsesMap; |
79 | | TLSConfig d_tlsConfig; |
80 | | TLSErrorCounters d_tlsCounters; |
81 | | std::string d_serverTokens{"h2o/dnsdist"}; |
82 | | std::unordered_map<std::string, std::string> d_customResponseHeaders; |
83 | | ComboAddress d_local; |
84 | | |
85 | | uint32_t d_idleTimeout{30}; // HTTP idle timeout in seconds |
86 | | std::vector<std::string> d_urls; |
87 | | |
88 | | pdns::stat_t d_httpconnects{0}; // number of TCP/IP connections established |
89 | | pdns::stat_t d_getqueries{0}; // valid DNS queries received via GET |
90 | | pdns::stat_t d_postqueries{0}; // valid DNS queries received via POST |
91 | | pdns::stat_t d_badrequests{0}; // request could not be converted to dns query |
92 | | pdns::stat_t d_errorresponses{0}; // dnsdist set 'error' on response |
93 | | pdns::stat_t d_redirectresponses{0}; // dnsdist set 'redirect' on response |
94 | | pdns::stat_t d_validresponses{0}; // valid responses sent out |
95 | | |
96 | | struct HTTPVersionStats |
97 | | { |
98 | | pdns::stat_t d_nbQueries{0}; // valid DNS queries received |
99 | | pdns::stat_t d_nb200Responses{0}; |
100 | | pdns::stat_t d_nb400Responses{0}; |
101 | | pdns::stat_t d_nb403Responses{0}; |
102 | | pdns::stat_t d_nb500Responses{0}; |
103 | | pdns::stat_t d_nb502Responses{0}; |
104 | | pdns::stat_t d_nbOtherResponses{0}; |
105 | | }; |
106 | | |
107 | | HTTPVersionStats d_http1Stats; |
108 | | HTTPVersionStats d_http2Stats; |
109 | | #ifdef __linux__ |
110 | | // On Linux this gives us 128k pending queries (default is 8192 queries), |
111 | | // which should be enough to deal with huge spikes |
112 | | uint32_t d_internalPipeBufferSize{1024*1024}; |
113 | | #else |
114 | | uint32_t d_internalPipeBufferSize{0}; |
115 | | #endif |
116 | | bool d_sendCacheControlHeaders{true}; |
117 | | bool d_trustForwardedForHeader{false}; |
118 | | /* whether we require tue query path to exactly match one of configured ones, |
119 | | or accept everything below these paths. */ |
120 | | bool d_exactPathMatching{true}; |
121 | | bool d_keepIncomingHeaders{false}; |
122 | | |
123 | | time_t getTicketsKeyRotationDelay() const |
124 | 0 | { |
125 | 0 | return d_tlsConfig.d_ticketsKeyRotationDelay; |
126 | 0 | } |
127 | | |
128 | | bool isHTTPS() const |
129 | 0 | { |
130 | 0 | return !d_tlsConfig.d_certKeyPairs.empty(); |
131 | 0 | } |
132 | | |
133 | | #ifndef HAVE_DNS_OVER_HTTPS |
134 | | void setup() |
135 | 0 | { |
136 | 0 | } |
137 | | |
138 | | void reloadCertificates() |
139 | 0 | { |
140 | 0 | } |
141 | | |
142 | | void rotateTicketsKey(time_t /* now */) |
143 | 0 | { |
144 | 0 | } |
145 | | |
146 | | void loadTicketsKeys(const std::string& /* keyFile */) |
147 | 0 | { |
148 | 0 | } |
149 | | |
150 | | void handleTicketsKeyRotation() |
151 | 0 | { |
152 | 0 | } |
153 | | |
154 | | time_t getNextTicketsKeyRotation() const |
155 | 0 | { |
156 | 0 | return 0; |
157 | 0 | } |
158 | | |
159 | | size_t getTicketsKeysCount() const |
160 | 0 | { |
161 | 0 | size_t res = 0; |
162 | 0 | return res; |
163 | 0 | } |
164 | | |
165 | | #else |
166 | | void setup(); |
167 | | void reloadCertificates(); |
168 | | |
169 | | void rotateTicketsKey(time_t now); |
170 | | void loadTicketsKeys(const std::string& keyFile); |
171 | | void handleTicketsKeyRotation(); |
172 | | time_t getNextTicketsKeyRotation() const; |
173 | | size_t getTicketsKeysCount() const; |
174 | | #endif /* HAVE_DNS_OVER_HTTPS */ |
175 | | }; |
176 | | |
177 | | #ifndef HAVE_DNS_OVER_HTTPS |
178 | | struct DOHUnit |
179 | | { |
180 | | static void release(DOHUnit*) |
181 | 0 | { |
182 | 0 | } |
183 | | |
184 | | void get() |
185 | 0 | { |
186 | 0 | } |
187 | | |
188 | | void release() |
189 | 0 | { |
190 | 0 | } |
191 | | |
192 | | size_t proxyProtocolPayloadSize{0}; |
193 | | uint16_t status_code{200}; |
194 | | }; |
195 | | |
196 | | #else /* HAVE_DNS_OVER_HTTPS */ |
197 | | #include <unordered_map> |
198 | | |
199 | | #include "dnsdist-idstate.hh" |
200 | | |
201 | | struct st_h2o_req_t; |
202 | | struct DownstreamState; |
203 | | |
204 | | struct DOHUnit |
205 | | { |
206 | | DOHUnit(PacketBuffer&& q, std::string&& p, std::string&& h): path(std::move(p)), host(std::move(h)), query(std::move(q)) |
207 | | { |
208 | | ids.ednsAdded = false; |
209 | | } |
210 | | |
211 | | DOHUnit(const DOHUnit&) = delete; |
212 | | DOHUnit& operator=(const DOHUnit&) = delete; |
213 | | |
214 | | void get() |
215 | | { |
216 | | ++d_refcnt; |
217 | | } |
218 | | |
219 | | void release() |
220 | | { |
221 | | if (--d_refcnt == 0) { |
222 | | if (self) { |
223 | | *self = nullptr; |
224 | | } |
225 | | |
226 | | delete this; |
227 | | } |
228 | | } |
229 | | |
230 | | static void release(DOHUnit* ptr) |
231 | | { |
232 | | if (ptr) { |
233 | | ptr->release(); |
234 | | } |
235 | | } |
236 | | |
237 | | InternalQueryState ids; |
238 | | std::string sni; |
239 | | std::string path; |
240 | | std::string scheme; |
241 | | std::string host; |
242 | | std::string contentType; |
243 | | PacketBuffer query; |
244 | | PacketBuffer response; |
245 | | std::shared_ptr<DownstreamState> downstream{nullptr}; |
246 | | std::unique_ptr<std::unordered_map<std::string, std::string>> headers; |
247 | | st_h2o_req_t* req{nullptr}; |
248 | | DOHUnit** self{nullptr}; |
249 | | DOHServerConfig* dsc{nullptr}; |
250 | | std::atomic<uint64_t> d_refcnt{1}; |
251 | | size_t query_at{0}; |
252 | | size_t proxyProtocolPayloadSize{0}; |
253 | | int rsock{-1}; |
254 | | /* the status_code is set from |
255 | | processDOHQuery() (which is executed in |
256 | | the DOH client thread) so that the correct |
257 | | response can be sent in on_dnsdist(), |
258 | | after the DOHUnit has been passed back to |
259 | | the main DoH thread. |
260 | | */ |
261 | | uint16_t status_code{200}; |
262 | | /* whether the query was re-sent to the backend over |
263 | | TCP after receiving a truncated answer over UDP */ |
264 | | bool tcp{false}; |
265 | | bool truncated{false}; |
266 | | |
267 | | std::string getHTTPPath() const; |
268 | | std::string getHTTPHost() const; |
269 | | std::string getHTTPScheme() const; |
270 | | std::string getHTTPQueryString() const; |
271 | | std::unordered_map<std::string, std::string> getHTTPHeaders() const; |
272 | | void setHTTPResponse(uint16_t statusCode, PacketBuffer&& body, const std::string& contentType=""); |
273 | | }; |
274 | | |
275 | | void handleUDPResponseForDoH(std::unique_ptr<DOHUnit, void(*)(DOHUnit*)>&&, PacketBuffer&& response, InternalQueryState&& state); |
276 | | |
277 | | struct CrossProtocolQuery; |
278 | | struct DNSQuestion; |
279 | | |
280 | | std::unique_ptr<CrossProtocolQuery> getDoHCrossProtocolQueryFromDQ(DNSQuestion& dq, bool isResponse); |
281 | | |
282 | | #endif /* HAVE_DNS_OVER_HTTPS */ |
283 | | |
284 | | using DOHUnitUniquePtr = std::unique_ptr<DOHUnit, void(*)(DOHUnit*)>; |
285 | | |
286 | | void handleDOHTimeout(DOHUnitUniquePtr&& oldDU); |