Coverage Report

Created: 2026-07-16 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/dnswriter.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
#include <string>
24
#include <vector>
25
#include <map>
26
#include "dns.hh"
27
#include "dnsname.hh"
28
#include "namespaces.hh"
29
#include "iputils.hh"
30
#include "svc-records.hh"
31
#include <arpa/inet.h>
32
33
34
/** this class can be used to write DNS packets. It knows about DNS in the sense that it makes
35
    the packet header and record headers.
36
37
    The model is:
38
39
    packetheader (recordheader recordcontent)*
40
41
    The packetheader needs to be updated with the amount of packets of each kind (answer, auth, additional)
42
43
    Each recordheader contains the length of a dns record.
44
45
    Calling convention:
46
47
    vector<uint8_t> content;
48
    DNSPacketWriter dpw(content, const string& qname, uint16_t qtype, uint16_t qclass=QClass:IN);  // sets the question
49
    dpw.startrecord("this.is.an.ip.address.", ns_t_a);    // does nothing, except store qname and qtype
50
    dpw.xfr32BitInt(0x01020304);                         // adds 4 bytes (0x01020304) to the record buffer
51
    dpw.startrecord("this.is.an.ip.address.", ns_t_a);    // aha! writes out dnsrecord header containing qname and qtype and length 4, plus the recordbuffer, which gets emptied
52
                                                         // new qname and qtype are stored
53
    dpw.xfr32BitInt(0x04030201);                         // adds 4 bytes (0x04030201) to the record buffer
54
    dpw.commit();                                        // writes out dnsrecord header containing qname and qtype and length 4, plus the recordbuffer
55
56
    // content now contains the ready packet, with 1 question and 2 answers
57
58
*/
59
60
template <typename Container> class GenericDNSPacketWriter : public boost::noncopyable
61
{
62
63
public:
64
  //! Start a DNS Packet in the vector passed, with question qname, qtype and qclass
65
  GenericDNSPacketWriter(Container& content, const DNSName& qname, uint16_t  qtype, uint16_t qclass=QClass::IN, uint8_t opcode=0);
66
67
  /** Start a new DNS record within this packet for name, qtype, ttl, class and in the requested place. Note that packets can only be written in natural order -
68
      ANSWER, AUTHORITY, ADDITIONAL */
69
  void startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl=3600, uint16_t qclass=QClass::IN, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, bool compress=true);
70
71
  /** Shorthand way to add an Opt-record, for example for EDNS0 purposes */
72
  using optvect_t = vector<pair<uint16_t,std::string> >;
73
  void addOpt(const uint16_t udpsize, const uint16_t extRCode, const uint16_t ednsFlags, const optvect_t& options=optvect_t(), const uint8_t version=0);
74
75
  /** needs to be called after the last record is added, but can be called again and again later on. Is called internally by startRecord too.
76
      The content of the vector<> passed to the constructor is inconsistent until commit is called.
77
   */
78
  void commit();
79
80
  uint32_t size() const; // needs to be 32 bit because otherwise we don't see the wrap coming when it happened!
81
82
  /** Should the packet have grown too big for the writer's liking, rollback removes the record currently being written */
83
  void rollback();
84
85
  /** Discard all content except the question section */
86
  void truncate();
87
88
  void xfr48BitInt(uint64_t val);
89
  void xfrNodeOrLocatorID(const NodeOrLocatorID& val);
90
  void xfr32BitInt(uint64_t val);
91
  void xfr16BitInt(uint64_t val);
92
  void xfrType(uint16_t val)
93
17
  {
94
17
    xfr16BitInt(val);
95
17
  }
GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::xfrType(unsigned short)
Line
Count
Source
93
17
  {
94
17
    xfr16BitInt(val);
95
17
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::xfrType(unsigned short)
96
  void xfrIP(const uint32_t& val)
97
258k
  {
98
258k
    xfr32BitInt(htonl(val));
99
258k
  }
GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::xfrIP(unsigned int const&)
Line
Count
Source
97
258k
  {
98
    xfr32BitInt(htonl(val));
99
258k
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::xfrIP(unsigned int const&)
100
  void xfrIP6(const std::string& val)
101
11
  {
102
11
    xfrBlob(val,16);
103
11
  }
GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::xfrIP6(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Line
Count
Source
101
11
  {
102
11
    xfrBlob(val,16);
103
11
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::xfrIP6(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
104
105
  void xfrCAWithoutPort(uint8_t version, const ComboAddress &val)
106
261k
  {
107
261k
    if (version == 4) xfrIP(val.sin4.sin_addr.s_addr);
108
3.64k
    else if (version == 6) {
109
3.64k
      string blob;
110
3.64k
      blob.assign((const char*)val.sin6.sin6_addr.s6_addr, 16);
111
3.64k
      xfrBlob(blob, 16);
112
3.64k
    }
113
0
    else throw runtime_error("invalid IP protocol");
114
261k
  }
GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::xfrCAWithoutPort(unsigned char, ComboAddress const&)
Line
Count
Source
106
261k
  {
107
261k
    if (version == 4) xfrIP(val.sin4.sin_addr.s_addr);
108
3.64k
    else if (version == 6) {
109
3.64k
      string blob;
110
3.64k
      blob.assign((const char*)val.sin6.sin6_addr.s6_addr, 16);
111
3.64k
      xfrBlob(blob, 16);
112
3.64k
    }
113
0
    else throw runtime_error("invalid IP protocol");
114
261k
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::xfrCAWithoutPort(unsigned char, ComboAddress const&)
115
116
  void xfrCAPort(const ComboAddress &val)
117
0
  {
118
0
    uint16_t port;
119
0
    port = val.sin4.sin_port;
120
0
    xfr16BitInt(port);
121
0
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::xfrCAPort(ComboAddress const&)
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::xfrCAPort(ComboAddress const&)
122
123
  void xfrTime(const uint32_t& val)
124
34
  {
125
34
    xfr32BitInt(val);
126
34
  }
GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::xfrTime(unsigned int const&)
Line
Count
Source
124
34
  {
125
34
    xfr32BitInt(val);
126
34
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::xfrTime(unsigned int const&)
127
128
  void xfr8BitInt(uint64_t val);
129
130
  void xfrName(const DNSName& name, bool compress=false);
131
  void xfrText(const string& text, bool multi=false, bool lenField=true);
132
  void xfrUnquotedText(const string& text, bool lenField);
133
  void xfrBlob(const string& blob, int len=-1);
134
  void xfrBlob(const vector<uint8_t>& blob);
135
  void xfrSvcParamKeyVals(const set<SvcParam>& kvs);
136
  void xfrBlobNoSpaces(const string& blob, int len=-1);
137
  void xfrHexBlob(const string& blob, bool keepReading=false);
138
139
  dnsheader* getHeader();
140
  void getRecordPayload(string& records); // call __before commit__
141
  void getWireFormatContent(string& record); // call __before commit__
142
143
  void setCanonic(bool val)
144
3.60k
  {
145
3.60k
    d_canonic=val;
146
3.60k
  }
GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::setCanonic(bool)
Line
Count
Source
144
3.60k
  {
145
3.60k
    d_canonic=val;
146
3.60k
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::setCanonic(bool)
147
148
  void setLowercase(bool val)
149
0
  {
150
0
    d_lowerCase=val;
151
0
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::setLowercase(bool)
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::setLowercase(bool)
152
  Container& getContent()
153
0
  {
154
0
    return d_content;
155
0
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::getContent()
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::getContent()
156
2.52k
  bool eof() const { return true; } // we don't know how long the record should be
GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::eof() const
Line
Count
Source
156
2.52k
  bool eof() const { return true; } // we don't know how long the record should be
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::eof() const
157
158
0
  std::string getRemaining() const {
159
0
    return "";
160
0
  }
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >::getRemaining() const
Unexecuted instantiation: GenericDNSPacketWriter<std::__1::vector<unsigned char, noinit_adaptor<std::__1::allocator<unsigned char> > > >::getRemaining() const
161
162
#if defined(PDNS_AUTH) // [
163
  /* This method is only there for parity with DNSParser::consumeRemaining(),
164
     see the comment there to know why it is needed.
165
  */
166
  void consumeRemaining() const
167
  {
168
  }
169
#endif // ]
170
171
  size_t getSizeWithOpts(const optvect_t& options) const;
172
173
private:
174
  uint16_t lookupName(const DNSName& name, uint16_t* matchlen);
175
176
  std::vector<uint16_t> d_namepositions;
177
  DNSName d_qname;
178
  Container& d_content;
179
  size_t d_sor{0};
180
  uint16_t d_rollbackmarker{0}; // start of last complete packet, for rollback
181
  uint16_t d_truncatemarker{0}; // end of header, for truncate
182
  DNSResourceRecord::Place d_recordplace{DNSResourceRecord::QUESTION};
183
  bool d_canonic{false};
184
  bool d_lowerCase{false};
185
  bool d_compress{false};
186
};
187
188
using DNSPacketWriter = GenericDNSPacketWriter<std::vector<uint8_t>>;
189
190
std::vector<string> segmentDNSText(const string& text); // from dnslabeltext.rl