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 24 #include <protozero/pbf_writer.hpp> 25 26 #include "config.h" 27 #include "iputils.hh" 28 #include "gettime.hh" 29 #include "uuid-utils.hh" 30 31 namespace pdns { 32 namespace ProtoZero { 33 class Message { 34 public: 35 enum class MessageType : int32_t { DNSQueryType = 1, DNSResponseType = 2, DNSOutgoingQueryType = 3, DNSIncomingResponseType = 4 }; 36 enum class Field : protozero::pbf_tag_type { type = 1, messageId = 2, serverIdentity = 3, socketFamily = 4, socketProtocol = 5, from = 6, to = 7, inBytes = 8, timeSec = 9, timeUsec = 10, id = 11, question = 12, response = 13, originalRequestorSubnet = 14, requestorId = 15, initialRequestId = 16, deviceId = 17, newlyObservedDomain = 18, deviceName = 19, fromPort = 20, toPort = 21 }; 37 enum class QuestionField : protozero::pbf_tag_type { qName = 1, qType = 2, qClass = 3}; 38 enum class ResponseField : protozero::pbf_tag_type { rcode = 1, rrs = 2, appliedPolicy = 3, tags = 4, queryTimeSec = 5, queryTimeUsec = 6, appliedPolicyType = 7, appliedPolicyTrigger = 8, appliedPolicyHit = 9, appliedPolicyKind = 10, validationState = 11 }; 39 enum class RRField : protozero::pbf_tag_type { name = 1, type = 2, class_ = 3, ttl = 4, rdata = 5, udr = 6 }; 40 Message(std::string & buffer)41 Message(std::string& buffer): d_buffer(buffer), d_message{d_buffer} 42 { 43 } 44 45 Message(const Message&) = delete; 46 Message(Message&&) = delete; 47 Message& operator=(const Message&) = delete; 48 Message& operator=(Message&&) = delete; 49 50 void setRequest(const boost::uuids::uuid& uniqueId, const ComboAddress& requestor, const ComboAddress& local, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t id, bool tcp, size_t len); 51 void setResponse(const DNSName& qname, uint16_t qtype, uint16_t qclass); 52 setType(MessageType mtype)53 void setType(MessageType mtype) 54 { 55 add_enum(d_message, Field::type, static_cast<int32_t>(mtype)); 56 } 57 setMessageIdentity(const boost::uuids::uuid & uniqueId)58 void setMessageIdentity(const boost::uuids::uuid& uniqueId) 59 { 60 add_bytes(d_message, Field::messageId, reinterpret_cast<const char*>(uniqueId.begin()), uniqueId.size()); 61 } 62 setServerIdentity(const std::string & serverIdentity)63 void setServerIdentity(const std::string& serverIdentity) 64 { 65 add_bytes(d_message, Field::serverIdentity, serverIdentity.data(), serverIdentity.length()); 66 } 67 setSocketFamily(int family)68 void setSocketFamily(int family) 69 { 70 add_enum(d_message, Field::socketFamily, family == AF_INET ? 1 : 2); 71 } 72 setSocketProtocol(bool tcp)73 void setSocketProtocol(bool tcp) 74 { 75 add_enum(d_message, Field::socketProtocol, tcp ? 2 : 1); 76 } 77 setFrom(const ComboAddress & ca)78 void setFrom(const ComboAddress& ca) 79 { 80 encodeComboAddress(static_cast<protozero::pbf_tag_type>(Field::from), ca); 81 } 82 setTo(const ComboAddress & ca)83 void setTo(const ComboAddress& ca) 84 { 85 encodeComboAddress(static_cast<protozero::pbf_tag_type>(Field::to), ca); 86 } 87 setInBytes(uint64_t len)88 void setInBytes(uint64_t len) 89 { 90 add_uint64(d_message, Field::inBytes, len); 91 } 92 setTime()93 void setTime() 94 { 95 struct timespec ts; 96 gettime(&ts, true); 97 98 setTime(ts.tv_sec, ts.tv_nsec / 1000); 99 } 100 setTime(time_t sec,uint32_t usec)101 void setTime(time_t sec, uint32_t usec) 102 { 103 add_uint32(d_message, Field::timeSec, sec); 104 add_uint32(d_message, Field::timeUsec, usec); 105 } 106 setId(uint16_t id)107 void setId(uint16_t id) 108 { 109 add_uint32(d_message, Field::id, ntohs(id)); 110 } 111 setQuestion(const DNSName & qname,uint16_t qtype,uint16_t qclass)112 void setQuestion(const DNSName& qname, uint16_t qtype, uint16_t qclass) 113 { 114 protozero::pbf_writer pbf_question{d_message, static_cast<protozero::pbf_tag_type>(Field::question)}; 115 encodeDNSName(pbf_question, d_buffer, static_cast<protozero::pbf_tag_type>(QuestionField::qName), qname); 116 pbf_question.add_uint32(static_cast<protozero::pbf_tag_type>(QuestionField::qType), qtype); 117 pbf_question.add_uint32(static_cast<protozero::pbf_tag_type>(QuestionField::qClass), qclass); 118 } 119 setEDNSSubnet(const Netmask & nm,uint8_t mask)120 void setEDNSSubnet(const Netmask& nm, uint8_t mask) 121 { 122 encodeNetmask(static_cast<protozero::pbf_tag_type>(Field::originalRequestorSubnet), nm, mask); 123 } 124 setRequestorId(const std::string & req)125 void setRequestorId(const std::string& req) 126 { 127 if (!req.empty()) { 128 add_string(d_message, Field::requestorId, req); 129 } 130 } 131 setInitialRequestID(const boost::uuids::uuid & uniqueId)132 void setInitialRequestID(const boost::uuids::uuid& uniqueId) 133 { 134 add_bytes(d_message, Field::initialRequestId, reinterpret_cast<const char*>(uniqueId.begin()), uniqueId.size()); 135 } 136 setDeviceId(const std::string & id)137 void setDeviceId(const std::string& id) 138 { 139 if (!id.empty()) { 140 add_string(d_message, Field::deviceId, id); 141 } 142 } 143 setNewlyObservedDomain(bool nod)144 void setNewlyObservedDomain(bool nod) 145 { 146 add_bool(d_message, Field::newlyObservedDomain, nod); 147 } 148 setDeviceName(const std::string & name)149 void setDeviceName(const std::string& name) 150 { 151 if (!name.empty()) { 152 add_string(d_message, Field::deviceName, name); 153 } 154 } 155 setFromPort(in_port_t port)156 void setFromPort(in_port_t port) 157 { 158 add_uint32(d_message, Field::fromPort, port); 159 } 160 setToPort(in_port_t port)161 void setToPort(in_port_t port) 162 { 163 add_uint32(d_message, Field::toPort, port); 164 } 165 startResponse()166 void startResponse() 167 { 168 d_response = protozero::pbf_writer{d_message, static_cast<protozero::pbf_tag_type>(Field::response)}; 169 } 170 commitResponse()171 void commitResponse() 172 { 173 d_response.commit(); 174 } 175 setResponseCode(uint8_t rcode)176 void setResponseCode(uint8_t rcode) 177 { 178 d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::rcode), rcode); 179 } 180 setNetworkErrorResponseCode()181 void setNetworkErrorResponseCode() 182 { 183 /* special code meaning 'network error', like a timeout */ 184 d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::rcode), 65536); 185 } 186 setAppliedPolicy(const std::string & policy)187 void setAppliedPolicy(const std::string& policy) 188 { 189 d_response.add_string(static_cast<protozero::pbf_tag_type>(ResponseField::appliedPolicy), policy); 190 } 191 addPolicyTags(const std::unordered_set<std::string> & tags)192 void addPolicyTags(const std::unordered_set<std::string>& tags) 193 { 194 for (const auto& tag : tags) { 195 addPolicyTag(tag); 196 } 197 } 198 addPolicyTag(const string & tag)199 void addPolicyTag(const string& tag) 200 { 201 d_response.add_string(static_cast<protozero::pbf_tag_type>(ResponseField::tags), tag); 202 } 203 setQueryTime(uint32_t sec,uint32_t usec)204 void setQueryTime(uint32_t sec, uint32_t usec) 205 { 206 d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::queryTimeSec), sec); 207 d_response.add_uint32(static_cast<protozero::pbf_tag_type>(ResponseField::queryTimeUsec), usec); 208 } 209 210 void addRRsFromPacket(const char* packet, const size_t len, bool includeCNAME=false); 211 void addRR(const DNSName& name, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& blob); 212 213 protected: 214 void encodeComboAddress(protozero::pbf_tag_type type, const ComboAddress& ca); 215 void encodeNetmask(protozero::pbf_tag_type type, const Netmask& subnet, uint8_t mask); 216 void encodeDNSName(protozero::pbf_writer& pbf, std::string& buffer, protozero::pbf_tag_type type, const DNSName& name); 217 add_enum(protozero::pbf_writer & writer,Field type,int32_t value)218 static void add_enum(protozero::pbf_writer& writer, Field type, int32_t value) 219 { 220 writer.add_enum(static_cast<protozero::pbf_tag_type>(type), value); 221 } 222 add_bool(protozero::pbf_writer & writer,Field type,bool value)223 static void add_bool(protozero::pbf_writer& writer, Field type, bool value) 224 { 225 writer.add_bool(static_cast<protozero::pbf_tag_type>(type), value); 226 } 227 add_uint32(protozero::pbf_writer & writer,Field type,uint32_t value)228 static void add_uint32(protozero::pbf_writer& writer, Field type, uint32_t value) 229 { 230 writer.add_uint32(static_cast<protozero::pbf_tag_type>(type), value); 231 } 232 add_uint64(protozero::pbf_writer & writer,Field type,uint64_t value)233 static void add_uint64(protozero::pbf_writer& writer, Field type, uint64_t value) 234 { 235 writer.add_uint64(static_cast<protozero::pbf_tag_type>(type), value); 236 } 237 add_bytes(protozero::pbf_writer & writer,Field type,const char * data,size_t len)238 static void add_bytes(protozero::pbf_writer& writer, Field type, const char* data, size_t len) 239 { 240 writer.add_bytes(static_cast<protozero::pbf_tag_type>(type), data, len); 241 } 242 add_string(protozero::pbf_writer & writer,Field type,const std::string & str)243 static void add_string(protozero::pbf_writer& writer, Field type, const std::string& str) 244 { 245 writer.add_string(static_cast<protozero::pbf_tag_type>(type), str); 246 } 247 248 249 std::string& d_buffer; 250 protozero::pbf_writer d_message; 251 protozero::pbf_writer d_response; 252 }; 253 }; 254 }; 255