/* * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #if BOOST_VERSION >= 105400 #include #endif #include "dnswriter.hh" #include "misc.hh" #include "dnsparser.hh" #include /* d_content: <---- d_stuff ----> v d_truncatemarker dnsheader | qname | qtype | qclass | {recordname| dnsrecordheader | record } ^ d_rollbackmarker ^ d_sor */ template GenericDNSPacketWriter::GenericDNSPacketWriter(Container& content, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode) : d_content(content), d_qname(qname), d_canonic(false), d_lowerCase(false) { d_content.clear(); dnsheader dnsheader; memset(&dnsheader, 0, sizeof(dnsheader)); dnsheader.id=0; dnsheader.qdcount=htons(1); dnsheader.opcode=opcode; const uint8_t* ptr=(const uint8_t*)&dnsheader; d_content.reserve(sizeof(dnsheader) + qname.wirelength() + sizeof(qtype) + sizeof(qclass)); d_content.resize(sizeof(dnsheader)); uint8_t* dptr=(&*d_content.begin()); memcpy(dptr, ptr, sizeof(dnsheader)); d_namepositions.reserve(16); xfrName(qname, false); xfr16BitInt(qtype); xfr16BitInt(qclass); d_truncatemarker=d_content.size(); d_sor = 0; d_rollbackmarker = 0; } template dnsheader* GenericDNSPacketWriter::getHeader() { return reinterpret_cast(&*d_content.begin()); } template void GenericDNSPacketWriter::startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, DNSResourceRecord::Place place, bool compress) { d_compress = compress; commit(); d_rollbackmarker=d_content.size(); if(compress && !name.isRoot() && d_qname==name) { // don't do the whole label compression thing if we *know* we can get away with "see question" - except when compressing the root static unsigned char marker[2]={0xc0, 0x0c}; d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]); } else { xfrName(name, compress); } xfr16BitInt(qtype); xfr16BitInt(qclass); xfr32BitInt(ttl); xfr16BitInt(0); // this will be the record size d_recordplace = place; d_sor=d_content.size(); // this will remind us where to stuff the record size } template void GenericDNSPacketWriter::addOpt(const uint16_t udpsize, const uint16_t extRCode, const uint16_t ednsFlags, const optvect_t& options, const uint8_t version) { uint32_t ttl=0; EDNS0Record stuff; stuff.version = version; stuff.extFlags = htons(ednsFlags); /* RFC 6891 section 4 on the Extended RCode wire format * EXTENDED-RCODE * Forms the upper 8 bits of extended 12-bit RCODE (together with the * 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0 * indicates that an unextended RCODE is in use (values 0 through 15). */ // XXX Should be check for extRCode > 1<<12 ? stuff.extRCode = extRCode>>4; if (extRCode != 0) { // As this trumps the existing RCODE getHeader()->rcode = extRCode; } static_assert(sizeof(EDNS0Record) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(ttl)"); memcpy(&ttl, &stuff, sizeof(stuff)); ttl=ntohl(ttl); // will be reversed later on startRecord(g_rootdnsname, QType::OPT, ttl, udpsize, DNSResourceRecord::ADDITIONAL, false); for(auto const &option : options) { xfr16BitInt(option.first); xfr16BitInt(option.second.length()); xfrBlob(option.second); } } template void GenericDNSPacketWriter::xfr48BitInt(uint64_t val) { std::array bytes; uint16_t theLeft = htons((val >> 32)&0xffffU); uint32_t theRight = htonl(val & 0xffffffffU); memcpy(&bytes[0], (void*)&theLeft, sizeof(theLeft)); memcpy(&bytes[2], (void*)&theRight, sizeof(theRight)); d_content.insert(d_content.end(), bytes.begin(), bytes.end()); } template void GenericDNSPacketWriter::xfrNodeOrLocatorID(const NodeOrLocatorID& val) { d_content.insert(d_content.end(), val.content, val.content + sizeof(val.content)); } template void GenericDNSPacketWriter::xfr32BitInt(uint32_t val) { uint32_t rval=htonl(val); uint8_t* ptr=reinterpret_cast(&rval); d_content.insert(d_content.end(), ptr, ptr+4); } template void GenericDNSPacketWriter::xfr16BitInt(uint16_t val) { uint16_t rval=htons(val); uint8_t* ptr=reinterpret_cast(&rval); d_content.insert(d_content.end(), ptr, ptr+2); } template void GenericDNSPacketWriter::xfr8BitInt(uint8_t val) { d_content.push_back(val); } /* input: if lenField is true "" -> 0 "blah" -> 4blah "blah" "blah" -> output 4blah4blah "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit) "blah\"blah" -> 9blah"blah "blah\97" -> 5blahb if lenField is false "blah" -> blah "blah\"blah" -> blah"blah */ template void GenericDNSPacketWriter::xfrText(const string& text, bool, bool lenField) { if(text.empty()) { d_content.push_back(0); return; } vector segments = segmentDNSText(text); for(const string& str : segments) { if(lenField) d_content.push_back(str.length()); d_content.insert(d_content.end(), str.c_str(), str.c_str() + str.length()); } } template void GenericDNSPacketWriter::xfrUnquotedText(const string& text, bool lenField) { if(text.empty()) { d_content.push_back(0); return; } if(lenField) d_content.push_back(text.length()); d_content.insert(d_content.end(), text.c_str(), text.c_str() + text.length()); } static constexpr bool l_verbose=false; static constexpr uint16_t maxCompressionOffset=16384; template uint16_t GenericDNSPacketWriter::lookupName(const DNSName& name, uint16_t* matchLen) { // iterate over the written labels, see if we find a match const auto& raw = name.getStorage(); /* name might be a.root-servers.net, we need to be able to benefit from finding: b.root-servers.net, or even: b\xc0\x0c */ unsigned int bestpos=0; *matchLen=0; #if BOOST_VERSION >= 105400 boost::container::static_vector nvect, pvect; #else vector nvect, pvect; #endif try { for(auto riter= raw.cbegin(); riter < raw.cend(); ) { if(!*riter) break; nvect.push_back(riter - raw.cbegin()); riter+=*riter+1; } } catch(std::bad_alloc& ba) { if(l_verbose) cout<<"Domain "<= maxCompressionOffset) break; // compression pointers cannot point here pvect.push_back(offset); iter+=*iter+1; } } catch(std::bad_alloc& ba) { if(l_verbose) cout<<"Domain "< void GenericDNSPacketWriter::xfrName(const DNSName& name, bool compress, bool) { if(l_verbose) cout<<"Wants to write "<> 8)); d_content.push_back((char)(offset & 0xff)); } else { unsigned int pos=d_content.size(); if(l_verbose) cout<<"Found nothing, we are at pos "< lc; if(d_lowerCase) lc = make_unique(name.makeLowerCase()); const DNSName::string_t& raw = (lc ? *lc : name).getStorage(); if(l_verbose) cout<<"Writing out the whole thing "< void GenericDNSPacketWriter::xfrBlob(const string& blob, int ) { const uint8_t* ptr=reinterpret_cast(blob.c_str()); d_content.insert(d_content.end(), ptr, ptr+blob.size()); } template void GenericDNSPacketWriter::xfrBlob(const std::vector& blob) { d_content.insert(d_content.end(), blob.begin(), blob.end()); } template void GenericDNSPacketWriter::xfrBlobNoSpaces(const string& blob, int ) { xfrBlob(blob); } template void GenericDNSPacketWriter::xfrHexBlob(const string& blob, bool /* keepReading */) { xfrBlob(blob); } template void GenericDNSPacketWriter::xfrSvcParamKeyVals(const std::set &kvs) { for (auto const ¶m : kvs) { // Key first! xfr16BitInt(param.getKey()); switch (param.getKey()) { case SvcParam::mandatory: xfr16BitInt(2 * param.getMandatory().size()); for (auto const &m: param.getMandatory()) { xfr16BitInt(m); } break; case SvcParam::alpn: { uint16_t totalSize = param.getALPN().size(); // All 1 octet size headers for each value for (auto const &a : param.getALPN()) { totalSize += a.length(); } xfr16BitInt(totalSize); for (auto const &a : param.getALPN()) { xfrUnquotedText(a, true); // will add the 1-byte length field } break; } case SvcParam::no_default_alpn: xfr16BitInt(0); // no size :) break; case SvcParam::port: xfr16BitInt(2); // size xfr16BitInt(param.getPort()); break; case SvcParam::ipv4hint: xfr16BitInt(param.getIPHints().size() * 4); // size for (const auto& a: param.getIPHints()) { xfrCAWithoutPort(param.getKey(), a); } break; case SvcParam::ipv6hint: xfr16BitInt(param.getIPHints().size() * 16); // size for (const auto& a: param.getIPHints()) { xfrCAWithoutPort(param.getKey(), a); } break; case SvcParam::ech: xfr16BitInt(param.getECH().size()); // size xfrBlobNoSpaces(param.getECH()); break; default: xfr16BitInt(param.getValue().size()); xfrBlob(param.getValue()); break; } } } // call __before commit__ template void GenericDNSPacketWriter::getRecordPayload(string& records) { records.assign(d_content.begin() + d_sor, d_content.end()); } template uint32_t GenericDNSPacketWriter::size() const { return d_content.size(); } template void GenericDNSPacketWriter::rollback() { d_content.resize(d_rollbackmarker); d_sor = 0; } template void GenericDNSPacketWriter::truncate() { d_content.resize(d_truncatemarker); dnsheader* dh=reinterpret_cast( &*d_content.begin()); dh->ancount = dh->nscount = dh->arcount = 0; } template void GenericDNSPacketWriter::commit() { if(!d_sor) return; uint16_t rlen = d_content.size() - d_sor; d_content[d_sor-2]=rlen >> 8; d_content[d_sor-1]=rlen & 0xff; d_sor=0; dnsheader* dh=reinterpret_cast( &*d_content.begin()); switch(d_recordplace) { case DNSResourceRecord::QUESTION: dh->qdcount = htons(ntohs(dh->qdcount) + 1); break; case DNSResourceRecord::ANSWER: dh->ancount = htons(ntohs(dh->ancount) + 1); break; case DNSResourceRecord::AUTHORITY: dh->nscount = htons(ntohs(dh->nscount) + 1); break; case DNSResourceRecord::ADDITIONAL: dh->arcount = htons(ntohs(dh->arcount) + 1); break; } } template size_t GenericDNSPacketWriter::getSizeWithOpts(const optvect_t& options) const { size_t result = size() + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE + DNS_RDLENGTH_SIZE; for(auto const &option : options) { result += 4; result += option.second.size(); } return result; } template class GenericDNSPacketWriter>; #include "noinitvector.hh" template class GenericDNSPacketWriter;