summaryrefslogtreecommitdiffstats
path: root/dnswriter.hh
blob: 485b5478337c895a586cca52d96ec315cb93b2b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
 * This file is part of PowerDNS or dnsdist.
 * Copyright -- PowerDNS.COM B.V. and its contributors
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * In addition, for the avoidance of any doubt, permission is granted to
 * link this program with OpenSSL and to (re)distribute the binaries
 * produced as the result of such linking.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#pragma once
#include <string>
#include <vector>
#include <map>
#include "dns.hh"
#include "dnsname.hh"
#include "namespaces.hh"
#include "iputils.hh"
#include "svc-records.hh"
#include <arpa/inet.h>


/** this class can be used to write DNS packets. It knows about DNS in the sense that it makes
    the packet header and record headers.

    The model is:

    packetheader (recordheader recordcontent)*

    The packetheader needs to be updated with the amount of packets of each kind (answer, auth, additional)

    Each recordheader contains the length of a dns record.

    Calling convention:

    vector<uint8_t> content;
    DNSPacketWriter dpw(content, const string& qname, uint16_t qtype, uint16_t qclass=QClass:IN);  // sets the question
    dpw.startrecord("this.is.an.ip.address.", ns_t_a);    // does nothing, except store qname and qtype
    dpw.xfr32BitInt(0x01020304);                         // adds 4 bytes (0x01020304) to the record buffer
    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
                                                         // new qname and qtype are stored
    dpw.xfr32BitInt(0x04030201);                         // adds 4 bytes (0x04030201) to the record buffer
    dpw.commit();                                        // writes out dnsrecord header containing qname and qtype and length 4, plus the recordbuffer

    // content now contains the ready packet, with 1 question and 2 answers

*/

template <typename Container> class GenericDNSPacketWriter : public boost::noncopyable
{

public:
  //! Start a DNS Packet in the vector passed, with question qname, qtype and qclass
  GenericDNSPacketWriter(Container& content, const DNSName& qname, uint16_t  qtype, uint16_t qclass=QClass::IN, uint8_t opcode=0);

  /** 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 -
      ANSWER, AUTHORITY, ADDITIONAL */
  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);

  /** Shorthand way to add an Opt-record, for example for EDNS0 purposes */
  typedef vector<pair<uint16_t,std::string> > optvect_t;
  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);

  /** 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.
      The content of the vector<> passed to the constructor is inconsistent until commit is called.
   */
  void commit();

  uint32_t size() const; // needs to be 32 bit because otherwise we don't see the wrap coming when it happened!

  /** Should the packet have grown too big for the writer's liking, rollback removes the record currently being written */
  void rollback();

  /** Discard all content except the question section */
  void truncate();

  void xfr48BitInt(uint64_t val);
  void xfrNodeOrLocatorID(const NodeOrLocatorID& val);
  void xfr32BitInt(uint32_t val);
  void xfr16BitInt(uint16_t val);
  void xfrType(uint16_t val)
  {
    xfr16BitInt(val);
  }
  void xfrIP(const uint32_t& val)
  {
    xfr32BitInt(htonl(val));
  }
  void xfrIP6(const std::string& val)
  {
    xfrBlob(val,16);
  }

  void xfrCAWithoutPort(uint8_t version, const ComboAddress &val)
  {
    if (version == 4) xfrIP(val.sin4.sin_addr.s_addr);
    else if (version == 6) {
      string blob;
      blob.assign((const char*)val.sin6.sin6_addr.s6_addr, 16);
      xfrBlob(blob, 16);
    }
    else throw runtime_error("invalid IP protocol");
  }

  void xfrCAPort(const ComboAddress &val)
  {
    uint16_t port;
    port = val.sin4.sin_port;
    xfr16BitInt(port);
  }

  void xfrTime(const uint32_t& val)
  {
    xfr32BitInt(val);
  }

  void xfr8BitInt(uint8_t val);

  void xfrName(const DNSName& label, bool compress=false, bool noDot=false);
  void xfrText(const string& text, bool multi=false, bool lenField=true);
  void xfrUnquotedText(const string& text, bool lenField);
  void xfrBlob(const string& blob, int len=-1);
  void xfrBlob(const vector<uint8_t>& blob);
  void xfrSvcParamKeyVals(const set<SvcParam>& kvs);
  void xfrBlobNoSpaces(const string& blob, int len=-1);
  void xfrHexBlob(const string& blob, bool keepReading=false);

  dnsheader* getHeader();
  void getRecordPayload(string& records); // call __before commit__

  void setCanonic(bool val)
  {
    d_canonic=val;
  }

  void setLowercase(bool val)
  {
    d_lowerCase=val;
  }
  Container& getContent()
  {
    return d_content;
  }
  bool eof() { return true; } // we don't know how long the record should be

  const string getRemaining() const {
    return "";
  }

  size_t getSizeWithOpts(const optvect_t& options) const;

private:
  uint16_t lookupName(const DNSName& name, uint16_t* matchlen);
  vector<uint16_t> d_namepositions;
  // We declare 1 uint_16 in the public section, these 3 align on a 8-byte boundary
  uint16_t d_sor;
  uint16_t d_rollbackmarker; // start of last complete packet, for rollback

  Container& d_content;
  DNSName d_qname;

  uint16_t d_truncatemarker; // end of header, for truncate
  DNSResourceRecord::Place d_recordplace;
  bool d_canonic, d_lowerCase, d_compress{false};
};

using DNSPacketWriter = GenericDNSPacketWriter<std::vector<uint8_t>>;

typedef vector<pair<string::size_type, string::size_type> > labelparts_t;
// bool labeltokUnescape(labelparts_t& parts, const DNSName& label);
std::vector<string> segmentDNSText(const string& text); // from dnslabeltext.rl
std::deque<string> segmentDNSName(const string& input); // from dnslabeltext.rl