• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 19741624072

27 Nov 2025 03:45PM UTC coverage: 73.086% (+0.02%) from 73.065%
19741624072

Pull #16570

github

web-flow
Merge 08a2cdb1d into f94a3f63f
Pull Request #16570: rec: rewrite all unwrap calls in web.rs

38523 of 63408 branches covered (60.75%)

Branch coverage included in aggregate %.

128044 of 164496 relevant lines covered (77.84%)

6531485.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.17
/pdns/dnsdistdist/dnsdist-svc.cc
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 "dnsdist-svc.hh"
23
#include "dnsdist.hh"
24
#include "dnsdist-dnsparser.hh"
25
#include "dnsdist-ecs.hh"
26
#include "dnsdist-lua.hh"
27
#include "dnswriter.hh"
28
#include "svc-records.hh"
29

30
bool generateSVCPayload(std::vector<uint8_t>& payload, uint16_t priority, const DNSName& target, const std::set<uint16_t>& mandatoryParams, const std::vector<std::string>& alpns, bool noDefaultAlpn, std::optional<uint16_t> port, const std::string& ech, const std::vector<ComboAddress>& ipv4hints, const std::vector<ComboAddress>& ipv6hints, const std::vector<std::pair<uint16_t, std::string>>& additionalParams)
31
{
40✔
32
  // this is an _ordered_ set and the comparison operator is properly defined,
33
  // so the parameters will be ordered as defined in the RFC
34
  std::set<SvcParam> params;
40✔
35

36
  if (!mandatoryParams.empty()) {
40!
37
    std::set<SvcParam::SvcParamKey> mandatoryKeys;
40✔
38
    for (const auto& entry : mandatoryParams) {
40✔
39
      mandatoryKeys.insert(static_cast<SvcParam::SvcParamKey>(entry));
40✔
40
    }
40✔
41
    params.insert({SvcParam::SvcParamKey::mandatory, std::move(mandatoryKeys)});
40✔
42
  }
40✔
43

44
  if (!alpns.empty()) {
40!
45
    params.insert({SvcParam::SvcParamKey::alpn, std::vector<std::string>(alpns)});
40✔
46
  }
40✔
47

48
  if (noDefaultAlpn) {
40✔
49
    params.insert({SvcParam::SvcParamKey::no_default_alpn});
20✔
50
  }
20✔
51

52
  if (port) {
40!
53
    params.insert({SvcParam::SvcParamKey::port, *port});
40✔
54
  }
40✔
55

56
  if (!ipv4hints.empty()) {
40✔
57
    params.insert({SvcParam::SvcParamKey::ipv4hint, std::vector<ComboAddress>(ipv4hints)});
32✔
58
  }
32✔
59

60
  if (!ech.empty()) {
40✔
61
    params.insert({SvcParam::SvcParamKey::ech, ech});
6✔
62
  }
6✔
63

64
  if (!ipv6hints.empty()) {
40✔
65
    params.insert({SvcParam::SvcParamKey::ipv6hint, std::vector<ComboAddress>(ipv6hints)});
32✔
66
  }
32✔
67

68
  for (const auto& param : additionalParams) {
40✔
69
    params.insert({static_cast<SvcParam::SvcParamKey>(param.first), param.second});
21✔
70
  }
21✔
71

72
  if (priority == 0 && params.size() != 0) {
40!
73
    return false;
3✔
74
  }
3✔
75

76
  payload.clear();
37✔
77
  /* we will remove the header, question and record header parts later */
78
  DNSPacketWriter pw(payload, g_rootdnsname, QType::A, QClass::IN, 0);
37✔
79
  pw.startRecord(g_rootdnsname, QType::A, 60, QClass::IN, DNSResourceRecord::ANSWER, false);
37✔
80
  size_t offset = pw.size();
37✔
81
  pw.xfr16BitInt(priority);
37✔
82
  pw.xfrName(target, false);
37✔
83
  pw.xfrSvcParamKeyVals(params);
37✔
84
  pw.commit();
37✔
85

86
  if (payload.size() <= offset) {
37!
87
    return false;
×
88
  }
×
89

90
  payload.erase(payload.begin(), payload.begin() + offset);
37✔
91
  return true;
37✔
92
}
37✔
93

94
bool generateSVCPayload(std::vector<uint8_t>& payload, const SVCRecordParameters& parameters)
95
{
31✔
96
  return generateSVCPayload(payload, parameters.priority, parameters.target, parameters.mandatoryParams, parameters.alpns, parameters.noDefaultAlpn, parameters.port, parameters.ech, parameters.ipv4hints, parameters.ipv6hints, parameters.additionalParams);
31✔
97
}
31✔
98

99
struct SVCRecordParameters parseSVCParameters(const svcParamsLua_t& params)
100
{
17✔
101
  struct SVCRecordParameters parameters;
17✔
102
  for (const auto& p : params) {
97✔
103
    if (p.first == "mandatory") {
97✔
104
      for (auto const& entry : boost::get<std::vector<std::pair<int, std::string>>>(p.second)) {
17✔
105
        parameters.mandatoryParams.insert(SvcParam::keyFromString(entry.second));
17✔
106
      }
17✔
107
    }
17✔
108
    else if (p.first == "alpn") {
80✔
109
      for (auto const& entry : boost::get<std::vector<std::pair<int, std::string>>>(p.second)) {
17✔
110
        parameters.alpns.push_back(entry.second);
17✔
111
      }
17✔
112
    }
17✔
113
    else if (p.first == "noDefaultAlpn") {
63✔
114
      parameters.noDefaultAlpn = boost::get<bool>(p.second);
11✔
115
    }
11✔
116
    else if (p.first == "port") {
52✔
117
      parameters.port = boost::get<uint16_t>(p.second);
17✔
118
    }
17✔
119
    else if (p.first == "ipv4hint") {
35✔
120
      for (auto const& entry : boost::get<std::vector<std::pair<int, std::string>>>(p.second)) {
13✔
121
        parameters.ipv4hints.push_back(ComboAddress(entry.second));
13✔
122
      }
13✔
123
    }
13✔
124
    else if (p.first == "ech") {
22✔
125
      parameters.ech = boost::get<std::string>(p.second);
3✔
126
    }
3✔
127
    else if (p.first == "ipv6hint") {
19✔
128
      for (auto const& entry : boost::get<std::vector<std::pair<int, std::string>>>(p.second)) {
13✔
129
        parameters.ipv6hints.push_back(ComboAddress(entry.second));
13✔
130
      }
13✔
131
    }
13✔
132
    else {
6✔
133
      parameters.additionalParams.push_back({SvcParam::keyFromString(p.first), boost::get<std::string>(p.second)});
6✔
134
    }
6✔
135
  }
97✔
136
  return parameters;
17✔
137
}
17✔
138

139
namespace dnsdist::svc
140
{
141
bool generateSVCResponse(DNSQuestion& dnsQuestion, const std::vector<std::vector<uint8_t>>& svcRecordPayloads, const std::set<std::pair<DNSName, ComboAddress>>& additionals4, const std::set<std::pair<DNSName, ComboAddress>>& additionals6, const ResponseConfig& responseConfig)
142
{
19✔
143
  /* it will likely be a bit bigger than that because of additionals */
144
  size_t totalPayloadsSize = 0;
19✔
145
  for (const auto& payload : svcRecordPayloads) {
31✔
146
    totalPayloadsSize += payload.size();
31✔
147
  }
31✔
148
  const auto numberOfRecords = svcRecordPayloads.size();
19✔
149
  const auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
19✔
150
  if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totalPayloadsSize)) {
19!
151
    return false;
×
152
  }
×
153

154
  PacketBuffer newPacket;
19✔
155
  newPacket.reserve(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totalPayloadsSize);
19✔
156
  GenericDNSPacketWriter<PacketBuffer> packetWriter(newPacket, dnsQuestion.ids.qname, dnsQuestion.ids.qtype);
19✔
157
  for (const auto& payload : svcRecordPayloads) {
31✔
158
    packetWriter.startRecord(dnsQuestion.ids.qname, dnsQuestion.ids.qtype, responseConfig.ttl);
31✔
159
    packetWriter.xfrBlob(payload);
31✔
160
    packetWriter.commit();
31✔
161
  }
31✔
162

163
  if (newPacket.size() < dnsQuestion.getMaximumSize()) {
19!
164
    for (const auto& additional : additionals4) {
19✔
165
      packetWriter.startRecord(additional.first.isRoot() ? dnsQuestion.ids.qname : additional.first, QType::A, responseConfig.ttl, QClass::IN, DNSResourceRecord::ADDITIONAL);
19✔
166
      packetWriter.xfrCAWithoutPort(4, additional.second);
19✔
167
      packetWriter.commit();
19✔
168
    }
19✔
169
  }
19✔
170

171
  if (newPacket.size() < dnsQuestion.getMaximumSize()) {
19!
172
    for (const auto& additional : additionals6) {
19✔
173
      packetWriter.startRecord(additional.first.isRoot() ? dnsQuestion.ids.qname : additional.first, QType::AAAA, responseConfig.ttl, QClass::IN, DNSResourceRecord::ADDITIONAL);
19✔
174
      packetWriter.xfrCAWithoutPort(6, additional.second);
19✔
175
      packetWriter.commit();
19✔
176
    }
19✔
177
  }
19✔
178

179
  const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
19✔
180
  if (runtimeConfig.d_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
19!
181
    bool dnssecOK = ((dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
×
182
    packetWriter.addOpt(runtimeConfig.d_payloadSizeSelfGenAnswers, 0, dnssecOK ? EDNS_HEADER_FLAG_DO : 0);
×
183
    packetWriter.commit();
×
184
  }
×
185

186
  if (newPacket.size() >= dnsQuestion.getMaximumSize()) {
19!
187
    /* sorry! */
188
    return false;
×
189
  }
×
190

191
  packetWriter.getHeader()->id = dnsQuestion.getHeader()->id;
19✔
192
  packetWriter.getHeader()->qr = true; // for good measure
19✔
193
  setResponseHeadersFromConfig(*packetWriter.getHeader(), responseConfig);
19✔
194
  dnsQuestion.getMutableData() = std::move(newPacket);
19✔
195

196
  return true;
19✔
197
}
19✔
198

199
bool generateSVCResponse(DNSQuestion& dnsQuestion, uint32_t ttl, const std::vector<SVCRecordParameters>& parameters)
200
{
11✔
201
  std::vector<std::vector<uint8_t>> payloads;
11✔
202
  std::set<std::pair<DNSName, ComboAddress>> additionals4;
11✔
203
  std::set<std::pair<DNSName, ComboAddress>> additionals6;
11✔
204
  ResponseConfig responseConfig;
11✔
205
  responseConfig.setAA = true;
11✔
206
  responseConfig.ttl = ttl;
11✔
207

208
  payloads.reserve(parameters.size());
11✔
209
  for (const auto& parameter : parameters) {
17✔
210
    std::vector<uint8_t> payload;
17✔
211
    if (!generateSVCPayload(payload, parameter)) {
17!
212
      throw std::runtime_error("Unable to generate a valid SVC record from the supplied parameters");
×
213
    }
×
214

215
    payloads.push_back(std::move(payload));
17✔
216

217
    for (const auto& hint : parameter.ipv4hints) {
17✔
218
      additionals4.insert({parameter.target, ComboAddress(hint)});
13✔
219
    }
13✔
220

221
    for (const auto& hint : parameter.ipv6hints) {
17✔
222
      additionals6.insert({parameter.target, ComboAddress(hint)});
13✔
223
    }
13✔
224
  }
17✔
225

226
  return generateSVCResponse(dnsQuestion, payloads, additionals4, additionals6, responseConfig);
11✔
227
}
11✔
228
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc