summaryrefslogtreecommitdiffstats
path: root/dnswriter.hh
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:34:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:34:30 +0000
commit4fc2f55f761d71aae1f145d5aa94ba929cc39676 (patch)
tree5c1e1db3b46dd4edbe11f612d93cb94b96891ce3 /dnswriter.hh
parentInitial commit. (diff)
downloaddnsdist-upstream.tar.xz
dnsdist-upstream.zip
Adding upstream version 1.7.3.upstream/1.7.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dnswriter.hh')
-rw-r--r--dnswriter.hh183
1 files changed, 183 insertions, 0 deletions
diff --git a/dnswriter.hh b/dnswriter.hh
new file mode 100644
index 0000000..485b547
--- /dev/null
+++ b/dnswriter.hh
@@ -0,0 +1,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