Coverage Report

Created: 2026-06-07 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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  */