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

PowerDNS / pdns / 14103313974

27 Mar 2025 09:35AM UTC coverage: 63.413% (+2.0%) from 61.378%
14103313974

Pull #15362

github

web-flow
Merge bbd1a19e0 into 72002bd86
Pull Request #15362: dnsdist: Do not register Xsk sockets on configuration check or client mode

41494 of 100080 branches covered (41.46%)

Branch coverage included in aggregate %.

6 of 7 new or added lines in 2 files covered. (85.71%)

56 existing lines in 11 files now uncovered.

128265 of 167624 relevant lines covered (76.52%)

3990104.75 hits per line

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

90.37
/pdns/rcpgenerator.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
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include "rcpgenerator.hh"
26
#include "dnsparser.hh"
27
#include "misc.hh"
28
#include "utility.hh"
29
#include <boost/algorithm/string.hpp>
30
#include <boost/algorithm/string/classification.hpp>
31
#include <boost/algorithm/string/replace.hpp>
32
#include <boost/format.hpp>
33

34
#include <iostream>
35
#include "base32.hh"
36
#include "base64.hh"
37
#include "namespaces.hh"
38

39
RecordTextReader::RecordTextReader(string str, DNSName zone) :
40
  d_string(std::move(str)), d_zone(std::move(zone))
41
{
1,657,879✔
42
   /* remove whitespace */
43
   if(!d_string.empty() && ( dns_isspace(*d_string.begin()) || dns_isspace(*d_string.rbegin()) ))
1,657,879!
44
     boost::trim_if(d_string, dns_isspace);
7✔
45
   d_end = d_string.size();
1,657,879✔
46
}
1,657,879✔
47

48
void RecordTextReader::xfr48BitInt(uint64_t &val)
49
{
6✔
50
  xfr64BitInt(val);
6✔
51
  if (val > 281474976710655LL)
6!
52
    throw RecordTextException("Overflow reading 48 bit integer from record content"); // fixme improve
×
53
}
6✔
54

55
void RecordTextReader::xfrNodeOrLocatorID(NodeOrLocatorID& val) {
12✔
56
  skipSpaces();
12✔
57
  size_t len;
12✔
58
  for(len=0;
12✔
59
      d_pos+len < d_string.length() && (isxdigit(d_string.at(d_pos+len)) || d_string.at(d_pos+len) == ':');
240!
60
      len++) ;   // find length of ID
228✔
61

62
  // Parse as v6, and then strip the final 64 zero bytes
63
  struct in6_addr tmpbuf;
12✔
64
  string to_parse = d_string.substr(d_pos, len) + ":0:0:0:0";
12✔
65

66
  if (inet_pton(AF_INET6, to_parse.c_str(), &tmpbuf) != 1) {
12!
67
    throw RecordTextException("while parsing colon-delimited 64-bit field: '" + d_string.substr(d_pos, len) + "' is invalid");
×
68
  }
×
69

70
  std::memcpy(&val.content, tmpbuf.s6_addr, sizeof(val.content));
12✔
71
  d_pos += len;
12✔
72
}
12✔
73

74
void RecordTextReader::xfr64BitInt(uint64_t &val)
75
{
625,646✔
76
  skipSpaces();
625,646✔
77

78
  if(!isdigit(d_string.at(d_pos)))
625,646!
79
    throw RecordTextException("expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
×
80

81
  size_t pos;
625,646✔
82
  val=std::stoull(d_string.substr(d_pos), &pos);
625,646✔
83

84
  d_pos += pos;
625,646✔
85
}
625,646✔
86

87

88
void RecordTextReader::xfr32BitInt(uint32_t &val)
89
{
2,316,810✔
90
  skipSpaces();
2,316,810✔
91

92
  if(!isdigit(d_string.at(d_pos)))
2,316,810!
93
    throw RecordTextException("expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
×
94

95
  size_t pos;
2,316,810✔
96
  val = pdns::checked_stoi<uint32_t>(d_string.c_str() + d_pos, &pos);
2,316,810✔
97

98
  d_pos += pos;
2,316,810✔
99
}
2,316,810✔
100

101
void RecordTextReader::xfrTime(uint32_t &val)
102
{
625,640✔
103
  struct tm tm;
625,640✔
104
  memset(&tm, 0, sizeof(tm));
625,640✔
105

106
  uint64_t itmp;
625,640✔
107
  xfr64BitInt(itmp);
625,640✔
108

109
  if (itmp <= (uint32_t)~0) {
625,640✔
110
    // formatted as seconds since epoch, not as YYYYMMDDHHmmSS:
111
    val = (uint32_t) itmp;
6✔
112
    return;
6✔
113
  }
6✔
114

115
  ostringstream tmp;
625,634✔
116

117
  tmp<<itmp;
625,634✔
118

119
  if (sscanf(tmp.str().c_str(), "%04d%02d%02d" "%02d%02d%02d",
625,634!
120
             &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
625,634✔
121
             &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
625,634✔
122
    throw RecordTextException("unable to parse '"+std::to_string(itmp)+"' into a valid time at position "+std::to_string(d_pos)+" in '"+d_string+"'");
×
123
  }
×
124

125
  tm.tm_year-=1900;
625,634✔
126
  tm.tm_mon-=1;
625,634✔
127
  // coverity[store_truncates_time_t]
128
  val=(uint32_t)Utility::timegm(&tm);
625,634✔
129
}
625,634✔
130

131
void RecordTextReader::xfrIP(uint32_t &val)
132
{
947,282✔
133
  skipSpaces();
947,282✔
134

135
  if(!isdigit(d_string.at(d_pos)))
947,282✔
136
    throw RecordTextException("while parsing IP address, expected digits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
4✔
137

138
  uint32_t octet=0;
947,278✔
139
  val=0;
947,278✔
140
  char count=0;
947,278✔
141
  bool last_was_digit = false;
947,278✔
142

143
  for(;;) {
11,856,466✔
144
    if(d_string.at(d_pos)=='.') {
11,856,466✔
145
      if (!last_was_digit)
2,841,810✔
146
        throw RecordTextException(string("unable to parse IP address, dot without previous digit"));
3✔
147
      last_was_digit = false;
2,841,807✔
148
      val<<=8;
2,841,807✔
149
      val+=octet;
2,841,807✔
150
      octet=0;
2,841,807✔
151
      count++;
2,841,807✔
152
      if(count > 3)
2,841,807✔
153
        throw RecordTextException(string("unable to parse IP address, too many dots"));
3✔
154
    }
2,841,807✔
155
    else if(isdigit(d_string.at(d_pos))) {
9,014,656✔
156
      last_was_digit = true;
9,014,650✔
157
      octet*=10;
9,014,650✔
158
      octet+=d_string.at(d_pos) - '0';
9,014,650✔
159
      if(octet > 255)
9,014,650✔
160
        throw RecordTextException("unable to parse IP address");
6✔
161
    }
9,014,650✔
162
    else if(dns_isspace(d_string.at(d_pos)) || d_string.at(d_pos) == ',')
6!
163
      break;
6✔
UNCOV
164
    else {
×
UNCOV
165
      throw RecordTextException(string("unable to parse IP address, strange character: ")+d_string.at(d_pos));
×
UNCOV
166
    }
×
167
    d_pos++;
11,856,448✔
168
    if(d_pos == d_string.length())
11,856,448✔
169
      break;
947,260✔
170
  }
11,856,448✔
171
  if (count != 3)
947,266✔
172
    throw RecordTextException(string("unable to parse IP address, not enough dots"));
3✔
173
  if (!last_was_digit)
947,263✔
174
    throw RecordTextException(string("unable to parse IP address, trailing dot"));
3✔
175
  val<<=8;
947,260✔
176
  val+=octet;
947,260✔
177
  val=ntohl(val);
947,260✔
178
}
947,260✔
179

180

181
void RecordTextReader::xfrIP6(std::string &val)
182
{
6,147✔
183
  struct in6_addr tmpbuf;
6,147✔
184

185
  skipSpaces();
6,147✔
186

187
  size_t len;
6,147✔
188
  // lookup end of value - think of ::ffff encoding too, has dots in it!
189
  for(len=0;
6,147✔
190
      d_pos+len < d_string.length() && (isxdigit(d_string.at(d_pos+len)) || d_string.at(d_pos+len) == ':' || d_string.at(d_pos+len)=='.');
103,921✔
191
    len++);
97,774✔
192

193
  if(!len)
6,147!
194
    throw RecordTextException("while parsing IPv6 address, expected xdigits at position "+std::to_string(d_pos)+" in '"+d_string+"'");
×
195

196
  // end of value is here, try parse as IPv6
197
  string address=d_string.substr(d_pos, len);
6,147✔
198

199
  if (inet_pton(AF_INET6, address.c_str(), &tmpbuf) != 1) {
6,147✔
200
    throw RecordTextException("while parsing IPv6 address: '" + address + "' is invalid");
9✔
201
  }
9✔
202

203
  val = std::string((char*)tmpbuf.s6_addr, 16);
6,138✔
204

205
  d_pos += len;
6,138✔
206
}
6,138✔
207

208
void RecordTextReader::xfrCAWithoutPort(uint8_t version, ComboAddress &val)
209
{
×
210
  if (version == 4) {
×
211
    uint32_t ip;
×
212
    xfrIP(ip);
×
213
    val = makeComboAddressFromRaw(4, string((const char*) &ip, 4));
×
214
  }
×
215
  else if (version == 6) {
×
216
    string ip;
×
217
    xfrIP6(ip);
×
218
    val = makeComboAddressFromRaw(6, ip);
×
219
  }
×
220
  else throw RecordTextException("invalid address family");
×
221
}
×
222

223
void RecordTextReader::xfrCAPort(ComboAddress &val)
224
{
×
225
  uint16_t port;
×
226
  xfr16BitInt(port);
×
227
  val.sin4.sin_port = port;
×
228
}
×
229

230
bool RecordTextReader::eof()
231
{
2,239,761✔
232
  return d_pos==d_end;
2,239,761✔
233
}
2,239,761✔
234

235
void RecordTextReader::xfr16BitInt(uint16_t &val)
236
{
644,993✔
237
  uint32_t tmp;
644,993✔
238
  xfr32BitInt(tmp);
644,993✔
239
  val=tmp;
644,993✔
240
  if(val!=tmp)
644,993!
241
    throw RecordTextException("Overflow reading 16 bit integer from record content"); // fixme improve
×
242
}
644,993✔
243

244
void RecordTextReader::xfr8BitInt(uint8_t &val)
245
{
1,268,152✔
246
  uint32_t tmp;
1,268,152✔
247
  xfr32BitInt(tmp);
1,268,152✔
248
  val=tmp;
1,268,152✔
249
  if(val!=tmp)
1,268,152!
250
    throw RecordTextException("Overflow reading 8 bit integer from record content"); // fixme improve
×
251
}
1,268,152✔
252

253
// this code should leave all the escapes around
254
void RecordTextReader::xfrName(DNSName& val, bool, bool)
255
{
393,108✔
256
  skipSpaces();
393,108✔
257
  DNSName sval;
393,108✔
258

259
  string::size_type begin_pos = d_pos;
393,108✔
260
  while (d_pos < d_end) {
4,598,431✔
261
    if (d_string[d_pos]!='\r' && dns_isspace(d_string[d_pos])) {
4,556,858!
262
      break;
351,535✔
263
    }
351,535✔
264

265
    d_pos++;
4,205,323✔
266
  }
4,205,323✔
267

268
  {
393,108✔
269
    std::string_view view(d_string);
393,108✔
270
    sval = DNSName(view.substr(begin_pos, d_pos - begin_pos));
393,108✔
271
  }
393,108✔
272

273
  if (sval.empty()) {
393,108!
274
    sval = d_zone;
×
275
  }
×
276
  else if (!d_zone.empty()) {
393,108✔
277
    sval += d_zone;
384,222✔
278
  }
384,222✔
279
  val = std::move(sval);
393,108✔
280
}
393,108✔
281

282
static bool isbase64(char c, bool acceptspace)
283
{
23,924,239✔
284
  if(dns_isspace(c))
23,924,239✔
285
    return acceptspace;
169✔
286
  if(c >= '0' && c <= '9')
23,924,070✔
287
    return true;
3,579,373✔
288
  if(c >= 'a' && c <= 'z')
20,344,697!
289
    return true;
9,663,320✔
290
  if(c >= 'A' && c <= 'Z')
10,681,377!
291
    return true;
9,446,582✔
292
  if(c=='+' || c=='/' || c=='=')
1,234,795✔
293
    return true;
1,234,791✔
294
  return false;
2,147,483,665✔
295
}
1,234,795✔
296

297
void RecordTextReader::xfrBlobNoSpaces(string& val, int len) {
36✔
298
  skipSpaces();
36✔
299
  int pos=(int)d_pos;
36✔
300
  const char* strptr=d_string.c_str();
36✔
301
  while(d_pos < d_end && isbase64(strptr[d_pos], false))
948✔
302
    d_pos++;
912✔
303

304
  string tmp;
36✔
305
  tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
36✔
306
  boost::erase_all(tmp," ");
36✔
307
  val.clear();
36✔
308
  B64Decode(tmp, val);
36✔
309

310
  if (len>-1 && val.size() != static_cast<size_t>(len))
36!
311
    throw RecordTextException("Record length "+std::to_string(val.size()) + " does not match expected length '"+std::to_string(len));
×
312
}
36✔
313

314
void RecordTextReader::xfrBlob(string& val, int)
315
{
314,104✔
316
  skipSpaces();
314,104✔
317
  int pos=(int)d_pos;
314,104✔
318
  const char* strptr=d_string.c_str();
314,104✔
319
  while(d_pos < d_end && isbase64(strptr[d_pos], true))
24,237,405✔
320
    d_pos++;
23,923,301✔
321

322
  string tmp;
314,104✔
323
  tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
314,104✔
324
  boost::erase_all(tmp," ");
314,104✔
325
  val.clear();
314,104✔
326
  B64Decode(tmp, val);
314,104✔
327
}
314,104✔
328

329
void RecordTextReader::xfrRFC1035CharString(string &val) {
295✔
330
  auto ctr = parseRFC1035CharString(d_string.substr(d_pos, d_end - d_pos), val);
295✔
331
  d_pos += ctr;
295✔
332
}
295✔
333

334
void RecordTextReader::xfrSVCBValueList(vector<string> &val) {
587✔
335
  auto ctr = parseSVCBValueList(d_string.substr(d_pos, d_end - d_pos), val);
587✔
336
  d_pos += ctr;
587✔
337
}
587✔
338

339
void RecordTextReader::xfrSvcParamKeyVals(set<SvcParam>& val) // NOLINT(readability-function-cognitive-complexity)
340
{
659✔
341
  while (d_pos != d_end) {
1,535✔
342
    skipSpaces();
942✔
343
    if (d_pos == d_end)
942!
344
      return;
×
345

346
    // Find the SvcParamKey
347
    size_t pos = d_pos;
942✔
348
    while (d_pos != d_end) {
5,382✔
349
      if (d_string.at(d_pos) == '=' || d_string.at(d_pos) == ' ') {
5,358!
350
        break;
918✔
351
      }
918✔
352
      d_pos++;
4,440✔
353
    }
4,440✔
354

355
    // We've reached a space or equals-sign or the end of the string (d_pos is at this char)
356
    string k = d_string.substr(pos, d_pos - pos);
942✔
357
    SvcParam::SvcParamKey key;
942✔
358
    bool generic;
942✔
359
    try {
942✔
360
      key = SvcParam::keyFromString(k, generic);
942✔
361
    } catch (const std::invalid_argument &e) {
942✔
362
      throw RecordTextException(e.what());
3✔
363
    }
3✔
364

365
    if (key != SvcParam::no_default_alpn) {
939✔
366
      if (d_pos == d_end || d_string.at(d_pos) != '=') {
927!
367
        throw RecordTextException("expected '=' after " + k);
12✔
368
      }
12✔
369
      d_pos++; // Now on the first character after '='
915✔
370
      if (d_pos == d_end || d_string.at(d_pos) == ' ') {
915✔
371
        throw RecordTextException("expected value after " + k + "=");
12✔
372
      }
12✔
373
    }
915✔
374

375
    switch (key) {
915✔
376
    case SvcParam::no_default_alpn:
12✔
377
      if (d_pos != d_end && d_string.at(d_pos) == '=') {
12!
378
        throw RecordTextException(k + " key can not have values");
3✔
379
      }
3✔
380
      val.insert(SvcParam(key));
9✔
381
      break;
9✔
382
    case SvcParam::ipv4hint: /* fall-through */
60✔
383
    case SvcParam::ipv6hint: {
105✔
384
      vector<ComboAddress> hints;
105✔
385
      bool doAuto{false};
105✔
386
      if (generic) {
105✔
387
        string value;
18✔
388
        xfrRFC1035CharString(value);
18✔
389
        size_t len = key == SvcParam::ipv4hint ? 4 : 16;
18✔
390
        if (value.size() % len != 0) {
18✔
391
          throw RecordTextException(k + " in generic format has wrong number of bytes");
6✔
392
        }
6✔
393
        for (size_t i=0; i<value.size(); i += len) {
30✔
394
          auto hint = makeComboAddressFromRaw(static_cast<uint8_t>(key), &value.at(i), len);
18✔
395
          hints.push_back(hint);
18✔
396
        }
18✔
397
      } else {
87✔
398
        vector<string> value;
87✔
399
        xfrSVCBValueList(value);
87✔
400
        for (auto const &v: value) {
126✔
401
          if (v == "auto") {
126✔
402
            doAuto = true;
16✔
403
            hints.clear();
16✔
404
            break;
16✔
405
          }
16✔
406
          hints.push_back(ComboAddress(v));
110✔
407
        }
110✔
408
      }
87✔
409
      try {
99✔
410
        auto p = SvcParam(key, std::move(hints));
99✔
411
        p.setAutoHint(doAuto);
99✔
412
        val.insert(std::move(p));
99✔
413
      }
99✔
414
      catch (const std::invalid_argument& e) {
99✔
415
        throw RecordTextException(e.what());
12✔
416
      }
12✔
417
      break;
87✔
418
    }
99✔
419
    case SvcParam::alpn: {
476✔
420
      vector<string> value;
476✔
421
      if (generic) {
476✔
422
        string v;
9✔
423
        xfrRFC1035CharString(v);
9✔
424
        size_t spos{0}, len;
9✔
425
        while (spos < v.length()) {
24✔
426
          len = v.at(spos);
21✔
427
          spos += 1;
21✔
428
          if (len > v.length() - spos) {
21✔
429
            throw RecordTextException("Length of ALPN value goes over total length of alpn SVC Param");
6✔
430
          }
6✔
431
          value.push_back(v.substr(spos, len));
15✔
432
          spos += len;
15✔
433
        }
15✔
434
      } else {
467✔
435
        xfrSVCBValueList(value);
467✔
436
      }
467✔
437
      val.insert(SvcParam(key, std::move(value)));
470✔
438
      break;
470✔
439
    }
476✔
440
    case SvcParam::mandatory: {
39✔
441
      if (generic) {
39✔
442
        string v;
6✔
443
        xfrRFC1035CharString(v);
6✔
444
        if (v.length() % 2 != 0) {
6✔
445
          throw RecordTextException("Wrong number of bytes in SVC Param " + k);
3✔
446
        }
3✔
447
        std::set<SvcParam::SvcParamKey> keys;
3✔
448
        for (size_t i=0; i < v.length(); i += 2) {
9✔
449
          uint16_t mand = (v.at(i) << 8);
6✔
450
          mand += v.at(i+1);
6✔
451
          keys.insert(SvcParam::SvcParamKey(mand));
6✔
452
        }
6✔
453
        val.insert(SvcParam(key, std::move(keys)));
3✔
454
        break;
3✔
455
      }
6✔
456
      vector<string> parts;
33✔
457
      xfrSVCBValueList(parts);
33✔
458
      set<string> values(parts.begin(), parts.end());
33✔
459
      val.insert(SvcParam(key, std::move(values)));
33✔
460
      break;
33✔
461
    }
39✔
462
    case SvcParam::port: {
226✔
463
      uint16_t port;
226✔
464
      if (generic) {
226✔
465
        string v;
3✔
466
        xfrRFC1035CharString(v);
3✔
467
        if (v.length() != 2) {
3!
468
          throw RecordTextException("port in generic format has the wrong length, expected 2, got " + std::to_string(v.length()));
×
469
        }
×
470
        port = (v.at(0) << 8);
3✔
471
        port += v.at(1);
3✔
472
      } else {
223✔
473
        string portstring;
223✔
474
        xfrRFC1035CharString(portstring);
223✔
475
        try {
223✔
476
          pdns::checked_stoi_into(port, portstring);
223✔
477
        } catch (const std::exception &e) {
223✔
478
          throw RecordTextException(e.what());
6✔
479
        }
6✔
480
      }
223✔
481
      val.insert(SvcParam(key, port));
220✔
482
      break;
220✔
483
    }
226✔
484
    case SvcParam::ech: {
24✔
485
      string value;
24✔
486
      if (generic) {
24✔
487
        xfrRFC1035CharString(value);
3✔
488
      } else {
21✔
489
        bool haveQuote = d_string.at(d_pos) == '"';
21✔
490
        if (haveQuote) {
21✔
491
          d_pos++;
18✔
492
        }
18✔
493
        xfrBlobNoSpaces(value);
21✔
494
        if (haveQuote) {
21✔
495
          if (d_string.at(d_pos) != '"') {
18!
496
            throw RecordTextException("ech value starts, but does not end with a '\"' symbol");
×
497
          }
×
498
          d_pos++;
18✔
499
        }
18✔
500
      }
21✔
501
      val.insert(SvcParam(key, value));
24✔
502
      break;
24✔
503
    }
24✔
504
    default: {
33✔
505
      string value;
33✔
506
      xfrRFC1035CharString(value);
33✔
507
      val.insert(SvcParam(key, value));
33✔
508
      break;
33✔
509
    }
24✔
510
    }
915✔
511
  }
915✔
512
}
659✔
513

514
static inline uint8_t hextodec(uint8_t val)
515
{
1,641,287✔
516
  if(val >= '0' && val<='9')
1,641,287!
517
    return val-'0';
313,280✔
518
  else if(val >= 'A' && val<='F')
1,328,008✔
519
    return 10+(val-'A');
8,494✔
520
  else if(val >= 'a' && val<='f')
1,319,514!
521
    return 10+(val-'a');
1,319,513✔
522
  else
×
523
    throw RecordTextException("Unknown hexadecimal character '"+std::to_string(val)+"'");
×
524
}
1,641,287✔
525

526

527
static void HEXDecode(const char* begin, const char* end, string& out)
528
{
319,926✔
529
  if(end - begin == 1 && *begin=='-') {
319,926!
530
    out.clear();
31✔
531
    return;
31✔
532
  }
31✔
533
  out.clear();
319,895✔
534
  out.reserve((end-begin)/2);
319,895✔
535
  uint8_t mode=0, val=0;
319,895✔
536
  for(; begin != end; ++begin) {
1,961,353✔
537
    if(!isalnum(*begin))
1,641,458✔
538
      continue;
170✔
539
    if(mode==0) {
1,641,288✔
540
      val = 16*hextodec(*begin);
820,645✔
541
      mode=1;
820,645✔
542
    } else {
820,646✔
543
      val += hextodec(*begin);
820,643✔
544
      out.append(1, (char) val);
820,643✔
545
      mode = 0;
820,643✔
546
      val = 0;
820,643✔
547
    }
820,643✔
548
  }
1,641,288✔
549
  if(mode)
319,895✔
550
    throw RecordTextException("Hexadecimal blob with odd number of characters");
3✔
551
}
319,895✔
552

553
void RecordTextReader::xfrHexBlob(string& val, bool keepReading)
554
{
319,926✔
555
  skipSpaces();
319,926✔
556
  int pos=(int)d_pos;
319,926✔
557
  while(d_pos < d_end && (keepReading || !dns_isspace(d_string[d_pos])))
1,961,414✔
558
    d_pos++;
1,641,488✔
559

560
  HEXDecode(d_string.c_str()+pos, d_string.c_str() + d_pos, val);
319,926✔
561
}
319,926✔
562

563
void RecordTextReader::xfrBase32HexBlob(string& val)
564
{
282,591✔
565
  skipSpaces();
282,591✔
566
  int pos=(int)d_pos;
282,591✔
567
  while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
8,365,359✔
568
    d_pos++;
8,082,768✔
569

570
  val=fromBase32Hex(string(d_string.c_str()+pos, d_pos-pos));
282,591✔
571
}
282,591✔
572

573

574
void RecordTextWriter::xfrBase32HexBlob(const string& val)
575
{
256,842✔
576
  if(!d_string.empty())
256,842!
577
    d_string.append(1,' ');
256,842✔
578

579
  d_string.append(toUpper(toBase32Hex(val)));
256,842✔
580
}
256,842✔
581

582

583
void RecordTextReader::xfrText(string& val, bool multi, bool /* lenField */)
584
{
8,133✔
585
  val.clear();
8,133✔
586
  val.reserve(d_end - d_pos);
8,133✔
587

588
  while(d_pos != d_end) {
12,096✔
589
    if(!val.empty())
8,827✔
590
      val.append(1, ' ');
696✔
591

592
    skipSpaces();
8,827✔
593
    if(d_string[d_pos]!='"') { // special case 'plenus' - without quotes
8,827✔
594
      string::size_type pos = d_pos;
55✔
595
      while(pos != d_end && isalnum(d_string[pos]))
1,921✔
596
        pos++;
1,866✔
597
      if(pos == d_end) {
55✔
598
        val.append(1, '"');
49✔
599
        val.append(d_string.c_str() + d_pos, d_end - d_pos);
49✔
600
        val.append(1, '"');
49✔
601
        d_pos = d_end;
49✔
602
        break;
49✔
603
      }
49✔
604
      throw RecordTextException("Data field in DNS should start with quote (\") at position "+std::to_string(d_pos)+" of '"+d_string+"'");
6✔
605
    }
55✔
606
    val.append(1, '"');
8,772✔
607
    while(++d_pos < d_end && d_string[d_pos]!='"') {
194,383!
608
      if(d_string[d_pos]=='\\' && d_pos+1!=d_end) {
185,611!
609
        val.append(1, d_string[d_pos++]);
1,822✔
610
      }
1,822✔
611
      val.append(1, d_string[d_pos]);
185,611✔
612
    }
185,611✔
613
    val.append(1,'"');
8,772✔
614
    if(d_pos == d_end)
8,772!
615
      throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'");
×
616
    d_pos++;
8,772✔
617
    if(!multi)
8,772✔
618
      break;
4,809✔
619
  }
8,772✔
620
}
8,133✔
621

622
void RecordTextReader::xfrUnquotedText(string& val, bool /* lenField */)
623
{
21✔
624
  val.clear();
21✔
625
  val.reserve(d_end - d_pos);
21✔
626

627
  if(!val.empty())
21!
628
    val.append(1, ' ');
×
629

630
  skipSpaces();
21✔
631
  val.append(1, d_string[d_pos]);
21✔
632
  while(++d_pos < d_end && d_string[d_pos] != ' '){
105!
633
    val.append(1, d_string[d_pos]);
84✔
634
  }
84✔
635
}
21✔
636

637
void RecordTextReader::xfrType(uint16_t& val)
638
{
924,677✔
639
  skipSpaces();
924,677✔
640
  int pos=(int)d_pos;
924,677✔
641
  while(d_pos < d_end && !dns_isspace(d_string[d_pos]))
3,827,933✔
642
    d_pos++;
2,903,256✔
643

644
  string tmp;
924,677✔
645
  tmp.assign(d_string.c_str()+pos, d_string.c_str() + d_pos);
924,677✔
646

647
  val=DNSRecordContent::TypeToNumber(tmp);
924,677✔
648
}
924,677✔
649

650

651
void RecordTextReader::skipSpaces()
652
{
6,140,128✔
653
  const char* strptr = d_string.c_str();
6,140,128✔
654
  while(d_pos < d_end && dns_isspace(strptr[d_pos]))
10,627,449✔
655
    d_pos++;
4,487,321✔
656
  if(d_pos == d_end)
6,140,128!
657
    throw RecordTextException("missing field at the end of record content '"+d_string+"'");
×
658
}
6,140,128✔
659

660

661
RecordTextWriter::RecordTextWriter(string& str, bool noDot) : d_string(str)
662
{
2,172,739✔
663
  d_string.clear();
2,172,739✔
664
  d_nodot=noDot;
2,172,739✔
665
}
2,172,739✔
666

667
void RecordTextWriter::xfrNodeOrLocatorID(const NodeOrLocatorID& val)
668
{
21✔
669
  if(!d_string.empty()) {
21✔
670
    d_string.append(1,' ');
18✔
671
  }
18✔
672

673
  size_t ctr = 0;
21✔
674
  char tmp[5];
21✔
675
  for (auto const &c : val.content) {
168✔
676
    snprintf(tmp, sizeof(tmp), "%02X", c);
168✔
677
    d_string+=tmp;
168✔
678
    ctr++;
168✔
679
    if (ctr % 2 == 0 && ctr != 8) {
168✔
680
      d_string+=':';
63✔
681
    }
63✔
682
  }
168✔
683
}
21✔
684

685
void RecordTextWriter::xfr48BitInt(const uint64_t& val)
686
{
81✔
687
  if(!d_string.empty())
81!
688
    d_string.append(1,' ');
81✔
689
  d_string+=std::to_string(val);
81✔
690
}
81✔
691

692

693
void RecordTextWriter::xfr32BitInt(const uint32_t& val)
694
{
4,354,326✔
695
  if(!d_string.empty())
4,354,326✔
696
    d_string.append(1,' ');
4,074,350✔
697
  d_string+=std::to_string(val);
4,354,326✔
698
}
4,354,326✔
699

700
void RecordTextWriter::xfrType(const uint16_t& val)
701
{
864,150✔
702
  if(!d_string.empty())
864,150!
703
    d_string.append(1,' ');
×
704
  d_string+=DNSRecordContent::NumberToType(val);
864,150✔
705
}
864,150✔
706

707
// this function is on the fast path for the pdns_recursor
708
void RecordTextWriter::xfrIP(const uint32_t& val)
709
{
840,940✔
710
  if(!d_string.empty())
840,940✔
711
    d_string.append(1,' ');
18✔
712

713
  char tmp[17];
840,940✔
714
  uint32_t ip=val;
840,940✔
715
  uint8_t vals[4];
840,940✔
716

717
  memcpy(&vals[0], &ip, sizeof(ip));
840,940✔
718

719
  char *pos=tmp;
840,940✔
720

721
  for(int n=0; n < 4; ++n) {
4,204,697✔
722
    if(vals[n]<10) {
3,363,757✔
723
      *(pos++)=vals[n]+'0';
877,818✔
724
    } else if(vals[n] < 100) {
2,489,795✔
725
      *(pos++)=(vals[n]/10) +'0';
351,145✔
726
      *(pos++)=(vals[n]%10) +'0';
351,145✔
727
    } else {
2,134,794✔
728
      *(pos++)=(vals[n]/100) +'0';
2,134,794✔
729
      vals[n]%=100;
2,134,794✔
730
      *(pos++)=(vals[n]/10) +'0';
2,134,794✔
731
      *(pos++)=(vals[n]%10) +'0';
2,134,794✔
732
    }
2,134,794✔
733
    if(n!=3)
3,363,757✔
734
      *(pos++)='.';
2,522,817✔
735
  }
3,363,757✔
736
  *pos=0;
840,940✔
737
  d_string.append(tmp, pos);
840,940✔
738
}
840,940✔
739

740
void RecordTextWriter::xfrIP6(const std::string& val)
741
{
2,316✔
742
  char tmpbuf[16];
2,316✔
743
  char addrbuf[40];
2,316✔
744

745
  if(!d_string.empty())
2,316✔
746
   d_string.append(1,' ');
12✔
747

748
  val.copy(tmpbuf,16);
2,316✔
749

750
  if (inet_ntop(AF_INET6, tmpbuf, addrbuf, sizeof addrbuf) == nullptr)
2,316!
751
    throw RecordTextException("Unable to convert to ipv6 address");
×
752

753
  d_string += std::string(addrbuf);
2,316✔
754
}
2,316✔
755

756
void RecordTextWriter::xfrCAWithoutPort(uint8_t /* version */, ComboAddress &val)
757
{
×
758
  string ip = val.toString();
×
759

760
  if(!d_string.empty())
×
761
    d_string.append(1,' ');
×
762

763
  d_string += ip;
×
764
}
×
765

766
void RecordTextWriter::xfrCAPort(ComboAddress &val)
767
{
×
768
  xfr16BitInt(val.sin4.sin_port);
×
769
}
×
770

771
void RecordTextWriter::xfrTime(const uint32_t& val)
772
{
1,728,300✔
773
  if(!d_string.empty())
1,728,300!
774
    d_string.append(1,' ');
1,728,300✔
775

776
  struct tm tm;
1,728,300✔
777
  time_t time=val; // Y2038 bug!
1,728,300✔
778
  gmtime_r(&time, &tm);
1,728,300✔
779

780
  static const boost::format fmt("%04d%02d%02d" "%02d%02d%02d");
1,728,300✔
781
  d_string += boost::str(boost::format(fmt) % (tm.tm_year+1900) % (tm.tm_mon+1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec);
1,728,300✔
782
}
1,728,300✔
783

784

785
void RecordTextWriter::xfr16BitInt(const uint16_t& val)
786
{
1,153,503✔
787
  xfr32BitInt(val);
1,153,503✔
788
}
1,153,503✔
789

790
void RecordTextWriter::xfr8BitInt(const uint8_t& val)
791
{
2,260,919✔
792
  xfr32BitInt(val);
2,260,919✔
793
}
2,260,919✔
794

795
// should not mess with the escapes
796
void RecordTextWriter::xfrName(const DNSName& val, bool /* unused */, bool /* noDot */)
797
{
1,054,451✔
798
  if(!d_string.empty())
1,054,451✔
799
    d_string.append(1,' ');
892,683✔
800

801
  if(d_nodot) {
1,054,451✔
802
    d_string+=val.toStringRootDot();
702,320✔
803
  }
702,320✔
804
  else
352,131✔
805
  {
352,131✔
806
    d_string+=val.toString();
352,131✔
807
  }
352,131✔
808
}
1,054,451✔
809

810
void RecordTextWriter::xfrBlobNoSpaces(const string& val, int size)
811
{
126✔
812
  xfrBlob(val, size);
126✔
813
}
126✔
814

815
void RecordTextWriter::xfrBlob(const string& val, int)
816
{
886,294✔
817
  if(!d_string.empty())
886,294✔
818
    d_string.append(1,' ');
868,485✔
819

820
  d_string+=Base64Encode(val);
886,294✔
821
}
886,294✔
822

823
void RecordTextWriter::xfrHexBlob(const string& val, bool)
824
{
261,956✔
825
  if(!d_string.empty())
261,956!
826
    d_string.append(1,' ');
261,956✔
827

828
  if(val.empty()) {
261,956✔
829
    d_string.append(1,'-');
5✔
830
    return;
5✔
831
  }
5✔
832

833
  string::size_type limit=val.size();
261,951✔
834
  char tmp[5];
261,951✔
835
  for(string::size_type n = 0; n < limit; ++n) {
904,014✔
836
    snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)val[n]);
642,063✔
837
    d_string+=tmp;
642,063✔
838
  }
642,063✔
839
}
261,951✔
840

841
// FIXME copied from dnsparser.cc, see #6010 and #3503 if you want a proper solution
842
static string txtEscape(const string &name)
843
{
1,311✔
844
  string ret;
1,311✔
845
  char ebuf[5];
1,311✔
846

847
  for(char i : name) {
3,105✔
848
    if((unsigned char) i >= 127 || (unsigned char) i < 32) {
3,105!
849
      snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)i);
24✔
850
      ret += ebuf;
24✔
851
    }
24✔
852
    else if(i=='"' || i=='\\'){
3,081!
853
      ret += '\\';
18✔
854
      ret += i;
18✔
855
    }
18✔
856
    else
3,063✔
857
      ret += i;
3,063✔
858
  }
3,105✔
859
  return ret;
1,311✔
860
}
1,311✔
861

862
void RecordTextWriter::xfrSVCBValueList(const vector<string> &val) {
1,016✔
863
  bool shouldQuote{false};
1,016✔
864
  vector<string> escaped;
1,016✔
865
  escaped.reserve(val.size());
1,016✔
866
  for (auto const &v : val) {
1,263✔
867
    if (v.find_first_of(' ') != string::npos) {
1,263✔
868
      shouldQuote = true;
6✔
869
    }
6✔
870
    string tmp = txtEscape(v);
1,263✔
871
    string unescaped;
1,263✔
872
    unescaped.reserve(tmp.size() + 4);
1,263✔
873
    for (auto const &ch : tmp) {
2,736✔
874
      if (ch == '\\') {
2,736✔
875
        unescaped += R"F(\\)F";
36✔
876
        continue;
36✔
877
      }
36✔
878
      if (ch == ',') {
2,700✔
879
        unescaped += R"F(\\,)F";
30✔
880
        continue;
30✔
881
      }
30✔
882
      unescaped += ch;
2,670✔
883
    }
2,670✔
884
    escaped.push_back(std::move(unescaped));
1,263✔
885
  }
1,263✔
886
  if (shouldQuote) {
1,016✔
887
    d_string.append(1, '"');
6✔
888
  }
6✔
889
  d_string.append(boost::join(escaped, ","));
1,016✔
890
  if (shouldQuote) {
1,016✔
891
    d_string.append(1, '"');
6✔
892
  }
6✔
893
}
1,016✔
894

895
void RecordTextWriter::xfrSvcParamKeyVals(const set<SvcParam>& val) {
1,160✔
896
  for (auto const &param : val) {
1,754✔
897
    if (!d_string.empty())
1,754✔
898
      d_string.append(1, ' ');
1,709✔
899

900
    d_string.append(SvcParam::keyToString(param.getKey()));
1,754✔
901
    if (param.getKey() != SvcParam::no_default_alpn) {
1,754✔
902
      d_string.append(1, '=');
1,745✔
903
    }
1,745✔
904

905
    switch (param.getKey())
1,754✔
906
    {
1,754✔
907
    case SvcParam::no_default_alpn:
9✔
908
      break;
9✔
909
    case SvcParam::ipv4hint: /* fall-through */
48✔
910
    case SvcParam::ipv6hint:
75✔
911
      // TODO use xfrCA and put commas in between?
912
      if (param.getAutoHint()) {
75!
913
        d_string.append("auto");
×
914
        break;
×
915
      }
×
916
      d_string.append(ComboAddress::caContainerToString(param.getIPHints(), false));
75✔
917
      break;
75✔
918
    case SvcParam::alpn:
1,016✔
919
      xfrSVCBValueList(param.getALPN());
1,016✔
920
      break;
1,016✔
921
    case SvcParam::mandatory:
48✔
922
    {
48✔
923
      bool doComma = false;
48✔
924
      for (auto const &k: param.getMandatory()) {
57✔
925
        if (doComma)
57✔
926
          d_string.append(1, ',');
9✔
927
        d_string.append(SvcParam::keyToString(k));
57✔
928
        doComma = true;
57✔
929
      }
57✔
930
      break;
48✔
931
    }
75✔
932
    case SvcParam::port: {
531✔
933
      auto str = d_string;
531✔
934
      d_string.clear();
531✔
935
      xfr16BitInt(param.getPort());
531✔
936
      d_string = str + d_string;
531✔
937
      break;
531✔
938
    }
75✔
939
    case SvcParam::ech: {
27✔
940
      auto str = d_string;
27✔
941
      d_string.clear();
27✔
942
      xfrBlobNoSpaces(param.getECH());
27✔
943
      d_string = str + '"' + d_string + '"';
27✔
944
      break;
27✔
945
    }
75✔
946
    default:
48✔
947
      auto str = d_string;
48✔
948
      d_string.clear();
48✔
949
      xfrText(param.getValue(), false, false);
48✔
950
      d_string = str + '"' + txtEscape(d_string) + '"';
48✔
951
      break;
48✔
952
    }
1,754✔
953
  }
1,754✔
954
}
1,160✔
955

956
void RecordTextWriter::xfrText(const string& val, bool /* multi */, bool /* lenField */)
957
{
17,367✔
958
  if(!d_string.empty())
17,367✔
959
    d_string.append(1,' ');
11,052✔
960

961
  d_string.append(val);
17,367✔
962
}
17,367✔
963

964
void RecordTextWriter::xfrUnquotedText(const string& val, bool /* lenField */)
965
{
42✔
966
  if(!d_string.empty())
42!
967
    d_string.append(1,' ');
42✔
968
  d_string.append(val);
42✔
969
}
42✔
970

971
#ifdef TESTING
972

973
int main(int argc, char**argv)
974
try
975
{
976
  RecordTextReader rtr(argv[1], argv[2]);
977

978
  unsigned int order, pref;
979
  string flags, services, regexp, replacement;
980
  string mx;
981

982
  rtr.xfrInt(order);
983
  rtr.xfrInt(pref);
984
  rtr.xfrText(flags);
985
  rtr.xfrText(services);
986
  rtr.xfrText(regexp);
987
  rtr.xfrName(replacement);
988

989
  cout<<"order: "<<order<<", pref: "<<pref<<"\n";
990
  cout<<"flags: \""<<flags<<"\", services: \""<<services<<"\", regexp: \""<<regexp<<"\", replacement: "<<replacement<<"\n";
991

992
  string out;
993
  RecordTextWriter rtw(out);
994

995
  rtw.xfrInt(order);
996
  rtw.xfrInt(pref);
997
  rtw.xfrText(flags);
998
  rtw.xfrText(services);
999
  rtw.xfrText(regexp);
1000
  rtw.xfrName(replacement);
1001

1002
  cout<<"Regenerated: '"<<out<<"'\n";
1003

1004
}
1005
catch(std::exception& e)
1006
{
1007
  cerr<<"Fatal: "<<e.what()<<endl;
1008
}
1009

1010
#endif
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