Coverage Report

Created: 2026-03-08 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/svc-records.cc
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
#include "svc-records.hh"
23
#include "misc.hh"
24
#include "base64.hh"
25
26
const std::map<std::string, SvcParam::SvcParamKey> SvcParam::SvcParams = {
27
  {"mandatory", SvcParam::SvcParamKey::mandatory},
28
  {"alpn", SvcParam::SvcParamKey::alpn},
29
  {"no-default-alpn", SvcParam::SvcParamKey::no_default_alpn},
30
  {"port", SvcParam::SvcParamKey::port},
31
  {"ipv4hint", SvcParam::SvcParamKey::ipv4hint},
32
  {"ech", SvcParam::SvcParamKey::ech},
33
  {"ipv6hint", SvcParam::SvcParamKey::ipv6hint},
34
  {"dohpath", SvcParam::SvcParamKey::dohpath},
35
  {"ohttp", SvcParam::SvcParamKey::ohttp},
36
  {"tls-supported-groups", SvcParam::SvcParamKey::tls_supported_groups},
37
};
38
39
SvcParam::SvcParamKey SvcParam::keyFromString(const std::string& k)
40
0
{
41
0
  bool ignored;
42
0
  return SvcParam::keyFromString(k, ignored);
43
0
}
44
45
SvcParam::SvcParamKey SvcParam::keyFromString(const std::string& k, bool& generic)
46
0
{
47
0
  auto it = SvcParams.find(k);
48
0
  if (it != SvcParams.end()) {
49
0
    generic = false;
50
0
    return it->second;
51
0
  }
52
0
  if (k.substr(0, 3) == "key") {
53
0
    try {
54
0
      generic = true;
55
0
      return SvcParam::SvcParamKey(pdns::checked_stoi<uint16_t>(k.substr(3)));
56
0
    }
57
0
    catch (...) {
58
0
    }
59
0
  }
60
0
  throw std::invalid_argument("SvcParam '" + k + "' is not recognized or in keyNNNN format");
61
0
}
62
63
std::string SvcParam::keyToString(const SvcParam::SvcParamKey& k)
64
9
{
65
57
  auto ret = std::find_if(SvcParams.begin(), SvcParams.end(), [&](const std::pair<std::string, SvcParam::SvcParamKey>& e) { return e.second == k; });
66
9
  if (ret != SvcParams.end()) {
67
9
    return ret->first;
68
9
  }
69
0
  return "key" + std::to_string(k);
70
9
}
71
72
SvcParam::SvcParam(const SvcParamKey& key)
73
413
{
74
413
  d_key = key;
75
413
  if (d_key != SvcParamKey::no_default_alpn && d_key != SvcParamKey::ohttp) {
76
0
    throw std::invalid_argument("can not create non-empty SvcParam for key '" + keyToString(key) + "'");
77
0
  }
78
413
}
79
80
SvcParam::SvcParam(const SvcParamKey& key, const std::string& value)
81
3.10k
{
82
3.10k
  d_key = key;
83
3.10k
  if (d_key != SvcParamKey::ech && d_key != SvcParamKey::dohpath && d_key < 10) {
84
0
    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a string value");
85
0
  }
86
3.10k
  if (d_key == SvcParamKey::ech) {
87
588
    std::string d;
88
    // TODO check Base64 decode
89
588
    d_ech = value;
90
588
    return;
91
588
  }
92
2.52k
  d_value = value;
93
2.52k
}
94
95
SvcParam::SvcParam(const SvcParamKey& key, std::vector<std::string>&& value)
96
1.11k
{
97
1.11k
  d_key = key;
98
1.11k
  if (d_key != SvcParamKey::alpn) {
99
0
    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a string-set value");
100
0
  }
101
1.11k
  if (d_key == SvcParamKey::alpn) {
102
1.11k
    d_alpn = std::move(value);
103
1.11k
  }
104
1.11k
}
105
106
SvcParam::SvcParam(const SvcParamKey& key, std::set<std::string>&& value)
107
0
{
108
0
  d_key = key;
109
0
  if (d_key != SvcParamKey::mandatory) {
110
0
    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a string-set value");
111
0
  }
112
0
  if (d_key == SvcParamKey::mandatory) {
113
0
    for (auto const& v : value) {
114
0
      d_mandatory.insert(keyFromString(v));
115
0
    }
116
0
  }
117
0
}
118
119
SvcParam::SvcParam(const SvcParamKey& key, std::set<SvcParam::SvcParamKey>&& value)
120
305
{
121
305
  d_key = key;
122
305
  if (d_key != SvcParamKey::mandatory) {
123
0
    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a SvcParamKey-set value");
124
0
  }
125
305
  d_mandatory = std::move(value);
126
305
}
127
128
SvcParam::SvcParam(const SvcParamKey& key, std::vector<ComboAddress>&& value)
129
1.10k
{
130
1.10k
  d_key = key;
131
1.10k
  if (d_key != SvcParamKey::ipv6hint && d_key != SvcParamKey::ipv4hint) {
132
0
    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with an IP address value");
133
0
  }
134
5.77k
  for (auto const& addr : value) {
135
5.77k
    if (d_key == SvcParam::ipv6hint && !addr.isIPv6()) {
136
0
      throw std::invalid_argument("non-IPv6 address ('" + addr.toString() + "') passed for " + keyToString(key));
137
0
    }
138
5.77k
    if (d_key == SvcParam::ipv4hint && !addr.isIPv4()) {
139
0
      throw std::invalid_argument("non-IPv4 address ('" + addr.toString() + "') passed for " + keyToString(key));
140
0
    }
141
5.77k
  }
142
1.10k
  d_ipHints = std::move(value);
143
1.10k
}
144
145
SvcParam::SvcParam(const SvcParamKey& key, std::vector<uint16_t>&& value) :
146
663
  d_key(key)
147
663
{
148
663
  if (d_key != SvcParamKey::tls_supported_groups) {
149
0
    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a uint16 value-vector");
150
0
  }
151
663
  d_tls_supported_groups = std::move(value);
152
663
}
153
154
SvcParam::SvcParam(const SvcParamKey& key, const uint16_t value)
155
1.33k
{
156
1.33k
  d_key = key;
157
1.33k
  if (d_key != SvcParamKey::port) {
158
0
    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a port value");
159
0
  }
160
1.33k
  d_port = value;
161
1.33k
}
162
163
//! This ensures an std::set<SvcParam> will be sorted by key (section 2.2 mandates this for wire format)
164
bool SvcParam::operator<(const SvcParam& other) const
165
5.94k
{
166
5.94k
  return this->d_key < other.getKey();
167
5.94k
}
168
169
bool SvcParam::operator==(const SvcParam& other) const
170
0
{
171
0
  if (this->getKey() != other.getKey()) {
172
0
    return false;
173
0
  }
174
0
  switch (this->d_key) {
175
0
  case SvcParamKey::mandatory:
176
0
    return this->getMandatory() == other.getMandatory();
177
0
  case SvcParamKey::alpn:
178
0
    return this->getALPN() == other.getALPN();
179
0
  case SvcParamKey::no_default_alpn: /* fallthrough */
180
0
  case SvcParamKey::ohttp:
181
0
    return true;
182
0
  case SvcParamKey::port:
183
0
    return this->getPort() == other.getPort();
184
0
  case SvcParamKey::ipv4hint: /* fallthrough */
185
0
  case SvcParamKey::ipv6hint:
186
0
    return (this->getIPHints() == other.getIPHints() && this->getAutoHint() == other.getAutoHint());
187
0
  case SvcParamKey::ech:
188
0
    return this->getECH() == other.getECH();
189
0
  case SvcParamKey::dohpath:
190
0
    return this->getValue() == other.getValue();
191
0
  case SvcParamKey::tls_supported_groups:
192
0
    return this->getTLSSupportedGroups() == other.getTLSSupportedGroups();
193
0
  default:
194
0
    return this->getValue() == other.getValue();
195
0
  }
196
0
}
197
198
bool SvcParam::operator!=(const SvcParam& other) const
199
0
{
200
0
  return !(*this == other);
201
0
}
202
203
const std::vector<ComboAddress>& SvcParam::getIPHints() const
204
0
{
205
0
  if (d_key != SvcParamKey::ipv6hint && d_key != SvcParamKey::ipv4hint) {
206
0
    throw std::invalid_argument("getIPHints called for non-IP address key '" + keyToString(d_key) + "'");
207
0
  }
208
0
  return d_ipHints;
209
0
}
210
211
uint16_t SvcParam::getPort() const
212
0
{
213
0
  if (d_key != SvcParam::port) {
214
0
    throw std::invalid_argument("getPort called for non-port key '" + keyToString(d_key) + "'");
215
0
  }
216
0
  return d_port;
217
0
}
218
219
const std::vector<std::string>& SvcParam::getALPN() const
220
0
{
221
0
  if (d_key != SvcParam::alpn) {
222
0
    throw std::invalid_argument("getALPN called for non-alpn key '" + keyToString(d_key) + "'");
223
0
  }
224
0
  return d_alpn;
225
0
}
226
227
const std::set<SvcParam::SvcParamKey>& SvcParam::getMandatory() const
228
0
{
229
0
  if (d_key != SvcParam::mandatory) {
230
0
    throw std::invalid_argument("getMandatory called for non-mandatory key '" + keyToString(d_key) + "'");
231
0
  }
232
0
  return d_mandatory;
233
0
}
234
235
const std::string& SvcParam::getECH() const
236
0
{
237
0
  if (d_key != SvcParam::ech) {
238
0
    throw std::invalid_argument("getECH called for non-ech key '" + keyToString(d_key) + "'");
239
0
  }
240
0
  return d_ech;
241
0
}
242
243
const std::string& SvcParam::getValue() const
244
0
{
245
0
  if (d_key != SvcParamKey::dohpath && d_key < 10) {
246
0
    throw std::invalid_argument("getValue called for non-single value key '" + keyToString(d_key) + "'");
247
0
  }
248
0
  return d_value;
249
0
}
250
251
const std::vector<uint16_t>& SvcParam::getTLSSupportedGroups() const
252
0
{
253
0
  if (d_key != SvcParam::tls_supported_groups) {
254
0
    throw std::invalid_argument("getTLSSupportedGroups called for non-tls-supported-groups key '" + keyToString(d_key) + "'");
255
0
  }
256
0
  return d_tls_supported_groups;
257
0
}