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

PowerDNS / pdns / 18679017918

21 Oct 2025 09:15AM UTC coverage: 69.743% (+2.0%) from 67.713%
18679017918

Pull #16307

github

web-flow
Merge ba88af487 into da98764c6
Pull Request #16307: rec: explicit disabling/enabling of tls-gnutls for full and least configs and packages

26192 of 45526 branches covered (57.53%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

2282 existing lines in 57 files now uncovered.

86265 of 115719 relevant lines covered (74.55%)

4323875.05 hits per line

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

61.54
/pdns/ednscookies.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

23
#include "config.h"
24

25
#include "ednscookies.hh"
26
#include "dns_random.hh"
27
#include "iputils.hh"
28

29
#ifdef HAVE_CRYPTO_SHORTHASH
30
#include <sodium.h>
31
#endif
32

33
EDNSCookiesOpt::EDNSCookiesOpt(const std::string& option)
34
{
28✔
35
  getEDNSCookiesOptFromString(option.c_str(), option.length());
28✔
36
}
28✔
37

38
EDNSCookiesOpt::EDNSCookiesOpt(const char* option, unsigned int len)
39
{
1✔
40
  getEDNSCookiesOptFromString(option, len);
1✔
41
}
1✔
42

43
bool EDNSCookiesOpt::makeFromString(const std::string& option)
44
{
9✔
45
  getEDNSCookiesOptFromString(option.c_str(), option.length());
9✔
46
  return isWellFormed();
9✔
47
}
9✔
48

49
bool EDNSCookiesOpt::makeFromString(const char* option, unsigned int len)
50
{
1✔
51
  getEDNSCookiesOptFromString(option, len);
1✔
52
  return isWellFormed();
1✔
53
}
1✔
54

55
string EDNSCookiesOpt::makeOptString() const
56
{
32✔
57
  string ret;
32✔
58
  if (!isWellFormed()) {
32!
59
    return ret;
×
60
  }
×
61
  ret.assign(client);
32✔
62
  if (server.length() != 0) {
32✔
63
    ret.append(server);
31✔
64
  }
31✔
65
  return ret;
32✔
66
}
32✔
67

68
string EDNSCookiesOpt::toDisplayString() const
UNCOV
69
{
×
UNCOV
70
  std::ostringstream str;
×
UNCOV
71
  str << makeHexDump(client, "");
×
UNCOV
72
  if (!server.empty()) {
×
UNCOV
73
    str << '|';
×
UNCOV
74
    if (server.length() != 16) {
×
75
      // It isn't a rfc9018 one
76
      str << makeHexDump(server, "");
×
77
    }
×
UNCOV
78
    else {
×
79
      // It very likely is a rfc9018 one
UNCOV
80
      str << makeHexDump(server.substr(0, 1), ""); // Version
×
UNCOV
81
      str << '|';
×
UNCOV
82
      str << makeHexDump(server.substr(1, 3), ""); // Reserved
×
UNCOV
83
      str << '|';
×
UNCOV
84
      str << makeHexDump(server.substr(4, 4), ""); // Timestamp
×
UNCOV
85
      str << '|';
×
UNCOV
86
      str << makeHexDump(server.substr(8, 8), ""); // Hash
×
UNCOV
87
    }
×
UNCOV
88
  }
×
UNCOV
89
  return str.str();
×
UNCOV
90
}
×
91

92
void EDNSCookiesOpt::getEDNSCookiesOptFromString(const char* option, unsigned int len)
93
{
39✔
94
  client.clear();
39✔
95
  server.clear();
39✔
96
  if (len < 8) {
39✔
97
    return;
3✔
98
  }
3✔
99
  const std::string tmp(option, len);
36✔
100
  client = tmp.substr(0, 8);
36✔
101
  if (len > 8) {
36✔
102
    server = tmp.substr(8);
33✔
103
  }
33✔
104
}
36✔
105

106
bool EDNSCookiesOpt::isValid([[maybe_unused]] const string& secret, [[maybe_unused]] const ComboAddress& source) const
107
{
7✔
108
#ifdef HAVE_CRYPTO_SHORTHASH
7✔
109
  if (server.length() != 16 || client.length() != 8) {
7!
110
    return false;
3✔
111
  }
3✔
112
  if (server[0] != '\x01') {
4!
113
    // Version is not 1, can't verify
114
    return false;
115
  }
116
  uint32_t timestamp{};
4✔
117
  memcpy(&timestamp, &server[4], sizeof(timestamp));
4✔
118
  timestamp = ntohl(timestamp);
4✔
119
  // coverity[store_truncates_time_t]
120
  auto now = static_cast<uint32_t>(time(nullptr));
4✔
121
  // RFC 9018 section 4.3:
122
  //    The DNS server
123
  //    SHOULD allow cookies within a 1-hour period in the past and a
124
  //    5-minute period into the future
125
  if (rfc1982LessThan(now + 300, timestamp) && rfc1982LessThan(timestamp + 3600, now)) {
4!
126
    return false;
127
  }
128
  if (secret.length() != crypto_shorthash_KEYBYTES) {
4!
129
    return false;
130
  }
131

132
  string toHash = client + server.substr(0, 8) + source.toByteString();
4✔
133
  string hashResult;
4✔
134
  hashResult.resize(8);
4✔
135

136
  // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
137
  crypto_shorthash(reinterpret_cast<unsigned char*>(hashResult.data()),
4✔
138
                   reinterpret_cast<const unsigned char*>(toHash.data()),
4✔
139
                   toHash.length(),
4✔
140
                   reinterpret_cast<const unsigned char*>(secret.data()));
4✔
141
  // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
142
  return constantTimeStringEquals(server.substr(8), hashResult);
4✔
143
#else
144
  return false;
145
#endif
146
}
4✔
147

148
bool EDNSCookiesOpt::shouldRefresh() const
149
{
×
150
  if (server.size() < 16) {
×
151
    return true;
×
152
  }
×
153
  uint32_t timestamp{};
×
154
  memcpy(&timestamp, &server.at(4), sizeof(timestamp));
×
155
  timestamp = ntohl(timestamp);
×
156
  // coverity[store_truncates_time_t]
157
  auto now = static_cast<uint32_t>(time(nullptr));
×
158
  // RFC 9018 section 4.3:
159
  //    The DNS server
160
  //    SHOULD allow cookies within a 1-hour period in the past and a
161
  //    5-minute period into the future
162
  // If this is not the case, we need to refresh
163
  if (rfc1982LessThan(now + 300, timestamp) && rfc1982LessThan(timestamp + 3600, now)) {
×
164
    return true;
×
165
  }
×
166

167
  // RFC 9018 section 4.3:
168
  //    The DNS server SHOULD generate a new Server Cookie at least if the
169
  //     received Server Cookie from the client is more than half an hour old
170
  return rfc1982LessThan(timestamp + 1800, now);
×
171
}
×
172

173
void EDNSCookiesOpt::makeClientCookie()
UNCOV
174
{
×
UNCOV
175
  const size_t clientCookieSize = 8;
×
UNCOV
176
  client.resize(clientCookieSize);
×
UNCOV
177
  dns_random(client.data(), clientCookieSize);
×
UNCOV
178
}
×
179

180
bool EDNSCookiesOpt::makeServerCookie([[maybe_unused]] const string& secret, [[maybe_unused]] const ComboAddress& source)
181
{
2✔
182
#ifdef HAVE_CRYPTO_SHORTHASH
2✔
183
  static_assert(EDNSCookieSecretSize == crypto_shorthash_KEYBYTES * static_cast<size_t>(2), "The EDNSCookieSecretSize is not twice crypto_shorthash_KEYBYTES");
2✔
184

185
  if (isValid(secret, source) && !shouldRefresh()) {
2!
186
    return true;
187
  }
188

189
  if (secret.length() != crypto_shorthash_KEYBYTES) {
2✔
190
    return false;
1✔
191
  }
1✔
192

193
  server.clear();
1✔
194
  server.reserve(16);
1✔
195
  server = "\x01"; // Version
1✔
196
  server.resize(4, '\0'); // 3 reserved bytes
1✔
197
  // coverity[store_truncates_time_t]
198
  uint32_t now = htonl(static_cast<uint32_t>(time(nullptr)));
1✔
199
  // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
200
  server += string(reinterpret_cast<const char*>(&now), sizeof(now));
1✔
201
  server.resize(8);
1✔
202

203
  string toHash = client;
1✔
204
  toHash += server;
1✔
205
  toHash += source.toByteString();
1✔
206
  server.resize(16);
1✔
207
  crypto_shorthash(reinterpret_cast<unsigned char*>(&server.at(8)),
1✔
208
                   reinterpret_cast<const unsigned char*>(toHash.data()),
1✔
209
                   toHash.length(),
1✔
210
                   reinterpret_cast<const unsigned char*>(secret.data()));
1✔
211
  // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
212
  return true;
1✔
213
#else
214
  return false;
215
#endif
216
}
2✔
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