%doc %{_defaultdocdir}/%{name}/nodnssec-3.x_to_3.4.0_schema.mysql.sql
%doc %{_defaultdocdir}/%{name}/dnssec-3.x_to_3.4.0_schema.mysql.sql
%doc %{_defaultdocdir}/%{name}/3.4.0_to_4.1.0_schema.mysql.sql
+%doc %{_defaultdocdir}/%{name}/4.1.0_to_4.1.1_schema.mysql.sql
%{_libdir}/%{name}/libgmysqlbackend.so
%files backend-postgresql
--enable-unit-tests \
--enable-libsodium \
--enable-dnscrypt \
+ --enable-dns-over-tls \
--prefix=$HOME/dnsdist \
--disable-silent-rules"
run "make -k -j3"
--ecs-mask <VAL> When EDNS forwarding an IP address, mask out first octet with this value
--ecs-stamp <FLAG> Add original IP address as EDNS Client Subnet Option when
forwarding to reference server
+--pcap-dns-port <VAL> Look at packets from or to this port in the PCAP. Default is 53.
--packet-limit <NUM> Stop after replaying *NUM* packets. Default for *NUM* is 0, which
means no limit.
--quiet <FLAG> If *FLAG* is set to 1. dnsreplay will not be very noisy with its
Send a text command to a backend for execution. GSQL backends will
take SQL commands, other backends may take different things. Be
careful!
+bench-db [*FILE*]
+ Perform a benchmark of the backend-database.
+ *FILE* can be a file with a list, one per line, of domain names to use for this.
+ If *FILE* is not specified, powerdns.com is used.
See also
--------
-@ 86400 IN SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2017121101 10800 3600 604800 10800
+@ 86400 IN SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2018012201 10800 3600 604800 10800
@ 3600 IN NS pdns-public-ns1.powerdns.com.
@ 3600 IN NS pdns-public-ns2.powerdns.com.
; Auth
recursor-4.1.0-rc1.security-status 60 IN TXT "3 Unsupported pre-release (final release is out)"
recursor-4.1.0-rc2.security-status 60 IN TXT "3 Unsupported pre-release (final release is out)"
recursor-4.1.0-rc3.security-status 60 IN TXT "3 Unsupported pre-release (final release is out)"
-recursor-4.1.0.security-status 60 IN TXT "1 OK"
+recursor-4.1.0.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2018-01.html"
+recursor-4.1.1.security-status 60 IN TXT "1 OK"
; Recursor Debian
recursor-3.6.2-2.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/"
See the `3.X <https://doc.powerdns.com/3/authoritative/upgrading/>`__
upgrade notes if your version is older than 3.4.2.
+4.1.0 to 4.1.1
+--------------
+
+- The :doc:`Generic MySQL backend <backends/generic-mysql>` schema has
+ changed: the ``notified_serial`` column default in the ``domains``
+ table has been changed from ``INT DEFAULT NULL`` to ``INT UNSIGNED
+ DEFAULT NULL``:
+
+ - ``ALTER TABLE domains MODIFY notified_serial INT UNSIGNED DEFAULT NULL;``
+
4.0.X to 4.1.0
--------------
--- /dev/null
+ALTER TABLE domains MODIFY notified_serial INT UNSIGNED DEFAULT NULL;
dnssec-3.x_to_3.4.0_schema.mysql.sql \
nodnssec-3.x_to_3.4.0_schema.mysql.sql \
3.4.0_to_4.1.0_schema.mysql.sql \
+ 4.1.0_to_4.1.1_schema.mysql.sql \
schema.mysql.sql
libgmysqlbackend_la_SOURCES = \
master VARCHAR(128) DEFAULT NULL,
last_check INT DEFAULT NULL,
type VARCHAR(6) NOT NULL,
- notified_serial INT DEFAULT NULL,
+ notified_serial INT UNSIGNED DEFAULT NULL,
account VARCHAR(40) CHARACTER SET 'utf8' DEFAULT NULL,
PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';
if ( !getDomainInfo( DNSName( result["associatedDomain"][0] ), di ) )
continue;
- di.backend = this;
-
if( di.notified_serial < di.serial )
domains->push_back( di );
}
{
string filter;
SOAData sd;
- int msgid=0;
PowerLDAP::sentry_t result;
const char* attronly[] = {
"sOARecord",
// search for SOARecord of domain
filter = "(&(associatedDomain=" + toLower( m_pldap->escape( domain.toStringRootDot() ) ) + ")(SOARecord=*))";
m_msgid = m_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, attronly );
- m_pldap->getSearchEntry( msgid, result );
+ m_pldap->getSearchEntry( m_msgid, result );
}
catch( LDAPTimeout < )
{
di.kind = DomainInfo::Native;
}
+ di.backend = this;
return true;
}
void lookup(const QType &qtype, const DNSName &qname, DNSPacket *p, int domain_id) override;
bool get(DNSResourceRecord &rr) override;
//! fills the soadata struct with the SOA details. Returns false if there is no SOA.
- bool getSOA(const DNSName &name, SOAData &soadata) override;
+ bool getSOA(const DNSName &name, SOAData &soadata, bool unmodifiedSerial) override;
// MASTER BACKEND
return !rr.content.empty();
}
-bool LUABackend::getSOA(const DNSName &name, SOAData &soadata) {
+bool LUABackend::getSOA(const DNSName &name, SOAData &soadata, bool unmodifiedSerial) {
if (logging)
L << Logger::Info << backend_name << "(getsoa) BEGIN" << endl;
return true;
}
-bool MyDNSBackend::getSOA(const DNSName& name, SOAData& soadata) {
+bool MyDNSBackend::getSOA(const DNSName& name, SOAData& soadata, bool unmodifiedSerial) {
string query;
SSqlStatement::row_t rrow;
reset();
}
catch (SSqlException &e) {
- throw PDNSException("MyDNSBackend unable to get soa for domain "+name.toString()+": "+e.txtReason());
+ throw PDNSException("MyDNSBackend unable to get soa for domain "+name.toLogString()+": "+e.txtReason());
}
if (d_result.empty()) {
reset();
}
catch (SSqlException &e) {
- throw PDNSException("MyDNSBackend unable to lookup "+qname.toString()+": "+e.txtReason());
+ throw PDNSException("MyDNSBackend unable to lookup "+qname.toLogString()+": "+e.txtReason());
}
if (d_result.empty() == false) {
reset();
}
catch (SSqlException &e) {
- throw PDNSException("MyDNSBackend unable to lookup "+qname.toString()+": "+e.txtReason());
+ throw PDNSException("MyDNSBackend unable to lookup "+qname.toLogString()+": "+e.txtReason());
}
if(d_result.empty()) {
}
}
catch (SSqlException &e) {
- throw PDNSException("MyDNSBackend unable to lookup "+qname.toString()+": "+e.txtReason());
+ throw PDNSException("MyDNSBackend unable to lookup "+qname.toLogString()+": "+e.txtReason());
}
d_qname = qname.toString();
void lookup(const QType &, const DNSName &qdomain, DNSPacket *p=0, int zoneId=-1) override;
bool list(const DNSName &target, int domain_id, bool include_disabled=false) override;
bool get(DNSResourceRecord &r) override;
- bool getSOA(const DNSName& name, SOAData& soadata) override;
+ bool getSOA(const DNSName& name, SOAData& soadata, bool unmodifiedSerial) override;
void getAllDomains(vector<DomainInfo> *domains, bool include_disabled=false) override;
+
private:
SMySQL *d_db;
-bool OdbxBackend::getSOA( const DNSName& domain, SOAData& sd)
+bool OdbxBackend::getSOA( const DNSName& domain, SOAData& sd, bool unmodifiedSerial)
{
const char* tmp;
sd.ttl = strtoul( tmp, NULL, 10 );
}
- if( sd.serial == 0 && ( tmp = odbx_field_value( m_result, 1 ) ) != NULL )
+ if( !unmodifiedSerial && sd.serial == 0 && ( tmp = odbx_field_value( m_result, 1 ) ) != NULL )
{
sd.serial = strtol( tmp, NULL, 10 );
}
~OdbxBackend();
void lookup( const QType& qtype, const DNSName& qdomain, DNSPacket* p = 0, int zoneid = -1 ) override;
- bool getSOA( const DNSName& domain, SOAData& sd ) override;
+ bool getSOA( const DNSName& domain, SOAData& sd, bool unmodifiedSerial ) override;
bool list( const DNSName& target, int domain_id, bool include_disabled=false ) override;
bool get( DNSResourceRecord& rr ) override;
ffi (~> 1.9)
json (1.8.5)
sqlite3 (1.3.9)
- webrick (1.3.1)
+ webrick (1.4.2)
zeromqrb (0.1.3)
ffi-rzmq
ffi (~> 1.9)
json (1.8.2)
sqlite3 (1.3.9)
- webrick (1.3.1)
+ webrick (1.4.2)
zeromqrb (0.1.3)
ffi-rzmq
return true;
}
-bool RemoteBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, time_t& serial) {
+bool RemoteBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, uint32_t& serial) {
Json query = Json::object{
{ "method", "calculateSOASerial" },
{ "parameters", Json::object{
bool startTransaction(const DNSName& domain, int domain_id) override;
bool commitTransaction() override;
bool abortTransaction() override;
- bool calculateSOASerial(const DNSName& domain, const SOAData& sd, time_t& serial) override;
+ bool calculateSOASerial(const DNSName& domain, const SOAData& sd, uint32_t& serial) override;
bool setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content) override;
bool deleteTSIGKey(const DNSName& name) override;
bool getTSIGKeys(std::vector< struct TSIGKey > &keys) override;
BOOST_AUTO_TEST_CASE(test_method_calculateSOASerial) {
SOAData sd;
- time_t serial;
+ uint32_t serial;
be->getSOA(DNSName("unit.test."),sd);
BOOST_CHECK(be->calculateSOASerial(DNSName("unit.test."),sd,serial));
reset();
}
catch (SSqlException &e) {
- throw PDNSException("GSQLBackend unable to set master of domain \""+domain.toString()+"\": "+e.txtReason());
+ throw PDNSException("GSQLBackend unable to set master of domain '"+domain.toLogString()+"': "+e.txtReason());
}
return true;
}
reset();
}
catch (SSqlException &e) {
- throw PDNSException("GSQLBackend unable to set kind of domain \""+domain.toString()+"\": "+e.txtReason());
+ throw PDNSException("GSQLBackend unable to set kind of domain '"+domain.toLogString()+"': "+e.txtReason());
}
return true;
}
reset();
}
catch (SSqlException &e) {
- throw PDNSException("GSQLBackend unable to set account of domain \""+domain.toString()+"\": "+e.txtReason());
+ throw PDNSException("GSQLBackend unable to set account of domain '"+domain.toLogString()+"': "+e.txtReason());
}
return true;
}
reset();
}
catch (SSqlException &e) {
- throw PDNSException("GSQLBackend unable to delete empty non-terminal rr "+qname.toString()+" from domain_id "+itoa(domain_id)+": "+e.txtReason());
+ throw PDNSException("GSQLBackend unable to delete empty non-terminal rr '"+qname.toLogString()+"' from domain_id "+itoa(domain_id)+": "+e.txtReason());
return false;
}
}
reset();
}
catch (SSqlException &e) {
- throw PDNSException("GSQLBackend unable to insert empty non-terminal rr "+qname.toString()+" in domain_id "+itoa(domain_id)+": "+e.txtReason());
+ throw PDNSException("GSQLBackend unable to insert empty non-terminal rr '"+qname.toLogString()+"' in domain_id "+itoa(domain_id)+": "+e.txtReason());
return false;
}
}
bool GSQLBackend::listSubZone(const DNSName &zone, int domain_id) {
- string wildzone = "%." + toLower(zone.toStringNoDot());
+ string wildzone = "%." + zone.makeLowerCase().toStringNoDot();
try {
reconnectIfNeeded();
reset();
}
catch(SSqlException &e) {
- throw PDNSException("Database error trying to insert new domain '"+domain.toString()+"': "+ e.txtReason());
+ throw PDNSException("Database error trying to insert new domain '"+domain.toLogString()+"': "+ e.txtReason());
}
return true;
}
createDomain(domain, "SLAVE", masters, account);
}
catch(SSqlException &e) {
- throw PDNSException("Database error trying to insert new slave domain '"+domain.toString()+"': "+ e.txtReason());
+ throw PDNSException("Database error trying to insert new slave domain '"+domain.toLogString()+"': "+ e.txtReason());
}
return true;
}
reset();
}
catch(SSqlException &e) {
- throw PDNSException("Database error trying to delete domain '"+domain.toString()+"': "+ e.txtReason());
+ throw PDNSException("Database error trying to delete domain '"+domain.toLogString()+"': "+ e.txtReason());
}
return true;
}
return true;
}
-bool GSQLBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, time_t& serial)
+bool GSQLBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, uint32_t& serial)
{
if (d_ZoneLastChangeQuery.empty()) {
// query not set => fall back to default impl
bool updateEmptyNonTerminals(uint32_t domain_id, set<DNSName>& insert ,set<DNSName>& erase, bool remove) override;
bool doesDNSSEC() override;
- bool calculateSOASerial(const DNSName& domain, const SOAData& sd, time_t& serial) override;
+ bool calculateSOASerial(const DNSName& domain, const SOAData& sd, uint32_t& serial) override;
bool replaceRRSet(uint32_t domain_id, const DNSName& qname, const QType& qt, const vector<DNSResourceRecord>& rrset) override;
bool listSubZone(const DNSName &zone, int domain_id) override;
virtual SSqlStatement* bind(const string& name, unsigned long long value)=0;
virtual SSqlStatement* bind(const string& name, const std::string& value)=0;
SSqlStatement* bind(const string& name, const DNSName& value) {
- return bind(name, toLower(value.toStringRootDot()));
+ return bind(name, value.makeLowerCase().toStringRootDot());
}
virtual SSqlStatement* bindNull(const string& name)=0;
virtual SSqlStatement* execute()=0;;
std::string keyStr = qname.toDNSStringLC();
if (keyStr.size() > sizeof(key.qname)) {
- throw std::runtime_error("Invalid QName to block " + qname.toString());
+ throw std::runtime_error("Invalid QName to block " + qname.toLogString());
}
memcpy(key.qname, keyStr.c_str(), keyStr.size());
{
std::unique_lock<std::mutex> lock(d_mutex);
if (d_qNamesCount >= d_maxQNames) {
- throw std::runtime_error("Table full when trying to block " + qname.toString());
+ throw std::runtime_error("Table full when trying to block " + qname.toLogString());
}
int res = bpf_lookup_elem(d_qnamemap.fd, &key, &value);
if (res != -1) {
- throw std::runtime_error("Trying to block an already blocked qname: " + qname.toString());
+ throw std::runtime_error("Trying to block an already blocked qname: " + qname.toLogString());
}
res = bpf_update_elem(d_qnamemap.fd, &key, &value, BPF_NOEXIST);
}
if (res != 0) {
- throw std::runtime_error("Error adding blocked qname " + qname.toString() + ": " + std::string(strerror(errno)));
+ throw std::runtime_error("Error adding blocked qname " + qname.toLogString() + ": " + std::string(strerror(errno)));
}
}
}
(void) qtype;
if (keyStr.size() > sizeof(key.qname)) {
- throw std::runtime_error("Invalid QName to block " + qname.toString());
+ throw std::runtime_error("Invalid QName to block " + qname.toLogString());
}
memcpy(key.qname, keyStr.c_str(), keyStr.size());
d_qNamesCount--;
}
else {
- throw std::runtime_error("Error removing qname address " + qname.toString() + ": " + std::string(strerror(errno)));
+ throw std::runtime_error("Error removing qname address " + qname.toLogString() + ": " + std::string(strerror(errno)));
}
}
}
return dpk;
}
- throw runtime_error("Can't find a key with id "+std::to_string(id)+" for zone '"+zname.toString()+"'");
+ throw runtime_error("Can't find a key with id "+std::to_string(id)+" for zone '"+zname.toLogString()+"'");
}
{
string error_msg = "";
if (!checkNSEC3PARAM(ns3p, error_msg))
- throw runtime_error("NSEC3PARAMs provided for zone '"+zname.toString()+"' are invalid: " + error_msg);
+ throw runtime_error("NSEC3PARAMs provided for zone '"+zname.toLogString()+"' are invalid: " + error_msg);
clearCaches(zname);
string descr = ns3p.getZoneRepresentation();
("Err#12")
("Err#13")
("Err#14")
- ("Err#15")
+ ("Err#15") // Last non-extended RCode
("Bad OPT Version / TSIG Signature Failure")
("Key not recognized")
("Signature out of time window")
("Duplicate key name")
("Algorithm not supported")
("Bad Truncation")
+ ("Bad/missing Server Cookie")
;
-std::string RCode::to_s(unsigned short rcode) {
- if (rcode > RCode::rcodes_s.size()-1 )
+std::string RCode::to_s(uint8_t rcode) {
+ if (rcode > 0xF)
+ return std::string("ErrOutOfRange");
+ return ERCode::to_s(rcode);
+}
+
+std::string ERCode::to_s(uint8_t rcode) {
+ if (rcode > RCode::rcodes_s.size()-1)
return std::string("Err#")+std::to_string(rcode);
return RCode::rcodes_s[rcode];
}
#include "dnsname.hh"
#include <time.h>
#include <sys/types.h>
+
+#undef BADSIG // signal.h SIG_ERR
+
class DNSBackend;
struct DNSRecord;
{
public:
enum rcodes_ { NoError=0, FormErr=1, ServFail=2, NXDomain=3, NotImp=4, Refused=5, YXDomain=6, YXRRSet=7, NXRRSet=8, NotAuth=9, NotZone=10};
- static std::string to_s(unsigned short rcode);
+ static std::string to_s(uint8_t rcode);
static std::vector<std::string> rcodes_s;
};
+class ERCode
+{
+public:
+ enum rcodes_ { BADVERS=16, BADSIG=16, BADKEY=17, BADTIME=18, BADMODE=19, BADNAME=20, BADALG=21, BADTRUNC=22, BADCOOKIE=23 };
+ static std::string to_s(uint8_t rcode);
+};
+
class Opcode
{
public:
static_assert(sizeof(EDNS0Record) == 4, "EDNS0Record size must be 4");
-#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
#include <machine/endian.h>
#elif __linux__ || __GNU__
# include <endian.h>
string secret;
};
-/** for use by DNSPacket, converts a SOAData class to a ascii line again */
-string serializeSOAData(const SOAData &data);
string &attodot(string &str); //!< for when you need to insert an email address in the SOA
\param domain Domain we want to get the SOA details of
\param sd SOAData which is filled with the SOA details
+ \param unmodifiedSerial bool if set, serial will be returned as stored in the backend (maybe 0)
*/
-bool DNSBackend::getSOA(const DNSName &domain, SOAData &sd)
+bool DNSBackend::getSOA(const DNSName &domain, SOAData &sd, bool unmodifiedSerial)
{
this->lookup(QType(QType::SOA),domain);
sd.hostmaster=DNSName("hostmaster")+domain;
}
- if(!sd.serial) { // magic time!
+ if(!unmodifiedSerial && !sd.serial) { // magic time!
DLOG(L<<Logger::Warning<<"Doing SOA serial number autocalculation for "<<rr.qname<<endl);
- time_t serial;
+ uint32_t serial = 0;
if (calculateSOASerial(domain, sd, serial)) {
sd.serial = serial;
//DLOG(L<<"autocalculated soa serialnumber for "<<rr.qname<<" is "<<newest<<endl);
* \param sd Information about the SOA record already available
* \param serial Output parameter. Only inspected when we return true
*/
-bool DNSBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, time_t& serial)
+bool DNSBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, uint32_t& serial)
{
// we do this by listing the domain and taking the maximum last modified timestamp
DNSResourceRecord i;
- time_t newest=0;
+ uint32_t newest=0;
if(!(this->list(domain, sd.domain_id))) {
DLOG(L<<Logger::Warning<<"Backend error trying to determine magic serial number of zone '"<<domain<<"'"<<endl);
throw PDNSException("Out of range exception parsing "+content);
}
}
-
-string serializeSOAData(const SOAData &d)
-{
- ostringstream o;
- // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
- o<<d.nameserver.toString()<<" "<< d.hostmaster.toString() <<" "<< d.serial <<" "<< d.refresh << " "<< d.retry << " "<< d.expire << " "<< d.default_ttl;
-
- return o.str();
-}
virtual ~DNSBackend(){};
//! fills the soadata struct with the SOA details. Returns false if there is no SOA.
- virtual bool getSOA(const DNSName &name, SOAData &soadata);
+ virtual bool getSOA(const DNSName &name, SOAData &soadata, bool unmodifiedSerial=false);
//! Calculates a SOA serial for the zone and stores it in the third argument.
- virtual bool calculateSOASerial(const DNSName& domain, const SOAData& sd, time_t& serial);
+ virtual bool calculateSOASerial(const DNSName& domain, const SOAData& sd, uint32_t& serial);
virtual bool replaceRRSet(uint32_t domain_id, const DNSName& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)
{
value = newValue;
}
-void DNSDistPacketCache::insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, uint8_t rcode)
+void DNSDistPacketCache::insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, uint8_t rcode, boost::optional<uint32_t> tempFailureTTL)
{
if (responseLen < sizeof(dnsheader))
return;
uint32_t minTTL;
if (rcode == RCode::ServFail || rcode == RCode::Refused) {
- minTTL = d_tempFailureTTL;
+ minTTL = tempFailureTTL == boost::none ? d_tempFailureTTL : *tempFailureTTL;
if (minTTL == 0) {
return;
}
DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL=86400, uint32_t minTTL=0, uint32_t tempFailureTTL=60, uint32_t staleTTL=60, bool dontAge=false, uint32_t shards=1, bool deferrableInsertLock=true);
~DNSDistPacketCache();
- void insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, uint8_t rcode);
+ void insert(uint32_t key, const DNSName& qname, uint16_t qtype, uint16_t qclass, const char* response, uint16_t responseLen, bool tcp, uint8_t rcode, boost::optional<uint32_t> tempFailureTTL);
bool get(const DNSQuestion& dq, uint16_t consumed, uint16_t queryId, char* response, uint16_t* responseLen, uint32_t* keyOut, uint32_t allowExpired=0, bool skipAging=false);
void purgeExpired(size_t upTo=0);
void expunge(size_t upTo=0);
#include "sodcrypto.hh"
#include "pwd.h"
-#if defined (__OpenBSD__)
+#if defined (__OpenBSD__) || defined(__NetBSD__)
#include <readline/readline.h>
#include <readline/history.h>
#else
const std::vector<ConsoleKeyword> g_consoleKeywords{
/* keyword, function, parameters, description */
{ "addACL", true, "netmask", "add to the ACL set who can use this server" },
- { "addAction", true, "DNS rule, DNS action", "add a rule" },
+ { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "add a rule" },
{ "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" },
{ "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
{ "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" },
- { "addLuaAction", true, "x, func", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dq`, which returns an action to be taken on this packet. Good for rare packets but where you want to do a lot of processing" },
- { "addLuaResponseAction", true, "x, func", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dr`, which returns an action to be taken on this response packet. Good for rare packets but where you want to do a lot of processing" },
- { "addCacheHitResponseAction", true, "DNS rule, DNS response action", "add a cache hit response rule" },
- { "addResponseAction", true, "DNS rule, DNS response action", "add a response rule" },
+ { "addLuaAction", true, "x, func [, {uuid=\"UUID\"}]", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dq`, which returns an action to be taken on this packet. Good for rare packets but where you want to do a lot of processing" },
+ { "addLuaResponseAction", true, "x, func [, {uuid=\"UUID\"}]", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dr`, which returns an action to be taken on this response packet. Good for rare packets but where you want to do a lot of processing" },
+ { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" },
+ { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" },
+ { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a self-answered response rule" },
+ { "addTLSLocal", true, "addr, certFile, keyFile[,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate and key. The last parameter is a table" },
{ "AllowAction", true, "", "let these packets go through" },
{ "AllowResponseAction", true, "", "let these packets go through" },
{ "AllRule", true, "", "matches all traffic" },
{ "getResponseRing", true, "", "return the current content of the response ring" },
{ "getServer", true, "n", "returns server with index n" },
{ "getServers", true, "", "returns a table with all defined servers" },
+ { "getTLSContext", true, "n", "returns the TLS context with index n" },
{ "inClientStartup", true, "", "returns true during console client parsing of configuration" },
{ "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
{ "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"},
{ "mvCacheHitResponseRule", true, "from, to", "move cache hit response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
{ "mvResponseRule", true, "from, to", "move response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
{ "mvRule", true, "from, to", "move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, in which case the rule will be moved to the last position" },
+ { "mvSelfAnsweredResponseRule", true, "from, to", "move self-answered response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
{ "newDNSName", true, "name", "make a DNSName based on this .-terminated name" },
{ "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true]", "return a new Packet Cache" },
{ "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" },
{ "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" },
- { "newRuleAction", true, "DNS rule, DNS action", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" },
+ { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" },
{ "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\"}", "instantiate a server" },
{ "newServerPolicy", true, "name, function", "create a policy object from a Lua function" },
{ "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" },
{ "registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed" },
{ "RemoteLogAction", true, "RemoteLogger [, alterFunction]", "send the content of this query to a remote logger via Protocol Buffer. `alterFunction` is a callback, receiving a DNSQuestion and a DNSDistProtoBufMessage, that can be used to modify the Protocol Buffer content, for example for anonymization purposes" },
{ "RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME]]", "send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction` and `includeCNAME` indicates whether CNAME records inside the response should be parsed and exported. The default is to only exports A and AAAA records" },
- { "rmCacheHitResponseRule", true, "n", "remove cache hit response rule n" },
- { "rmResponseRule", true, "n", "remove response rule n" },
- { "rmRule", true, "n", "remove rule n" },
+ { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
+ { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
+ { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
+ { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
{ "rmServer", true, "n", "remove server with index n" },
{ "roundrobin", false, "", "Simple round robin over available servers" },
{ "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" },
{ "QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes" },
{ "QTypeRule", true, "qtype", "matches queries with the specified qtype" },
{ "RCodeRule", true, "rcode", "matches responses with the specified rcode" },
+ { "ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)" },
{ "sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"},
{ "setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us" },
{ "setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API" },
{ "show", true, "string", "outputs `string`" },
{ "showACL", true, "", "show our ACL set" },
{ "showBinds", true, "", "show listening addresses (frontends)" },
- { "showCacheHitResponseRules", true, "", "show all defined cache hit response rules" },
+ { "showCacheHitResponseRules", true, "[showUUIDs]", "show all defined cache hit response rules, optionally with their UUIDs" },
{ "showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds" },
{ "showDynBlocks", true, "", "show dynamic blocks in force" },
{ "showPools", true, "", "show the available pools" },
{ "showPoolServerPolicy", true, "pool", "show server selection policy for this pool" },
{ "showResponseLatency", true, "", "show a plot of the response time latency distribution" },
- { "showResponseRules", true, "", "show all defined response rules" },
- { "showRules", true, "", "show all defined rules" },
+ { "showResponseRules", true, "[showUUIDs]", "show all defined response rules, optionally with their UUIDs" },
+ { "showRules", true, "[showUUIDs]", "show all defined rules, optionally with their UUIDs" },
+ { "showSelfAnsweredResponseRules", true, "[showUUIDs]", "show all defined self-answered response rules, optionally with their UUIDs" },
{ "showServerPolicy", true, "", "show name of currently operational server selection policy" },
{ "showServers", true, "", "output all servers" },
{ "showTCPStats", true, "", "show some statistics regarding TCP" },
+ { "showTLSContexts", true, "", "list all the available TLS contexts" },
{ "showVersion", true, "", "show the current version" },
{ "shutdown", true, "", "shut down `dnsdist`" },
{ "snmpAgent", true, "enableTraps [, masterSocket]", "enable `SNMP` support. `enableTraps` is a boolean indicating whether traps should be sent and `masterSocket` an optional string specifying how to connect to the master agent"},
{ "TagResponseAction", true, "name, value", "set the tag named 'name' to the given value" },
{ "TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any" },
{ "TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP" },
+ { "TeeAction", true, "remote [, addECS]", "send copy of query to remote, optionally adding ECS info" },
+ { "TempFailureCacheTTLAction", true, "ttl", "set packetcache TTL for temporary failure replies" },
{ "testCrypto", true, "", "test of the crypto all works" },
{ "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"},
{ "topBandwidth", true, "top", "show top-`top` clients that consume the most bandwidth over length of ringbuffer" },
{ "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" },
{ "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" },
{ "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=ServFail), as grouped when optionally cut down to 'labels' labels" },
+ { "topCacheHitResponseRule", true, "", "move the last cache hit response rule to the first position" },
{ "topResponseRule", true, "", "move the last response rule to the first position" },
{ "topRule", true, "", "move the last rule to the first position" },
+ { "topSelfAnsweredResponseRule", true, "", "move the last self-answered response rule to the first position" },
{ "topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels" },
{ "truncateTC", true, "bool", "if set (defaults to no starting with dnsdist 1.2.0) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. Note: turning this on breaks compatibility with RFC 6891." },
{ "unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter" },
class RCodeAction : public DNSAction
{
public:
- RCodeAction(int rcode) : d_rcode(rcode) {}
+ RCodeAction(uint8_t rcode) : d_rcode(rcode) {}
DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
{
dq->dh->rcode = d_rcode;
}
private:
- int d_rcode;
+ uint8_t d_rcode;
};
class TCAction : public DNSAction
}
};
+class TempFailureCacheTTLAction : public DNSAction
+{
+public:
+ TempFailureCacheTTLAction(uint32_t ttl) : d_ttl(ttl)
+ {}
+ TempFailureCacheTTLAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->tempFailureTTL = d_ttl;
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "set tempfailure cache ttl to "+std::to_string(d_ttl);
+ }
+private:
+ uint32_t d_ttl;
+};
+
class ECSPrefixLengthAction : public DNSAction
{
public:
std::string d_value;
};
+template<typename T, typename ActionT>
+static void addAction(GlobalStateHolder<vector<T> > *someRulActions, luadnsrule_t var, std::shared_ptr<ActionT> action, boost::optional<luaruleparams_t> params) {
+ setLuaSideEffect();
+
+ boost::uuids::uuid uuid;
+ parseRuleParams(params, uuid);
+
+ auto rule=makeRule(var);
+ someRulActions->modify([rule, action, uuid](vector<T>& rulactions){
+ rulactions.push_back({rule, action, uuid});
+ });
+}
+
void setupLuaActions()
{
- g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action) {
+ g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action, boost::optional<luaruleparams_t> params) {
+ boost::uuids::uuid uuid;
+ parseRuleParams(params, uuid);
+
auto rule=makeRule(dnsrule);
- return std::make_shared<std::pair< luadnsrule_t, std::shared_ptr<DNSAction> > >(rule, action);
+ DNSDistRuleAction ra({rule, action, uuid});
+ return std::make_shared<DNSDistRuleAction>(ra);
});
- g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
- if (era.type() == typeid(std::shared_ptr<DNSResponseAction>)) {
+ g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
+ if (era.type() != typeid(std::shared_ptr<DNSAction>)) {
throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?");
}
- auto ea = *boost::get<std::shared_ptr<DNSAction>>(&era);
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){
- rulactions.push_back({rule, ea});
- });
+ addAction(&g_rulactions, var, boost::get<std::shared_ptr<DNSAction> >(era), params);
});
- g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func)
- {
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){
- rulactions.push_back({rule,
- std::make_shared<LuaAction>(func)});
- });
- });
-
- g_lua.writeFunction("addLuaResponseAction", [](luadnsrule_t var, LuaResponseAction::func_t func) {
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_resprulactions.modify([rule,func](decltype(g_resprulactions)::value_type& rulactions){
- rulactions.push_back({rule,
- std::make_shared<LuaResponseAction>(func)});
- });
+ g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func, boost::optional<luaruleparams_t> params) {
+ addAction(&g_rulactions, var, std::make_shared<LuaAction>(func), params);
});
- g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
- if (era.type() == typeid(std::shared_ptr<DNSAction>)) {
+ g_lua.writeFunction("addLuaResponseAction", [](luadnsrule_t var, LuaResponseAction::func_t func, boost::optional<luaruleparams_t> params) {
+ addAction(&g_resprulactions, var, std::make_shared<LuaResponseAction>(func), params);
+ });
+
+ g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
+ if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
}
- auto ea = *boost::get<std::shared_ptr<DNSResponseAction>>(&era);
+ addAction(&g_resprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
+ });
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_resprulactions.modify([rule, ea](decltype(g_resprulactions)::value_type& rulactions){
- rulactions.push_back({rule, ea});
- });
+ g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
+ if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
+ throw std::runtime_error("addCacheHitResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
+ }
+
+ addAction(&g_cachehitresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
});
- g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, std::shared_ptr<DNSResponseAction> ea) {
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_cachehitresprulactions.modify([rule, ea](decltype(g_cachehitresprulactions)::value_type& rulactions){
- rulactions.push_back({rule, ea});
- });
+ g_lua.writeFunction("addSelfAnsweredResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
+ if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
+ throw std::runtime_error("addSelfAnsweredResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
+ }
+
+ addAction(&g_selfansweredresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
});
g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
boost::optional<std::shared_ptr<DNSAction>> ret;
auto rulactions = g_rulactions.getCopy();
if(num < rulactions.size())
- ret=rulactions[num].second;
+ ret=rulactions[num].d_action;
return ret;
});
return std::shared_ptr<DNSAction>(new LogAction(fname, binary ? *binary : true, append ? *append : false, buffered ? *buffered : false));
});
- g_lua.writeFunction("RCodeAction", [](int rcode) {
+ g_lua.writeFunction("RCodeAction", [](uint8_t rcode) {
return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
});
return std::shared_ptr<DNSAction>(new SkipCacheAction);
});
+ g_lua.writeFunction("TempFailureCacheTTLAction", [](int maxTTL) {
+ return std::shared_ptr<DNSAction>(new TempFailureCacheTTLAction(maxTTL));
+ });
+
g_lua.writeFunction("DropResponseAction", []() {
return std::shared_ptr<DNSResponseAction>(new DropResponseAction);
});
g_lua.registerMember<bool (DNSQuestion::*)>("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; });
g_lua.registerMember<bool (DNSQuestion::*)>("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; });
g_lua.registerMember<uint16_t (DNSQuestion::*)>("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; });
+ g_lua.registerMember<boost::optional<uint32_t> (DNSQuestion::*)>("tempFailureTTL",
+ [](const DNSQuestion& dq) -> boost::optional<uint32_t> {
+ return dq.tempFailureTTL;
+ },
+ [](DNSQuestion& dq, boost::optional<uint32_t> newValue) {
+ dq.tempFailureTTL = newValue;
+ }
+ );
g_lua.registerFunction<bool(DNSQuestion::*)()>("getDO", [](const DNSQuestion& dq) {
return getEDNSZ((const char*)dq.dh, dq.len) & EDNS_HEADER_FLAG_DO;
});
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dnsdist.hh"
+#include "dnsdist-ecs.hh"
#include "dnsdist-lua.hh"
#include "dnsparser.hh"
class RCodeRule : public DNSRule
{
public:
- RCodeRule(int rcode) : d_rcode(rcode)
+ RCodeRule(uint8_t rcode) : d_rcode(rcode)
{
}
bool matches(const DNSQuestion* dq) const override
return "rcode=="+RCode::to_s(d_rcode);
}
private:
- int d_rcode;
+ uint8_t d_rcode;
+};
+
+class ERCodeRule : public DNSRule
+{
+public:
+ ERCodeRule(uint8_t rcode) : d_rcode(rcode & 0xF), d_extrcode(rcode >> 4)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ // avoid parsing EDNS OPT RR when not needed.
+ if (d_rcode != dq->dh->rcode) {
+ return false;
+ }
+
+ char * optStart = NULL;
+ size_t optLen = 0;
+ bool last = false;
+ int res = locateEDNSOptRR(const_cast<char*>(reinterpret_cast<const char*>(dq->dh)), dq->len, &optStart, &optLen, &last);
+ if (res != 0) {
+ // no EDNS OPT RR
+ return d_extrcode == 0;
+ }
+
+ // root label (1), type (2), class (2), ttl (4) + rdlen (2)
+ if (optLen < 11) {
+ return false;
+ }
+
+ if (*optStart != 0) {
+ // OPT RR Name != '.'
+ return false;
+ }
+ EDNS0Record edns0;
+ static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
+ // copy out 4-byte "ttl" (really the EDNS0 record), after root label (1) + type (2) + class (2).
+ memcpy(&edns0, optStart + 5, sizeof edns0);
+
+ return d_extrcode == edns0.extRCode;
+ }
+ string toString() const override
+ {
+ return "ercode=="+ERCode::to_s(d_rcode | (d_extrcode << 4));
+ }
+private:
+ uint8_t d_rcode; // plain DNS Rcode
+ uint8_t d_extrcode; // upper bits in EDNS0 record
};
class RDRule : public DNSRule
return std::make_shared<NetmaskGroupRule>(nmg, true);
}
+static boost::uuids::uuid makeRuleID(std::string& id)
+{
+ if (id.empty()) {
+ return t_uuidGenerator();
+ }
+
+ boost::uuids::string_generator gen;
+ return gen(id);
+}
+
+void parseRuleParams(boost::optional<luaruleparams_t> params, boost::uuids::uuid& uuid)
+{
+ string uuidStr;
+
+ if (params) {
+ if (params->count("uuid")) {
+ uuidStr = boost::get<std::string>((*params)["uuid"]);
+ }
+ }
+
+ uuid = makeRuleID(uuidStr);
+}
+
+template<typename T>
+static void showRules(GlobalStateHolder<vector<T> > *someRulActions, boost::optional<bool> showUUIDs) {
+ setLuaNoSideEffect();
+ int num=0;
+ if (showUUIDs.get_value_or(false)) {
+ boost::format fmt("%-3d %-38s %9d %-56s %s\n");
+ g_outputBuffer += (fmt % "#" % "UUID" % "Matches" % "Rule" % "Action").str();
+ for(const auto& lim : someRulActions->getCopy()) {
+ string name = lim.d_rule->toString();
+ g_outputBuffer += (fmt % num % boost::uuids::to_string(lim.d_id) % lim.d_rule->d_matches % name % lim.d_action->toString()).str();
+ ++num;
+ }
+ }
+ else {
+ boost::format fmt("%-3d %9d %-56s %s\n");
+ g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
+ for(const auto& lim : someRulActions->getCopy()) {
+ string name = lim.d_rule->toString();
+ g_outputBuffer += (fmt % num % lim.d_rule->d_matches % name % lim.d_action->toString()).str();
+ ++num;
+ }
+ }
+}
+
+template<typename T>
+static void rmRule(GlobalStateHolder<vector<T> > *someRulActions, boost::variant<unsigned int, std::string> id) {
+ setLuaSideEffect();
+ auto rules = someRulActions->getCopy();
+ if (auto str = boost::get<std::string>(&id)) {
+ boost::uuids::string_generator gen;
+ const auto uuid = gen(*str);
+ if (rules.erase(std::remove_if(rules.begin(),
+ rules.end(),
+ [uuid](const T& a) { return a.d_id == uuid; }),
+ rules.end()) == rules.end()) {
+ g_outputBuffer = "Error: no rule matched\n";
+ return;
+ }
+ }
+ else if (auto pos = boost::get<unsigned int>(&id)) {
+ if (*pos >= rules.size()) {
+ g_outputBuffer = "Error: attempt to delete non-existing rule\n";
+ return;
+ }
+ rules.erase(rules.begin()+*pos);
+ }
+ someRulActions->setState(rules);
+}
+
+template<typename T>
+static void topRule(GlobalStateHolder<vector<T> > *someRulActions) {
+ setLuaSideEffect();
+ auto rules = someRulActions->getCopy();
+ if(rules.empty())
+ return;
+ auto subject = *rules.rbegin();
+ rules.erase(std::prev(rules.end()));
+ rules.insert(rules.begin(), subject);
+ someRulActions->setState(rules);
+}
+
+template<typename T>
+static void mvRule(GlobalStateHolder<vector<T> > *someRespRulActions, unsigned int from, unsigned int to) {
+ setLuaSideEffect();
+ auto rules = someRespRulActions->getCopy();
+ if(from >= rules.size() || to > rules.size()) {
+ g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
+ return;
+ }
+ auto subject = rules[from];
+ rules.erase(rules.begin()+from);
+ if(to == rules.size())
+ rules.push_back(subject);
+ else {
+ if(from < to)
+ --to;
+ rules.insert(rules.begin()+to, subject);
+ }
+ someRespRulActions->setState(rules);
+}
+
void setupLuaRules()
{
g_lua.writeFunction("makeRule", makeRule);
g_lua.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
- g_lua.writeFunction("showResponseRules", []() {
- setLuaNoSideEffect();
- boost::format fmt("%-3d %9d %-50s %s\n");
- g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
- int num=0;
- for(const auto& lim : g_resprulactions.getCopy()) {
- string name = lim.first->toString();
- g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
- ++num;
- }
+ g_lua.writeFunction("showResponseRules", [](boost::optional<bool> showUUIDs) {
+ showRules(&g_resprulactions, showUUIDs);
});
- g_lua.writeFunction("rmResponseRule", [](unsigned int num) {
- setLuaSideEffect();
- auto rules = g_resprulactions.getCopy();
- if(num >= rules.size()) {
- g_outputBuffer = "Error: attempt to delete non-existing rule\n";
- return;
- }
- rules.erase(rules.begin()+num);
- g_resprulactions.setState(rules);
+ g_lua.writeFunction("rmResponseRule", [](boost::variant<unsigned int, std::string> id) {
+ rmRule(&g_resprulactions, id);
});
g_lua.writeFunction("topResponseRule", []() {
- setLuaSideEffect();
- auto rules = g_resprulactions.getCopy();
- if(rules.empty())
- return;
- auto subject = *rules.rbegin();
- rules.erase(std::prev(rules.end()));
- rules.insert(rules.begin(), subject);
- g_resprulactions.setState(rules);
+ topRule(&g_resprulactions);
});
g_lua.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
- setLuaSideEffect();
- auto rules = g_resprulactions.getCopy();
- if(from >= rules.size() || to > rules.size()) {
- g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
- return;
- }
- auto subject = rules[from];
- rules.erase(rules.begin()+from);
- if(to == rules.size())
- rules.push_back(subject);
- else {
- if(from < to)
- --to;
- rules.insert(rules.begin()+to, subject);
- }
- g_resprulactions.setState(rules);
+ mvRule(&g_resprulactions, from, to);
});
- g_lua.writeFunction("showCacheHitResponseRules", []() {
- setLuaNoSideEffect();
- boost::format fmt("%-3d %9d %-50s %s\n");
- g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
- int num=0;
- for(const auto& lim : g_cachehitresprulactions.getCopy()) {
- string name = lim.first->toString();
- g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
- ++num;
- }
+ g_lua.writeFunction("showCacheHitResponseRules", [](boost::optional<bool> showUUIDs) {
+ showRules(&g_cachehitresprulactions, showUUIDs);
});
- g_lua.writeFunction("rmCacheHitResponseRule", [](unsigned int num) {
- setLuaSideEffect();
- auto rules = g_cachehitresprulactions.getCopy();
- if(num >= rules.size()) {
- g_outputBuffer = "Error: attempt to delete non-existing rule\n";
- return;
- }
- rules.erase(rules.begin()+num);
- g_cachehitresprulactions.setState(rules);
+ g_lua.writeFunction("rmCacheHitResponseRule", [](boost::variant<unsigned int, std::string> id) {
+ rmRule(&g_cachehitresprulactions, id);
});
g_lua.writeFunction("topCacheHitResponseRule", []() {
- setLuaSideEffect();
- auto rules = g_cachehitresprulactions.getCopy();
- if(rules.empty())
- return;
- auto subject = *rules.rbegin();
- rules.erase(std::prev(rules.end()));
- rules.insert(rules.begin(), subject);
- g_cachehitresprulactions.setState(rules);
+ topRule(&g_cachehitresprulactions);
});
g_lua.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) {
- setLuaSideEffect();
- auto rules = g_cachehitresprulactions.getCopy();
- if(from >= rules.size() || to > rules.size()) {
- g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
- return;
- }
- auto subject = rules[from];
- rules.erase(rules.begin()+from);
- if(to == rules.size())
- rules.push_back(subject);
- else {
- if(from < to)
- --to;
- rules.insert(rules.begin()+to, subject);
- }
- g_cachehitresprulactions.setState(rules);
+ mvRule(&g_cachehitresprulactions, from, to);
});
- g_lua.writeFunction("rmRule", [](unsigned int num) {
- setLuaSideEffect();
- auto rules = g_rulactions.getCopy();
- if(num >= rules.size()) {
- g_outputBuffer = "Error: attempt to delete non-existing rule\n";
- return;
- }
- rules.erase(rules.begin()+num);
- g_rulactions.setState(rules);
+ g_lua.writeFunction("showSelfAnsweredResponseRules", [](boost::optional<bool> showUUIDs) {
+ showRules(&g_selfansweredresprulactions, showUUIDs);
+ });
+
+ g_lua.writeFunction("rmSelfAnsweredResponseRule", [](boost::variant<unsigned int, std::string> id) {
+ rmRule(&g_selfansweredresprulactions, id);
+ });
+
+ g_lua.writeFunction("topSelfAnsweredResponseRule", []() {
+ topRule(&g_selfansweredresprulactions);
+ });
+
+ g_lua.writeFunction("mvSelfAnsweredResponseRule", [](unsigned int from, unsigned int to) {
+ mvRule(&g_selfansweredresprulactions, from, to);
+ });
+
+ g_lua.writeFunction("rmRule", [](boost::variant<unsigned int, std::string> id) {
+ rmRule(&g_rulactions, id);
});
g_lua.writeFunction("topRule", []() {
- setLuaSideEffect();
- auto rules = g_rulactions.getCopy();
- if(rules.empty())
- return;
- auto subject = *rules.rbegin();
- rules.erase(std::prev(rules.end()));
- rules.insert(rules.begin(), subject);
- g_rulactions.setState(rules);
+ topRule(&g_rulactions);
});
g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
- setLuaSideEffect();
- auto rules = g_rulactions.getCopy();
- if(from >= rules.size() || to > rules.size()) {
- g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
- return;
- }
-
- auto subject = rules[from];
- rules.erase(rules.begin()+from);
- if(to == rules.size())
- rules.push_back(subject);
- else {
- if(from < to)
- --to;
- rules.insert(rules.begin()+to, subject);
- }
- g_rulactions.setState(rules);
+ mvRule(&g_rulactions, from, to);
});
g_lua.writeFunction("clearRules", []() {
});
});
- g_lua.writeFunction("setRules", [](std::vector< std::pair<int, std::shared_ptr<std::pair<luadnsrule_t, std::shared_ptr<DNSAction> > > > > newruleactions) {
+ g_lua.writeFunction("setRules", [](std::vector<DNSDistRuleAction>& newruleactions) {
setLuaSideEffect();
g_rulactions.modify([newruleactions](decltype(g_rulactions)::value_type& gruleactions) {
gruleactions.clear();
for (const auto& newruleaction : newruleactions) {
- if (newruleaction.second) {
- auto rule=makeRule(newruleaction.second->first);
- gruleactions.push_back({rule, newruleaction.second->second});
+ if (newruleaction.d_action) {
+ auto rule=makeRule(newruleaction.d_rule);
+ gruleactions.push_back({rule, newruleaction.d_action, newruleaction.d_id});
}
}
});
return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
});
- g_lua.writeFunction("RCodeRule", [](int rcode) {
+ g_lua.writeFunction("RCodeRule", [](uint8_t rcode) {
return std::shared_ptr<DNSRule>(new RCodeRule(rcode));
});
- g_lua.writeFunction("showRules", []() {
- setLuaNoSideEffect();
- boost::format fmt("%-3d %9d %-50s %s\n");
- g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
- int num=0;
- for(const auto& lim : g_rulactions.getCopy()) {
- string name = lim.first->toString();
- g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
- ++num;
- }
+ g_lua.writeFunction("ERCodeRule", [](uint8_t rcode) {
+ return std::shared_ptr<DNSRule>(new ERCodeRule(rcode));
+ });
+
+ g_lua.writeFunction("showRules", [](boost::optional<bool> showUUIDs) {
+ showRules(&g_rulactions, showUUIDs);
});
g_lua.writeFunction("RDRule", []() {
{"YXRRSET", RCode::YXRRSet },
{"NXRRSET", RCode::NXRRSet },
{"NOTAUTH", RCode::NotAuth },
- {"NOTZONE", RCode::NotZone }
+ {"NOTZONE", RCode::NotZone },
+ {"BADVERS", ERCode::BADVERS },
+ {"BADSIG", ERCode::BADSIG },
+ {"BADKEY", ERCode::BADKEY },
+ {"BADTIME", ERCode::BADTIME },
+ {"BADMODE", ERCode::BADMODE },
+ {"BADNAME", ERCode::BADNAME },
+ {"BADALG", ERCode::BADALG },
+ {"BADTRUNC", ERCode::BADTRUNC },
+ {"BADCOOKIE",ERCode::BADCOOKIE },
};
vector<pair<string, int> > dd;
for(const auto& n : QType::names)
#include "dnswriter.hh"
#include "dolog.hh"
#include "lock.hh"
+#include "protobuf.hh"
#include "sodcrypto.hh"
#include <boost/logic/tribool.hpp>
ret->ipBindAddrNoPort=boost::get<bool>(vars["ipBindAddrNoPort"]);
}
+ if(vars.count("addXPF")) {
+ ret->xpfRRCode=std::stoi(boost::get<string>(vars["addXPF"]));
+ }
+
if(vars.count("maxCheckFailures")) {
ret->maxCheckFailures=std::stoi(boost::get<string>(vars["maxCheckFailures"]));
}
g_lua.writeFunction("shutdown", []() {
#ifdef HAVE_SYSTEMD
sd_notify(0, "STOPPING=1");
-#endif
+#endif /* HAVE_SYSTEMD */
+#if 0
+ // Useful for debugging leaks, but might lead to race under load
+ // since other threads are still runing.
+ for(auto& frontend : g_tlslocals) {
+ frontend->cleanup();
+ }
+ g_tlslocals.clear();
+#ifdef HAVE_PROTOBUF
+ google::protobuf::ShutdownProtobufLibrary();
+#endif /* HAVE_PROTOBUF */
+#endif /* 0 */
_exit(0);
} );
#else
g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
#endif
+
});
g_lua.writeFunction("showDNSCryptBinds", []() {
g_useTCPSinglePipe = flag;
});
- g_lua.writeFunction("snmpAgent", [](bool enableTraps, boost::optional<std::string> masterSocket) {
+ g_lua.writeFunction("snmpAgent", [client](bool enableTraps, boost::optional<std::string> masterSocket) {
+ if(client)
+ return;
#ifdef HAVE_NET_SNMP
if (g_configurationDone) {
errlog("snmpAgent() cannot be used at runtime!");
g_outputBuffer="recvmmsg support is not available!\n";
#endif
});
+
+ g_lua.writeFunction("addTLSLocal", [client](const std::string& addr, const std::string& certFile, const std::string& keyFile, boost::optional<localbind_t> vars) {
+ if (client)
+ return;
+#ifdef HAVE_DNS_OVER_TLS
+ setLuaSideEffect();
+ if (g_configurationDone) {
+ g_outputBuffer="addTLSLocal cannot be used at runtime!\n";
+ return;
+ }
+ shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
+ frontend->d_certFile = certFile;
+ frontend->d_keyFile = keyFile;
+
+ if (vars) {
+ bool doTCP = true;
+ parseLocalBindVars(vars, doTCP, frontend->d_reusePort, frontend->d_tcpFastOpenQueueSize, frontend->d_interface, frontend->d_cpus);
+
+ if (vars->count("provider")) {
+ frontend->d_provider = boost::get<const string>((*vars)["provider"]);
+ }
+
+ if (vars->count("ciphers")) {
+ frontend->d_ciphers = boost::get<const string>((*vars)["ciphers"]);
+ }
+
+ if (vars->count("ticketKeyFile")) {
+ frontend->d_ticketKeyFile = boost::get<const string>((*vars)["ticketKeyFile"]);
+ }
+
+ if (vars->count("ticketsKeysRotationDelay")) {
+ frontend->d_ticketsKeyRotationDelay = std::stoi(boost::get<const string>((*vars)["ticketsKeysRotationDelay"]));
+ }
+
+ if (vars->count("numberOfTicketsKeys")) {
+ frontend->d_numberOfTicketsKeys = std::stoi(boost::get<const string>((*vars)["numberOfTicketsKeys"]));
+ }
+ }
+
+ try {
+ frontend->d_addr = ComboAddress(addr, 853);
+ vinfolog("Loading TLS provider %s", frontend->d_provider);
+ g_tlslocals.push_back(frontend); /// only works pre-startup, so no sync necessary
+ }
+ catch(const std::exception& e) {
+ g_outputBuffer="Error: "+string(e.what())+"\n";
+ }
+#else
+ g_outputBuffer="DNS over TLS support is not present!\n";
+#endif
+ });
+
+ g_lua.writeFunction("showTLSContexts", [client]() {
+#ifdef HAVE_DNS_OVER_TLS
+ setLuaNoSideEffect();
+ try {
+ ostringstream ret;
+ boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
+ // 1 2 3 4 5
+ ret << (fmt % "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation" ) << endl;
+ size_t counter = 0;
+ for (const auto& ctx : g_tlslocals) {
+ ret << (fmt % counter % ctx->d_addr.toStringWithPort() % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
+ counter++;
+ }
+ g_outputBuffer = ret.str();
+ }
+ catch(const std::exception& e) {
+ g_outputBuffer = e.what();
+ throw;
+ }
+#else
+ g_outputBuffer="DNS over TLS support is not present!\n";
+#endif
+ });
+
+ g_lua.writeFunction("getTLSContext", [client](size_t index) {
+ std::shared_ptr<TLSCtx> result = nullptr;
+#ifdef HAVE_DNS_OVER_TLS
+ setLuaNoSideEffect();
+ try {
+ if (index < g_tlslocals.size()) {
+ result = g_tlslocals.at(index)->getContext();
+ }
+ else {
+ errlog("Error: trying to get TLS context with index %zu but we only have %zu\n", index, g_tlslocals.size());
+ g_outputBuffer="Error: trying to get TLS context with index " + std::to_string(index) + " but we only have " + std::to_string(g_tlslocals.size()) + "\n";
+ }
+ }
+ catch(const std::exception& e) {
+ g_outputBuffer="Error: "+string(e.what())+"\n";
+ errlog("Error: %s\n", string(e.what()));
+ }
+#else
+ g_outputBuffer="DNS over TLS support is not present!\n";
+#endif
+ return result;
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
+ if (ctx != nullptr) {
+ ctx->rotateTicketsKey(time(nullptr));
+ }
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx> ctx, const std::string& file) {
+ if (ctx != nullptr) {
+ ctx->loadTicketsKeys(file);
+ }
+ });
}
vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
typedef boost::variant<string, vector<pair<int, string>>, std::shared_ptr<DNSRule>, DNSName, vector<pair<int, DNSName> > > luadnsrule_t;
std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var);
+typedef std::unordered_map<std::string, boost::variant<std::string> > luaruleparams_t;
+void parseRuleParams(boost::optional<luaruleparams_t> params, boost::uuids::uuid& uuid);
typedef NetmaskTree<DynBlock> nmts_t;
#include "dolog.hh"
#include "lock.hh"
#include "gettime.hh"
+#include "tcpiohandler.hh"
#include <thread>
#include <atomic>
using std::thread;
using std::atomic;
-/* TCP: the grand design.
- We forward 'messages' between clients and downstream servers. Messages are 65k bytes large, tops.
- An answer might theoretically consist of multiple messages (for example, in the case of AXFR), initially
+/* TCP: the grand design.
+ We forward 'messages' between clients and downstream servers. Messages are 65k bytes large, tops.
+ An answer might theoretically consist of multiple messages (for example, in the case of AXFR), initially
we will not go there.
In a sense there is a strong symmetry between UDP and TCP, once a connection to a downstream has been setup.
return false;
}
-static bool sendResponseToClient(int fd, const char* response, uint16_t responseLen)
+static bool getNonBlockingMsgLenFromClient(TCPIOHandler& handler, uint16_t* len)
+try
{
- return sendSizeAndMsgWithTimeout(fd, responseLen, response, g_tcpSendTimeout, nullptr, nullptr, 0, 0, 0);
+ uint16_t raw;
+ size_t ret = handler.read(&raw, sizeof raw, g_tcpRecvTimeout);
+ if(ret != sizeof raw)
+ return false;
+ *len = ntohs(raw);
+ return true;
+}
+catch(...) {
+ return false;
}
static bool maxConnectionDurationReached(unsigned int maxConnectionDuration, time_t start, unsigned int& remainingTime)
{
/* we get launched with a pipe on which we receive file descriptors from clients that we own
from that point on */
-
+
bool outstanding = false;
time_t lastTCPCleanup = time(nullptr);
g_tcpclientthreads->decrementQueuedCount();
ci=*citmp;
- delete citmp;
+ delete citmp;
uint16_t qlen, rlen;
vector<uint8_t> rewrittenResponse;
}
try {
+ TCPIOHandler handler(ci.fd, g_tcpRecvTimeout, ci.cs->tlsFrontend ? ci.cs->tlsFrontend->getContext() : nullptr, connectionStartTime);
+
for(;;) {
unsigned int remainingTime = 0;
ds = nullptr;
outstanding = false;
- if(!getNonBlockingMsgLen(ci.fd, &qlen, g_tcpRecvTimeout)) {
+ if(!getNonBlockingMsgLenFromClient(handler, &qlen)) {
break;
}
queryBuffer.reserve(qlen + 512);
char* query = &queryBuffer[0];
- readn2WithTimeout(ci.fd, query, qlen, g_tcpRecvTimeout, remainingTime);
-
+ handler.read(query, qlen, g_tcpRecvTimeout, remainingTime);
#ifdef HAVE_DNSCRYPT
std::shared_ptr<DnsCryptQuery> dnsCryptQuery = nullptr;
if (!decrypted) {
if (response.size() > 0) {
- sendResponseToClient(ci.fd, reinterpret_cast<char*>(response.data()), (uint16_t) response.size());
+ handler.writeSizeAndMsg(response.data(), response.size(), g_tcpSendTimeout);
}
break;
}
if(dq.dh->qr) { // something turned it into a response
restoreFlags(dh, origFlags);
+
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(query), dq.size, dq.len, true, &queryRealTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ goto drop;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(query, &dq.len, dq.size, true, dnsCryptQuery, nullptr, nullptr)) {
goto drop;
}
#endif
- sendResponseToClient(ci.fd, query, dq.len);
+ handler.writeSizeAndMsg(query, dq.len, g_tcpSendTimeout);
g_stats.selfAnswered++;
continue;
}
goto drop;
}
#endif
- sendResponseToClient(ci.fd, cachedResponse, cachedResponseSize);
+ handler.writeSizeAndMsg(cachedResponse, cachedResponseSize, g_tcpSendTimeout);
g_stats.cacheHits++;
continue;
}
dq.dh->rcode = RCode::ServFail;
dq.dh->qr = true;
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(query), dq.size, dq.len, false, &queryRealTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ goto drop;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(query, &dq.len, dq.size, true, dnsCryptQuery, nullptr, nullptr)) {
goto drop;
}
#endif
- sendResponseToClient(ci.fd, query, dq.len);
+ handler.writeSizeAndMsg(query, dq.len, g_tcpSendTimeout);
continue;
}
break;
}
+ if (dq.addXPF && ds->xpfRRCode != 0) {
+ addXPF(dq, ds->xpfRRCode);
+ }
+
int dsock = -1;
uint16_t downstreamFailures=0;
#ifdef MSG_FASTOPEN
sendSizeAndMsgWithTimeout(dsock, dq.len, query, ds->tcpSendTimeout, &ds->remote, &ds->sourceAddr, ds->sourceItf, 0, socketFlags);
}
catch(const runtime_error& e) {
- vinfolog("Downstream connection to %s died on us, getting a new one!", ds->getName());
+ vinfolog("Downstream connection to %s died on us (%s), getting a new one!", ds->getName(), e.what());
close(dsock);
dsock=-1;
sockets.erase(ds->remote);
if (isXFR) {
dq.skipCache = true;
}
-
+ bool firstPacket=true;
getpacket:;
if(!getNonBlockingMsgLen(dsock, &rlen, ds->tcpRecvTimeout)) {
break;
}
- if (!responseContentMatches(response, responseLen, qname, qtype, qclass, ds->remote)) {
+ if (firstPacket && !responseContentMatches(response, responseLen, qname, qtype, qclass, ds->remote)) {
break;
}
-
+ firstPacket=false;
if (!fixUpResponse(&response, &responseLen, &responseSize, qname, origFlags, ednsAdded, ecsAdded, rewrittenResponse, addRoom)) {
break;
}
}
if (packetCache && !dq.skipCache) {
- packetCache->insert(cacheKey, qname, qtype, qclass, response, responseLen, true, dh->rcode);
+ packetCache->insert(cacheKey, qname, qtype, qclass, response, responseLen, true, dh->rcode, dq.tempFailureTTL);
}
#ifdef HAVE_DNSCRYPT
goto drop;
}
#endif
- if (!sendResponseToClient(ci.fd, response, responseLen)) {
+ if (!handler.writeSizeAndMsg(response, responseLen, g_tcpSendTimeout)) {
break;
}
rewrittenResponse.clear();
}
}
- catch(...){}
+ catch(...) {}
drop:;
-
+
vinfolog("Closing TCP client connection with %s", ci.remote.toStringWithPort());
if (ci.fd >= 0) {
close(ci.fd);
}
ci.fd = -1;
+
if (ds && outstanding) {
outstanding = false;
--ds->outstanding;
return 0;
}
-
bool getMsgLen32(int fd, uint32_t* len)
try
{
}
}
+template<typename T>
+static json11::Json::array someResponseRulesToJson(GlobalStateHolder<vector<T>>* someResponseRules)
+{
+ using namespace json11;
+ Json::array responseRules;
+ int num=0;
+ auto localResponseRules = someResponseRules->getCopy();
+ for(const auto& a : localResponseRules) {
+ Json::object rule{
+ {"id", num++},
+ {"uuid", boost::uuids::to_string(a.d_id)},
+ {"matches", (double)a.d_rule->d_matches},
+ {"rule", a.d_rule->toString()},
+ {"action", a.d_action->toString()},
+ };
+ responseRules.push_back(rule);
+ }
+ return responseRules;
+}
+
static void connectionThread(int sock, ComboAddress remote, string password, string apiKey, const boost::optional<std::map<std::string, std::string> >& customHeaders)
{
using namespace json11;
for(const auto& a : localRules) {
Json::object rule{
{"id", num++},
- {"matches", (double)a.first->d_matches},
- {"rule", a.first->toString()},
- {"action", a.second->toString()},
- {"action-stats", a.second->getStats()}
+ {"uuid", boost::uuids::to_string(a.d_id)},
+ {"matches", (double)a.d_rule->d_matches},
+ {"rule", a.d_rule->toString()},
+ {"action", a.d_action->toString()},
+ {"action-stats", a.d_action->getStats()}
};
rules.push_back(rule);
}
- Json::array responseRules;
- auto localResponseRules = g_resprulactions.getCopy();
- num=0;
- for(const auto& a : localResponseRules) {
- Json::object rule{
- {"id", num++},
- {"matches", (double)a.first->d_matches},
- {"rule", a.first->toString()},
- {"action", a.second->toString()},
- };
- responseRules.push_back(rule);
- }
-
- Json::array cacheHitResponseRules;
- num=0;
- auto localCacheHitResponseRules = g_cachehitresprulactions.getCopy();
- for(const auto& a : localCacheHitResponseRules) {
- Json::object rule{
- {"id", num++},
- {"matches", (double)a.first->d_matches},
- {"rule", a.first->toString()},
- {"action", a.second->toString()},
- };
- cacheHitResponseRules.push_back(rule);
- }
+ auto responseRules = someResponseRulesToJson(&g_resprulactions);
+ auto cacheHitResponseRules = someResponseRulesToJson(&g_cachehitresprulactions);
+ auto selfAnsweredResponseRules = someResponseRulesToJson(&g_selfansweredresprulactions);
string acl;
{ "rules", rules},
{ "response-rules", responseRules},
{ "cache-hit-response-rules", cacheHitResponseRules},
+ { "self-answered-response-rules", selfAnsweredResponseRules},
{ "acl", acl},
{ "local", localaddresses}
};
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "dnsdist.hh"
-#include "dnsdist-ecs.hh"
-#include "sstuff.hh"
-#include "misc.hh"
-#include <netinet/tcp.h>
-#include <limits>
-#include "dolog.hh"
-#if defined (__OpenBSD__)
-#include <readline/readline.h>
-#else
-#include <editline/readline.h>
-#endif
+#include "config.h"
-#include "dnsname.hh"
-#include "dnswriter.hh"
-#include "base64.hh"
#include <fstream>
-#include "delaypipe.hh"
-#include <unistd.h>
-#include "sodcrypto.hh"
-#include "dnsdist-lua.hh"
+#include <getopt.h>
#include <grp.h>
+#include <limits>
+#include <netinet/tcp.h>
#include <pwd.h>
-#include "lock.hh"
-#include <getopt.h>
#include <sys/resource.h>
-#include "dnsdist-cache.hh"
-#include "gettime.hh"
-#include "ednsoptions.hh"
+#include <unistd.h>
+
+#if defined (__OpenBSD__) || defined(__NetBSD__)
+#include <readline/readline.h>
+#else
+#include <editline/readline.h>
+#endif
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
+#include "dnsdist.hh"
+#include "dnsdist-cache.hh"
+#include "dnsdist-ecs.hh"
+#include "dnsdist-lua.hh"
+
+#include "base64.hh"
+#include "delaypipe.hh"
+#include "dolog.hh"
+#include "dnsname.hh"
+#include "dnswriter.hh"
+#include "ednsoptions.hh"
+#include "gettime.hh"
+#include "lock.hh"
+#include "misc.hh"
+#include "sodcrypto.hh"
+#include "sstuff.hh"
+#include "xpf.hh"
+
#ifdef HAVE_PROTOBUF
thread_local boost::uuids::random_generator t_uuidGenerator;
#endif
GlobalStateHolder<NetmaskGroup> g_ACL;
string g_outputBuffer;
+
vector<std::tuple<ComboAddress, bool, bool, int, string, std::set<int>>> g_locals;
+std::vector<std::shared_ptr<TLSFrontend>> g_tlslocals;
#ifdef HAVE_DNSCRYPT
std::vector<std::tuple<ComboAddress,DnsCryptContext,bool, int, string, std::set<int>>> g_dnsCryptLocals;
#endif
If all downstreams are over QPS, we pick the fastest server */
-GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > g_rulactions;
-GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_resprulactions;
-GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_cachehitresprulactions;
+GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitresprulactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredresprulactions;
+
Rings g_rings;
QueryCount g_qcount;
}
if (ids->packetCache && !ids->skipCache) {
- ids->packetCache->insert(ids->cacheKey, ids->qname, ids->qtype, ids->qclass, response, responseLen, false, dh->rcode);
+ ids->packetCache->insert(ids->cacheKey, ids->qname, ids->qtype, ids->qclass, response, responseLen, false, dh->rcode, ids->tempFailureTTL);
}
if (ids->cs && !ids->cs->muted) {
DNSAction::Action action=DNSAction::Action::None;
string ruleresult;
for(const auto& lr : *holders.rulactions) {
- if(lr.first->matches(&dq)) {
- lr.first->d_matches++;
- action=(*lr.second)(&dq, &ruleresult);
+ if(lr.d_rule->matches(&dq)) {
+ lr.d_rule->d_matches++;
+ action=(*lr.d_action)(&dq, &ruleresult);
switch(action) {
case DNSAction::Action::Allow:
return true;
}
-bool processResponse(LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > >& localRespRulactions, DNSResponse& dr, int* delayMsec)
+bool processResponse(LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRulactions, DNSResponse& dr, int* delayMsec)
{
DNSResponseAction::Action action=DNSResponseAction::Action::None;
std::string ruleresult;
for(const auto& lr : *localRespRulactions) {
- if(lr.first->matches(&dr)) {
- lr.first->d_matches++;
- action=(*lr.second)(&dr, &ruleresult);
+ if(lr.d_rule->matches(&dr)) {
+ lr.d_rule->d_matches++;
+ action=(*lr.d_action)(&dr, &ruleresult);
switch(action) {
case DNSResponseAction::Action::Allow:
return true;
return result;
}
+bool addXPF(DNSQuestion& dq, uint16_t optionCode)
+{
+ std::string payload = generateXPFPayload(dq.tcp, *dq.remote, *dq.local);
+ uint8_t root = '\0';
+ dnsrecordheader drh;
+ drh.d_type = htons(optionCode);
+ drh.d_class = htons(QClass::IN);
+ drh.d_ttl = 0;
+ drh.d_clen = htons(payload.size());
+ size_t recordHeaderLen = sizeof(root) + sizeof(drh);
+
+ size_t available = dq.size - dq.len;
+
+ if ((payload.size() + recordHeaderLen) > available) {
+ return false;
+ }
+
+ size_t pos = dq.len;
+ memcpy(reinterpret_cast<char*>(dq.dh) + pos, &root, sizeof(root));
+ pos += sizeof(root);
+ memcpy(reinterpret_cast<char*>(dq.dh) + pos, &drh, sizeof(drh));
+ pos += sizeof(drh);
+ memcpy(reinterpret_cast<char*>(dq.dh) + pos, payload.data(), payload.size());
+ pos += payload.size();
+
+ dq.len = pos;
+
+ dq.dh->arcount = htons(ntohs(dq.dh->arcount) + 1);
+
+ return true;
+}
+
static bool isUDPQueryAcceptable(ClientState& cs, LocalHolders& holders, const struct msghdr* msgh, const ComboAddress& remote, ComboAddress& dest)
{
if (msgh->msg_flags & MSG_TRUNC) {
char* response = query;
uint16_t responseLen = dq.len;
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(response), dq.size, responseLen, false, &realTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ return;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(response, &responseLen, dq.size, false, dnsCryptQuery, nullptr, nullptr)) {
return;
dq.dh->rcode = RCode::ServFail;
dq.dh->qr = true;
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(response), dq.size, responseLen, false, &realTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ return;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(response, &responseLen, dq.size, false, dnsCryptQuery, nullptr, nullptr)) {
return;
return;
}
+ if (dq.addXPF && ss->xpfRRCode != 0) {
+ addXPF(dq, ss->xpfRRCode);
+ }
+
ss->queries++;
unsigned int idOffset = (ss->idOffset++) % ss->idStates.size();
ids->qtype = dq.qtype;
ids->qclass = dq.qclass;
ids->delayMsec = delayMsec;
+ ids->tempFailureTTL = dq.tempFailureTTL;
ids->origFlags = origFlags;
ids->cacheKey = cacheKey;
ids->skipCache = dq.skipCache;
cout<<"dnsdist "<<VERSION<<" ("<<LUA_RELEASE<<")"<<endl;
#endif
cout<<"Enabled features: ";
+#ifdef HAVE_DNS_OVER_TLS
+ cout<<"dns-over-tls(";
+#ifdef HAVE_GNUTLS
+ cout<<"gnutls ";
+#endif
+#ifdef HAVE_LIBSSL
+ cout<<"openssl";
+#endif
+ cout<<") ";
+#endif
#ifdef HAVE_DNSCRYPT
cout<<"dnscrypt ";
#endif
SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEADDR, 1);
#ifdef TCP_DEFER_ACCEPT
- SSetsockopt(cs->tcpFD, SOL_TCP,TCP_DEFER_ACCEPT, 1);
+ SSetsockopt(cs->tcpFD, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
#endif
if (std::get<3>(local) > 0) {
#ifdef TCP_FASTOPEN
cs->tcpFD = SSocket(cs->local.sin4.sin_family, SOCK_STREAM, 0);
SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEADDR, 1);
#ifdef TCP_DEFER_ACCEPT
- SSetsockopt(cs->tcpFD, SOL_TCP,TCP_DEFER_ACCEPT, 1);
+ SSetsockopt(cs->tcpFD, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
#endif
if (std::get<3>(dcLocal) > 0) {
#ifdef TCP_FASTOPEN
}
#endif
+ for(auto& frontend : g_tlslocals) {
+ ClientState* cs = new ClientState;
+ cs->local = frontend->d_addr;
+ cs->tcpFD = SSocket(cs->local.sin4.sin_family, SOCK_STREAM, 0);
+ SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEADDR, 1);
+#ifdef TCP_DEFER_ACCEPT
+ SSetsockopt(cs->tcpFD, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
+#endif
+ if (frontend->d_tcpFastOpenQueueSize > 0) {
+#ifdef TCP_FASTOPEN
+ SSetsockopt(cs->tcpFD, IPPROTO_TCP, TCP_FASTOPEN, frontend->d_tcpFastOpenQueueSize);
+#else
+ warnlog("TCP Fast Open has been configured on local address '%s' but is not supported", cs->local.toStringWithPort());
+#endif
+ }
+ if (frontend->d_reusePort) {
+#ifdef SO_REUSEPORT
+ SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEPORT, 1);
+#else
+ warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", cs.local.toStringWithPort());
+#endif
+ }
+ if(cs->local.sin4.sin_family == AF_INET6) {
+ SSetsockopt(cs->tcpFD, IPPROTO_IPV6, IPV6_V6ONLY, 1);
+ }
+
+ if (!frontend->d_interface.empty()) {
+#ifdef SO_BINDTODEVICE
+ int res = setsockopt(cs->tcpFD, SOL_SOCKET, SO_BINDTODEVICE, frontend->d_interface.c_str(), frontend->d_interface.length());
+ if (res != 0) {
+ warnlog("Error setting up the interface on local address '%s': %s", cs->local.toStringWithPort(), strerror(errno));
+ }
+#else
+ warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", cs->local.toStringWithPort());
+#endif
+ }
+
+ cs->cpus = frontend->d_cpus;
+
+ bindAny(cs->local.sin4.sin_family, cs->tcpFD);
+ if (frontend->setupTLS()) {
+ cs->tlsFrontend = frontend;
+ SBind(cs->tcpFD, cs->local);
+ SListen(cs->tcpFD, 64);
+ warnlog("Listening on %s for TLS", cs->local.toStringWithPort());
+ toLaunch.push_back(cs);
+ g_frontends.push_back(cs);
+ tcpBindsCount++;
+ }
+ else {
+ delete cs;
+ errlog("Error while setting up TLS on local address '%s', exiting", cs->local.toStringWithPort());
+ _exit(EXIT_FAILURE);
+ }
+ }
+
if(g_cmdLine.beDaemon) {
g_console=false;
daemonize();
#include "bpf-filter.hh"
#include <string>
#include <unordered_map>
+#include "tcpiohandler.hh"
-
-#ifdef HAVE_PROTOBUF
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
-#endif
+#include <boost/uuid/uuid_io.hpp>
void* carbonDumpThread();
uint64_t uptimeOfProcess(const std::string& str);
static constexpr char const *strSep = "\t";
};
-#ifdef HAVE_PROTOBUF
extern thread_local boost::uuids::random_generator t_uuidGenerator;
-#endif
struct DNSQuestion
{
- DNSQuestion(const DNSName* name, uint16_t type, uint16_t class_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t queryLen, bool isTcp): qname(name), qtype(type), qclass(class_), local(lc), remote(rem), dh(header), size(bufferSize), len(queryLen), ecsPrefixLength(rem->sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6), tcp(isTcp), ecsOverride(g_ECSOverride) { }
+ DNSQuestion(const DNSName* name, uint16_t type, uint16_t class_, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t queryLen, bool isTcp): qname(name), qtype(type), qclass(class_), local(lc), remote(rem), dh(header), size(bufferSize), len(queryLen), ecsPrefixLength(rem->sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6), tempFailureTTL(boost::none), tcp(isTcp), ecsOverride(g_ECSOverride) { }
#ifdef HAVE_PROTOBUF
boost::optional<boost::uuids::uuid> uniqueId;
size_t size;
uint16_t len;
uint16_t ecsPrefixLength;
+ boost::optional<uint32_t> tempFailureTTL;
const bool tcp;
bool skipCache{false};
bool ecsOverride;
bool useECS{true};
+ bool addXPF{true};
};
struct DNSResponse : DNSQuestion
struct IDState
{
- IDState() : origFD(-1), sentTime(true), delayMsec(0) { origDest.sin4.sin_family = 0;}
+ IDState() : origFD(-1), sentTime(true), delayMsec(0), tempFailureTTL(boost::none) { origDest.sin4.sin_family = 0;}
IDState(const IDState& orig)
{
origFD = orig.origFD;
origRemote = orig.origRemote;
origDest = orig.origDest;
delayMsec = orig.delayMsec;
+ tempFailureTTL = orig.tempFailureTTL;
age.store(orig.age.load());
}
uint16_t origID; // 2
uint16_t origFlags; // 2
int delayMsec;
+ boost::optional<uint32_t> tempFailureTTL;
bool ednsAdded{false};
bool ecsAdded{false};
bool skipCache{false};
#ifdef HAVE_DNSCRYPT
DnsCryptContext* dnscryptCtx{0};
#endif
+ shared_ptr<TLSFrontend> tlsFrontend;
std::atomic<uint64_t> queries{0};
int udpFD{-1};
int tcpFD{-1};
int tcpSendTimeout{30};
unsigned int sourceItf{0};
uint16_t retries{5};
+ uint16_t xpfRRCode{0};
uint8_t currentCheckFailures{0};
uint8_t maxCheckFailures{1};
StopWatch sw;
EDNS_HEADER_FLAG_DO = 32768
};
+struct DNSDistRuleAction
+{
+ std::shared_ptr<DNSRule> d_rule;
+ std::shared_ptr<DNSAction> d_action;
+ boost::uuids::uuid d_id;
+};
+
+struct DNSDistResponseRuleAction
+{
+ std::shared_ptr<DNSRule> d_rule;
+ std::shared_ptr<DNSResponseAction> d_action;
+ boost::uuids::uuid d_id;
+};
+
extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
extern DNSAction::Action g_dynBlockAction;
extern GlobalStateHolder<ServerPolicy> g_policy;
extern GlobalStateHolder<servers_t> g_dstates;
extern GlobalStateHolder<pools_t> g_pools;
-extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > g_rulactions;
-extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_resprulactions;
-extern GlobalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > g_cachehitresprulactions;
+extern GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions;
+extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions;
+extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitresprulactions;
+extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredresprulactions;
extern GlobalStateHolder<NetmaskGroup> g_ACL;
extern ComboAddress g_serverControl; // not changed during runtime
extern std::vector<std::tuple<ComboAddress, bool, bool, int, std::string, std::set<int>>> g_locals; // not changed at runtime (we hope XXX)
+extern std::vector<shared_ptr<TLSFrontend>> g_tlslocals;
extern vector<ClientState*> g_frontends;
extern std::string g_key; // in theory needs locking
extern bool g_truncateTC;
struct LocalHolders
{
- LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), rulactions(g_rulactions.getLocal()), cacheHitRespRulactions(g_cachehitresprulactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal())
+ LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), rulactions(g_rulactions.getLocal()), cacheHitRespRulactions(g_cachehitresprulactions.getLocal()), selfAnsweredRespRulactions(g_selfansweredresprulactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal())
{
}
LocalStateHolder<NetmaskGroup> acl;
LocalStateHolder<ServerPolicy> policy;
- LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > > rulactions;
- LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > > cacheHitRespRulactions;
+ LocalStateHolder<vector<DNSDistRuleAction> > rulactions;
+ LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRulactions;
+ LocalStateHolder<vector<DNSDistResponseRuleAction> > selfAnsweredRespRulactions;
LocalStateHolder<servers_t> servers;
LocalStateHolder<NetmaskTree<DynBlock> > dynNMGBlock;
LocalStateHolder<SuffixMatchTree<DynBlock> > dynSMTBlock;
bool responseContentMatches(const char* response, const uint16_t responseLen, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& remote);
bool processQuery(LocalHolders& holders, DNSQuestion& dq, string& poolname, int* delayMsec, const struct timespec& now);
-bool processResponse(LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > >& localRespRulactions, DNSResponse& dr, int* delayMsec);
+bool processResponse(LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRulactions, DNSResponse& dr, int* delayMsec);
bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, std::vector<uint8_t>& rewrittenResponse, uint16_t addRoom);
void restoreFlags(struct dnsheader* dh, uint16_t origFlags);
bool checkQueryHeaders(const struct dnsheader* dh);
bool encryptResponse(char* response, uint16_t* responseLen, size_t responseSize, bool tcp, std::shared_ptr<DnsCryptQuery> dnsCryptQuery, dnsheader** dh, dnsheader* dhCopy);
#endif
+bool addXPF(DNSQuestion& dq, uint16_t optionCode);
+
#include "dnsdist-snmp.hh"
extern bool g_snmpEnabled;
AM_CPPFLAGS += $(RE2_CFLAGS)
endif
+if HAVE_DNS_OVER_TLS
+if HAVE_LIBSSL
+AM_CPPFLAGS += $(LIBSSL_CFLAGS)
+endif
+
+if HAVE_GNUTLS
+AM_CPPFLAGS += $(GNUTLS_CFLAGS)
+endif
+endif
EXTRA_DIST=dnslabeltext.rl \
dnsdistconf.lua \
sodcrypto.cc sodcrypto.hh \
sstuff.hh \
statnode.cc statnode.hh \
+ tcpiohandler.cc tcpiohandler.hh \
+ xpf.cc xpf.hh \
ext/luawrapper/include/LuaContext.hpp \
ext/json11/json11.cpp \
ext/json11/json11.hpp \
dnsdist_LDADD += $(RE2_LIBS)
endif
+if HAVE_DNS_OVER_TLS
+if HAVE_GNUTLS
+dnsdist_LDADD += -lgnutls
+endif
+
+if HAVE_LIBSSL
+dnsdist_LDADD += $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
+endif
+endif
+
if !HAVE_LUA_HPP
BUILT_SOURCES += lua.hpp
nodist_dnsdist_SOURCES = lua.hpp
sholder.hh \
sodcrypto.cc \
sstuff.hh \
- testrunner.cc
+ testrunner.cc \
+ xpf.cc xpf.hh
testrunner_LDFLAGS = \
$(AM_LDFLAGS) \
PDNS_CHECK_NETWORK_LIBS
PDNS_CHECK_PTHREAD_NP
-boost_required_version=1.35
PDNS_WITH_PROTOBUF
-AS_IF([test "x$PROTOBUF_LIBS" != "x" -a x"$PROTOC" != "x"],
- # The protobuf code needs boost::uuid, which is available from 1.42 onward
- [AC_MSG_WARN([Bumping minimal Boost requirement to 1.42. To keep the requirement at 1.35, disable protobuf support])
- boost_required_version=1.42]
-)
+boost_required_version=1.42
BOOST_REQUIRE([$boost_required_version])
PDNS_ENABLE_UNIT_TESTS
])
PDNS_CHECK_LUA_HPP
+DNSDIST_ENABLE_DNS_OVER_TLS
+DNSDIST_CHECK_GNUTLS
+DNSDIST_CHECK_LIBSSL
+AS_IF([test "x$enable_dns_over_tls" != "xno"], [
+ AS_IF([test "$HAVE_LIBSSL" = "1"], [
+ # we need libcrypto if libssl is enabled
+ PDNS_CHECK_LIBCRYPTO
+ ])
+ AS_IF([test "$HAVE_GNUTLS" = "0" -a "$HAVE_LIBSSL" = "0"], [
+ AC_MSG_ERROR([DNS over TLS support requested but neither GnuTLS nor OpenSSL are available])
+ ])
+])
+
AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
AC_MSG_CHECKING([whether we will enable compiler security checks])
[AC_MSG_NOTICE([SNMP: yes])],
[AC_MSG_NOTICE([SNMP: no])]
)
+AS_IF([test "x$enable_dns_over_tls" != "xno"],
+ [AC_MSG_NOTICE([DNS over TLS: yes])],
+ [AC_MSG_NOTICE([DNS over TLS: no])]
+)
+AS_IF([test "x$GNUTLS_LIBS" != "x"],
+ [AC_MSG_NOTICE([GnuTLS: yes])],
+ [AC_MSG_NOTICE([GnuTLS: no])]
+)
+AS_IF([test "x$LIBSSL_LIBS" != "x"],
+ [AC_MSG_NOTICE([OpenSSL: yes])],
+ [AC_MSG_NOTICE([OpenSSL: no])]
+)
AC_MSG_NOTICE([])
.. code-block:: lua
- addAction("bad-domain.example.", dropAction())
+ addAction("bad-domain.example.", DropAction())
Or we configure a server pool dedicated to receiving the nasty stuff:
:>json string acl: A string of comma-separated netmasks currently allowed by the :ref:`ACL <ACL>`.
:>json list cache-hit-response-rules: A list of :json:object:`ResponseRule` objects applied on cache hits
+ :>json list self-answered-response-rules: A list of :json:object:`ResponseRule` objects applied on self-answered queries
:>json string daemon_type: The type of daemon, always "dnsdist"
:>json list frontends: A list of :json:object:`Frontend` objects
:>json list pools: A list of :json:object:`Pool` objects
:property string action: The action taken when the rule matches (e.g. "to pool abuse")
:property dict action-stats: A list of statistics whose content varies depending on the kind of rule
- :property integer id: The identifier (or order) of this rule
+ :property integer id: The position of this rule
:property integer matches: How many times this rule was hit
:property string rule: The matchers for the packet (e.g. "qname==bad-domain1.example., bad-domain2.example.")
+ :property string uuid: The UUID of this rule
.. json:object:: ResponseRule
higher than 0 to enable TCP Fast Open when available.
Default is 0.
+.. function:: addTLSLocal(address, certFile, keyFile[, options])
+
+ .. versionadded:: 1.3.0
+
+ Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate.
+
+ :param str address: The IP Address with an optional port to listen on.
+ The default port is 853.
+ :param str certFile: The path to a X.509 certificate file in PEM format.
+ :param str keyFile: The path to the private key file corresponding to the certificate.
+ :param table options: A table with key: value pairs with listen options.
+
+ Options:
+
+ * ``doTCP=true``: bool - Also bind on TCP on ``address``.
+ * ``reusePort=false``: bool - Set the ``SO_REUSEPORT`` socket option.
+ * ``tcpFastOpenSize=0``: int - Set the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0.
+ * ``interface=""``: str - Set the network interface to use.
+ * ``cpus={}``: table - Set the CPU affinity for this listener thread, asking the scheduler to run it on a single CPU id, or a set of CPU ids. This parameter is only available if the OS provides the pthread_setaffinity_np() function.
+ * ``provider``: str - The TLS library to use between GnuTLS and OpenSSL, if they were available and enabled at compilation time.
+ * ``ciphers``: str - The TLS ciphers to use. The exact format depends on the provider used.
+ * ``numberOfTicketsKeys``: int - The maximum number of tickets keys to keep in memory at the same time, if the provider supports it (GnuTLS doesn't, OpenSSL does). Only one key is marked as active and used to encrypt new tickets while the remaining ones can still be used to decrypt existing tickets after a rotation. Default to 5.
+ * ``ticketKeyFile``: str - The path to a file from where TLS tickets keys should be loaded, to support RFC 5077. These keys should be rotated often and never written to persistent storage to preserve forward secrecy. The default is to generate a random key. The OpenSSL provider supports several tickets keys to be able to decrypt existing sessions after the rotation, while the GnuTLS provider only supports one key.
+ * ``ticketsKeysRotationDelay``: int - Set the delay before the TLS tickets key is rotated, in seconds. Default is 43200 (12h).
+
.. function:: setLocal(address[, options])
.. versionadded:: 1.2.0
checkName=STRING, -- Use STRING as QNAME in the health-check query, default: "a.root-servers.net."
checkType=STRING, -- Use STRING as QTYPE in the health-check query, default: "A"
setCD=BOOL, -- Set the CD (Checking Disabled) flag in the health-check query, default: false
- maxCheckFailures=NUM, -- Allow NUM check failures before declaring the backend down, default: false
+ maxCheckFailures=NUM, -- Allow NUM check failures before declaring the backend down, default: 1
mustResolve=BOOL, -- Set to true when the health check MUST return a NOERROR RCODE and an answer
useClientSubnet=BOOL, -- Add the client's IP address in the EDNS Client Subnet option when forwarding the query to this backend
- source=STRING -- The source address or interface to use for queries to this backend, by default this is left to the kernel's address selection
+ source=STRING, -- The source address or interface to use for queries to this backend, by default this is left to the kernel's address selection
-- The following formats are supported:
-- "address", e.g. "192.0.2.2"
-- "interface name", e.g. "eth0"
-- "address@interface", e.g. "192.0.2.2@eth0"
+ addXPF=NUM -- Add the client's IP address and port to the query, along with the original destination address and port,
+ -- using the experimental XPF record from `draft-bellis-dnsop-xpf <https://datatracker.ietf.org/doc/draft-bellis-dnsop-xpf/>`_ and the specified option code. Default is disabled (0)
})
:param str server_string: A simple IP:PORT string.
Print all statistics dnsdist gathers
+.. function:: getTLSContext(idx)
+ .. versionadded:: 1.3.0
+
+ Return the TLSContext object for the context of index ``idx``.
+
.. function:: grepq(selector[, num])
grepq(selectors[, num])
Show some statistics regarding TCP
+.. function:: showTLSContexts()
+ .. versionadded:: 1.3.0
+
+ Print the list of all availables DNS over TLS contexts.
+
.. function:: showVersion()
Print the version of dnsdist
If this function exists, it is called every second to so regular tasks.
This can be used for e.g. :doc:`Dynamic Blocks <../guides/dynblocks>`.
+
+TLSContext
+~~~~~~~~~~
+
+.. class:: TLSContext
+ .. versionadded:: 1.3.0
+
+ This object represents an address and port dnsdist is listening on for DNS over TLS queries.
+
+.. classmethod:: TLSContext:rotateTicketsKey()
+
+ Replace the current TLS tickets key by a new random one.
+
+.. classmethod:: TLSContext:loadTicketsKeys(ticketsKeysFile)
+
+ Load new tickets keys from the selected file, replacing the existing ones. These keys should be rotated often and never written to persistent storage to preserve forward secrecy. The default is to generate a random key. The OpenSSL provider supports several tickets keys to be able to decrypt existing sessions after the rotation, while the GnuTLS provider only supports one key.
+
+ :param str ticketsKeysFile: The path to a file from where TLS tickets keys should be loaded.
- ``DNSOpcode.Notify``
- ``DNSOpcode.Update``
+Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
+
.. _DNSQClass:
QClass
- ``QClass.NONE``
- ``QClass.ANY``
+Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
+
.. _DNSRCode:
RCode
- ``dnsdist.NXRRSET``
- ``dnsdist.NOTAUTH``
- ``dnsdist.NOTZONE``
+- ``dnsdist.BADVERS``
+- ``dnsdist.BADSIG``
+- ``dnsdist.BADKEY``
+- ``dnsdist.BADTIME``
+- ``dnsdist.BADMODE``
+- ``dnsdist.BADNAME``
+- ``dnsdist.BADALG``
+- ``dnsdist.BADTRUNC``
+- ``dnsdist.BADCOOKIE``
+
+RCodes below and including ``BADVERS`` are extended RCodes that can only be matched using :func:`ERCodeRule`.
+
+Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
.. _DNSSection:
:param string domain: Domain name to spoof for
:param string cname: Domain name to add CNAME to
-.. function:: addLuaAction(DNSrule, function)
+.. function:: addLuaAction(DNSrule, function [, options])
+
+ .. versionchanged:: 1.3.0
+ Added the optional parameter ``options``.
Invoke a Lua function that accepts a :class:`DNSQuestion`.
This function works similar to using :func:`LuaAction`.
-
The ``function`` should return a :ref:`DNSAction`.
:param DNSRule: match queries based on this rule
:param string function: the name of a Lua function
+ :param table options: A table with key: value pairs with options.
+
+ Options:
+
+ * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.
-.. function:: addLuaResponseAction(DNSrule, function)
+.. function:: addLuaResponseAction(DNSrule, function [, options])
+
+ .. versionchanged:: 1.3.0
+ Added the optional parameter ``options``.
Invoke a Lua function that accepts a :class:`DNSQuestion` on the response.
This function works similar to using :func:`LuaAction`.
:param DNSRule: match queries based on this rule
:param string function: the name of a Lua function
+ :param table options: A table with key: value pairs with options.
+
+ Options:
+
+ * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.
.. function:: addNoRecurseRule(DNSrule)
For Rules related to the incoming query:
-.. function:: addAction(DNSrule, action)
+.. function:: addAction(DNSrule, action [, options])
+
+ .. versionchanged:: 1.3.0
+ Added the optional parameter ``options``.
Add a Rule and Action to the existing rules.
:param DNSrule rule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule`
:param action: The action to take
+ :param table options: A table with key: value pairs with options.
+
+ Options:
+
+ * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.
.. function:: clearRules()
:param int from: Rule number to move
:param int to: Location to more the Rule to
-.. function:: newRuleAction(rule, action)
+.. function:: newRuleAction(rule, action[, options])
+
+ .. versionchanged:: 1.3.0
+ Added the optional parameter ``options``.
Return a pair of DNS Rule and DNS Action, to be used with :func:`setRules`.
:param Rule rule: A `Rule <#traffic-matching>`_
:param Action action: The `Action <#actions>`_ to apply to the matched traffic
+ :param table options: A table with key: value pairs with options.
+
+ Options:
+
+ * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.
.. function:: setRules(rules)
:param [RuleAction] rules: A list of RuleActions
-.. function:: showRules()
+.. function:: showRules([showUUIDs])
+
+ Show all defined rules for queries, optionally displaying their UUIDs.
- Show all defined rules for queries.
+ :param bool showUUIDs: Whether to display the UUIDs, defaults to false
.. function:: topRule()
Move the last rule to the first position.
-.. function:: rmRule(n)
+.. function:: rmRule(id)
+
+ .. versionchanged:: 1.3.0
+ ``id`` can now be an UUID.
- Remove rule ``n``.
+ Remove rule ``id``.
- :param int n: Rule number to remove
+ :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise
For Rules related to responses:
-.. function:: addResponseAction(DNSRule, action)
+.. function:: addResponseAction(DNSRule, action [, options])
+
+ .. versionchanged:: 1.3.0
+ Added the optional parameter ``options``.
Add a Rule and Action for responses to the existing rules.
:param DNSRule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule`
:param action: The action to take
+ :param table options: A table with key: value pairs with options.
+
+ Options:
+
+ * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.
.. function:: mvResponseRule(from, to)
:param int from: Rule number to move
:param int to: Location to more the Rule to
-.. function:: rmResponseRule(n)
+.. function:: rmResponseRule(id)
+
+ .. versionchanged:: 1.3.0
+ ``id`` can now be an UUID.
- Remove response rule ``n``.
+ Remove response rule ``id``.
- :param int n: Rule number to remove
+ :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise
-.. function:: showResponseRules()
+.. function:: showResponseRules([showUUIDs])
- Show all defined response rules.
+ Show all defined response rules, optionally displaying their UUIDs.
+
+ :param bool showUUIDs: Whether to display the UUIDs, defaults to false
.. function:: topResponseRule()
Move the last response rule to the first position.
-Functions for manipulation Cache Hit Rules:
+Functions for manipulating Cache Hit Respone Rules:
-.. function:: addCacheHitAction(DNSRule, action)
+.. function:: addCacheHitResponseAction(DNSRule, action [, options])
.. versionadded:: 1.2.0
- Add a Rule and Action for Cache Hits to the existing rules.
+ .. versionchanged:: 1.3.0
+ Added the optional parameter ``options``.
+
+ Add a Rule and ResponseAction for Cache Hits to the existing rules.
:param DNSRule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule`
:param action: The action to take
+ :param table options: A table with key: value pairs with options.
+
+ Options:
+
+ * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.
.. function:: mvCacheHitResponseRule(from, to)
:param int from: Rule number to move
:param int to: Location to more the Rule to
-.. function:: rmCacheHitResponseRule(n)
+.. function:: rmCacheHitResponseRule(id)
.. versionadded:: 1.2.0
- Remove cache hit response rule ``n``.
+ .. versionchanged:: 1.3.0
+ ``id`` can now be an UUID.
- :param int n: Rule number to remove
+ :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise
-.. function:: showCacheHitResponseRules()
+.. function:: showCacheHitResponseRules([showUUIDs])
.. versionadded:: 1.2.0
- Show all defined cache hit response rules.
+ Show all defined cache hit response rules, optionally displaying their UUIDs.
+
+ :param bool showUUIDs: Whether to display the UUIDs, defaults to false
.. function:: topCacheHitResponseRule()
Move the last cache hit response rule to the first position.
+Functions for manipulating Self-Answered Response Rules:
+
+.. function:: addSelfAnsweredResponseAction(DNSRule, action [, options])
+
+ .. versionadded:: 1.3.0
+
+ Add a Rule and Action for Self-Answered queries to the existing rules.
+
+ :param DNSRule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule`
+ :param action: The action to take
+
+.. function:: mvSelfAnsweredResponseRule(from, to)
+
+ .. versionadded:: 1.3.0
+
+ Move self answered response rule ``from`` to a position where it is in front of ``to``.
+ ``to`` can be one larger than the largest rule, in which case the rule will be moved to the last position.
+
+ :param int from: Rule number to move
+ :param int to: Location to more the Rule to
+
+.. function:: rmSelfAnsweredResponseRule(id)
+
+ .. versionadded:: 1.3.0
+
+ Remove self answered response rule ``id``.
+
+ :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise
+
+.. function:: showSelfAnsweredResponseRules([showUUIDs])
+
+ .. versionadded:: 1.3.0
+
+ Show all defined self answered response rules, optionally displaying their UUIDs.
+
+ :param bool showUUIDs: Whether to display the UUIDs, defaults to false
+
+.. function:: topSelfAnsweredResponseRule()
+
+ .. versionadded:: 1.3.0
+
+ Move the last self answered response rule to the first position.
+
.. _RulesIntro:
Matching Packets (Selectors)
.. function:: RCodeRule(rcode)
- Matches queries or responses the specified ``rcode``.
- ``rcode`` can be specified as an integer or as one of the built-in `RCode <DNSRcode>`.
+ Matches queries or responses with the specified ``rcode``.
+ ``rcode`` can be specified as an integer or as one of the built-in :ref:`DNSRCode`.
+ Only the non-extended RCode is matched (lower 4bits).
+
+ :param int rcode: The RCODE to match on
+
+.. function:: ERCodeRule(rcode)
+
+ Matches queries or responses with the specified ``rcode``.
+ ``rcode`` can be specified as an integer or as one of the built-in :ref:`DNSRCode`.
+ The full 16bit RCode will be matched. If no EDNS OPT RR is present, the upper 12 bits are treated as 0.
:param int rcode: The RCODE to match on
Combining Rules
~~~~~~~~~~~~~~~
-.. function:: andRule(selectors)
+.. function:: AndRule(selectors)
Matches traffic if all ``selectors`` match.
:param {Rule} selector: A table of Rules
-Convience Functions
-~~~~~~~~~~~~~~~~~~~
+Convenience Functions
+~~~~~~~~~~~~~~~~~~~~~
.. function:: makeRule(rule)
.. function:: RCodeAction(rcode)
Reply immediatly by turning the query into a response with the specified ``rcode``.
- ``rcode`` can be specified as an integer or as one of the built-in `RCode <#rcode>`_.
+ ``rcode`` can be specified as an integer or as one of the built-in :ref:`DNSRCode`.
:param int rcode: The RCODE to respond with.
:param string remote: An IP:PORT conbination to send the copied queries to
:param bool addECS: Whether or not to add ECS information. Default false
+.. function:: TempFailureCacheTTLAction(ttl)
+
+ Set the cache TTL to use for ServFail and Refused replies. TTL is not applied for successful replies.
+ :param int ttl: Cache TTL for temporary failure replies
--- /dev/null
+AC_DEFUN([DNSDIST_CHECK_GNUTLS], [
+ AC_MSG_CHECKING([whether we will be linking in GnuTLS])
+ HAVE_GNUTLS=0
+ AC_ARG_ENABLE([gnutls],
+ AS_HELP_STRING([--enable-gnutls],[use GnuTLS @<:@default=auto@:>@]),
+ [enable_gnutls=$enableval],
+ [enable_gnutls=auto],
+ )
+ AC_MSG_RESULT([$enable_gnutls])
+
+ AS_IF([test "x$enable_gnutls" != "xno"], [
+ AS_IF([test "x$enable_gnutls" = "xyes" -o "x$enable_gnutls" = "xauto"], [
+ # we require gnutls_certificate_set_x509_key_file, added in 3.1.11
+ PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.1.11], [
+ [HAVE_GNUTLS=1]
+ AC_DEFINE([HAVE_GNUTLS], [1], [Define to 1 if you have GnuTLS])
+ ], [ : ])
+ ])
+ ])
+ AM_CONDITIONAL([HAVE_GNUTLS], [test "x$GNUTLS_LIBS" != "x"])
+ AS_IF([test "x$enable_gnutls" = "xyes"], [
+ AS_IF([test x"$GNUTLS_LIBS" = "x"], [
+ AC_MSG_ERROR([GnuTLS requested but libraries were not found])
+ ])
+ ])
+])
--- /dev/null
+AC_DEFUN([DNSDIST_CHECK_LIBSSL], [
+ AC_MSG_CHECKING([whether we will be linking in OpenSSL libssl])
+ HAVE_LIBSSL=0
+ AC_ARG_ENABLE([libssl],
+ AS_HELP_STRING([--enable-libssl],[use OpenSSL libssl @<:@default=auto@:>@]),
+ [enable_libssl=$enableval],
+ [enable_libssl=auto],
+ )
+ AC_MSG_RESULT([$enable_libssl])
+
+ AS_IF([test "x$enable_libssl" != "xno"], [
+ AS_IF([test "x$enable_libssl" = "xyes" -o "x$enable_libssl" = "xauto"], [
+ PKG_CHECK_MODULES([LIBSSL], [libssl], [
+ [HAVE_LIBSSL=1]
+ AC_DEFINE([HAVE_LIBSSL], [1], [Define to 1 if you have OpenSSL libssl])
+ ], [ : ])
+ ])
+ ])
+ AM_CONDITIONAL([HAVE_LIBSSL], [test "x$LIBSSL_LIBS" != "x"])
+ AS_IF([test "x$enable_libssl" = "xyes"], [
+ AS_IF([test x"$LIBSSL_LIBS" = "x"], [
+ AC_MSG_ERROR([OpenSSL libssl requested but libraries were not found])
+ ])
+ ])
+])
--- /dev/null
+AC_DEFUN([DNSDIST_ENABLE_DNS_OVER_TLS], [
+ AC_MSG_CHECKING([whether to enable DNS over TLS support])
+ AC_ARG_ENABLE([dns-over-tls],
+ AS_HELP_STRING([--enable-dns-over-tls], [enable DNS over TLS support (requires GnuTLS or OpenSSL) @<:@default=no@:>@]),
+ [enable_dns_over_tls=$enableval],
+ [enable_dns_over_tls=no]
+ )
+ AC_MSG_RESULT([$enable_dns_over_tls])
+ AM_CONDITIONAL([HAVE_DNS_OVER_TLS], [test "x$enable_dns_over_tls" != "xno"])
+
+ AM_COND_IF([HAVE_DNS_OVER_TLS], [
+ AC_DEFINE([HAVE_DNS_OVER_TLS], [1], [Define to 1 if you enable DNS over TLS support])
+ ])
+])
--- /dev/null
+../../../m4/pdns_check_libcrypto.m4
\ No newline at end of file
--- /dev/null
+#include <fstream>
+
+#include "config.h"
+#include "dolog.hh"
+#include "iputils.hh"
+#include "lock.hh"
+#include "tcpiohandler.hh"
+
+#ifdef HAVE_LIBSODIUM
+#include <sodium.h>
+#endif /* HAVE_LIBSODIUM */
+
+#ifdef HAVE_DNS_OVER_TLS
+#ifdef HAVE_LIBSSL
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include <boost/circular_buffer.hpp>
+
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER)
+/* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */
+static pthread_mutex_t *openssllocks{nullptr};
+
+extern "C" {
+static void openssl_pthreads_locking_callback(int mode, int type, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK) {
+ pthread_mutex_lock(&(openssllocks[type]));
+
+ } else {
+ pthread_mutex_unlock(&(openssllocks[type]));
+ }
+}
+
+static unsigned long openssl_pthreads_id_callback()
+{
+ return (unsigned long)pthread_self();
+}
+}
+
+static void openssl_thread_setup()
+{
+ openssllocks = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
+
+ for (int i = 0; i < CRYPTO_num_locks(); i++)
+ pthread_mutex_init(&(openssllocks[i]), NULL);
+
+ CRYPTO_set_id_callback(openssl_pthreads_id_callback);
+ CRYPTO_set_locking_callback(openssl_pthreads_locking_callback);
+}
+
+static void openssl_thread_cleanup()
+{
+ CRYPTO_set_locking_callback(NULL);
+
+ for (int i=0; i<CRYPTO_num_locks(); i++) {
+ pthread_mutex_destroy(&(openssllocks[i]));
+ }
+
+ OPENSSL_free(openssllocks);
+}
+
+#else
+static void openssl_thread_setup()
+{
+}
+
+static void openssl_thread_cleanup()
+{
+}
+#endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) */
+
+/* From rfc5077 Section 4. Recommended Ticket Construction */
+#define TLS_TICKETS_KEY_NAME_SIZE (16)
+
+/* AES-256 */
+#define TLS_TICKETS_CIPHER_KEY_SIZE (32)
+#define TLS_TICKETS_CIPHER_ALGO (EVP_aes_256_cbc)
+
+/* HMAC SHA-256 */
+#define TLS_TICKETS_MAC_KEY_SIZE (32)
+#define TLS_TICKETS_MAC_ALGO (EVP_sha256)
+
+static int s_ticketsKeyIndex{-1};
+
+class OpenSSLTLSTicketKey
+{
+public:
+ OpenSSLTLSTicketKey()
+ {
+ if (RAND_bytes(d_name, sizeof(d_name)) != 1) {
+ throw std::runtime_error("Error while generating the name of the OpenSSL TLS ticket key");
+ }
+
+ if (RAND_bytes(d_cipherKey, sizeof(d_cipherKey)) != 1) {
+ throw std::runtime_error("Error while generating the cipher key of the OpenSSL TLS ticket key");
+ }
+
+ if (RAND_bytes(d_hmacKey, sizeof(d_hmacKey)) != 1) {
+ throw std::runtime_error("Error while generating the HMAC key of the OpenSSL TLS ticket key");
+ }
+#ifdef HAVE_LIBSODIUM
+ sodium_mlock(d_name, sizeof(d_name));
+ sodium_mlock(d_cipherKey, sizeof(d_cipherKey));
+ sodium_mlock(d_hmacKey, sizeof(d_hmacKey));
+#endif /* HAVE_LIBSODIUM */
+ }
+
+ OpenSSLTLSTicketKey(ifstream& file)
+ {
+ file.read(reinterpret_cast<char*>(d_name), sizeof(d_name));
+ file.read(reinterpret_cast<char*>(d_cipherKey), sizeof(d_cipherKey));
+ file.read(reinterpret_cast<char*>(d_hmacKey), sizeof(d_hmacKey));
+
+ if (file.fail()) {
+ throw std::runtime_error("Unable to load a ticket key from the OpenSSL tickets key file");
+ }
+#ifdef HAVE_LIBSODIUM
+ sodium_mlock(d_name, sizeof(d_name));
+ sodium_mlock(d_cipherKey, sizeof(d_cipherKey));
+ sodium_mlock(d_hmacKey, sizeof(d_hmacKey));
+#endif /* HAVE_LIBSODIUM */
+ }
+
+ ~OpenSSLTLSTicketKey()
+ {
+#ifdef HAVE_LIBSODIUM
+ sodium_munlock(d_name, sizeof(d_name));
+ sodium_munlock(d_cipherKey, sizeof(d_cipherKey));
+ sodium_munlock(d_hmacKey, sizeof(d_hmacKey));
+#else
+ OPENSSL_cleanse(d_name, sizeof(d_name));
+ OPENSSL_cleanse(d_cipherKey, sizeof(d_cipherKey));
+ OPENSSL_cleanse(d_hmacKey, sizeof(d_hmacKey));
+#endif /* HAVE_LIBSODIUM */
+ }
+
+ bool nameMatches(const unsigned char name[TLS_TICKETS_KEY_NAME_SIZE]) const
+ {
+ return (memcmp(d_name, name, sizeof(d_name)) == 0);
+ }
+
+ int encrypt(unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx) const
+ {
+ memcpy(keyName, d_name, sizeof(d_name));
+
+ if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) != 1) {
+ return -1;
+ }
+
+ if (EVP_EncryptInit_ex(ectx, TLS_TICKETS_CIPHER_ALGO(), nullptr, d_cipherKey, iv) != 1) {
+ return -1;
+ }
+
+ if (HMAC_Init_ex(hctx, d_hmacKey, sizeof(d_hmacKey), TLS_TICKETS_MAC_ALGO(), nullptr) != 1) {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ bool decrypt(const unsigned char* iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx) const
+ {
+ if (HMAC_Init_ex(hctx, d_hmacKey, sizeof(d_hmacKey), TLS_TICKETS_MAC_ALGO(), nullptr) != 1) {
+ return false;
+ }
+
+ if (EVP_DecryptInit_ex(ectx, TLS_TICKETS_CIPHER_ALGO(), nullptr, d_cipherKey, iv) != 1) {
+ return false;
+ }
+
+ return true;
+ }
+
+private:
+ unsigned char d_name[TLS_TICKETS_KEY_NAME_SIZE];
+ unsigned char d_cipherKey[TLS_TICKETS_CIPHER_KEY_SIZE];
+ unsigned char d_hmacKey[TLS_TICKETS_MAC_KEY_SIZE];
+};
+
+class OpenSSLTLSTicketKeysRing
+{
+public:
+ OpenSSLTLSTicketKeysRing(size_t capacity)
+ {
+ pthread_rwlock_init(&d_lock, nullptr);
+ d_ticketKeys.set_capacity(capacity);
+ }
+
+ ~OpenSSLTLSTicketKeysRing()
+ {
+ pthread_rwlock_destroy(&d_lock);
+ }
+
+ void addKey(std::shared_ptr<OpenSSLTLSTicketKey> newKey)
+ {
+ WriteLock wl(&d_lock);
+ d_ticketKeys.push_back(newKey);
+ }
+
+ std::shared_ptr<OpenSSLTLSTicketKey> getEncryptionKey()
+ {
+ ReadLock rl(&d_lock);
+ return d_ticketKeys.front();
+ }
+
+ std::shared_ptr<OpenSSLTLSTicketKey> getDecryptionKey(unsigned char name[TLS_TICKETS_KEY_NAME_SIZE], bool& activeKey)
+ {
+ ReadLock rl(&d_lock);
+ for (auto& key : d_ticketKeys) {
+ if (key->nameMatches(name)) {
+ activeKey = (key == d_ticketKeys.front());
+ return key;
+ }
+ }
+ return nullptr;
+ }
+
+ size_t getKeysCount()
+ {
+ ReadLock rl(&d_lock);
+ return d_ticketKeys.size();
+ }
+
+private:
+ boost::circular_buffer<std::shared_ptr<OpenSSLTLSTicketKey> > d_ticketKeys;
+ pthread_rwlock_t d_lock;
+};
+
+class OpenSSLTLSConnection: public TLSConnection
+{
+public:
+ OpenSSLTLSConnection(int socket, unsigned int timeout, SSL_CTX* tlsCtx)
+ {
+ d_socket = socket;
+ d_conn = SSL_new(tlsCtx);
+
+ if (!d_conn) {
+ vinfolog("Error creating TLS object");
+ if (g_verbose) {
+ ERR_print_errors_fp(stderr);
+ }
+ throw std::runtime_error("Error creating TLS object");
+ }
+
+ if (!SSL_set_fd(d_conn, d_socket)) {
+ throw std::runtime_error("Error assigning socket");
+ }
+
+ int res = 0;
+ do {
+ res = SSL_accept(d_conn);
+ if (res < 0) {
+ handleIORequest(res, timeout);
+ }
+ }
+ while (res < 0);
+
+ if (res != 1) {
+ throw std::runtime_error("Error accepting TLS connection");
+ }
+ }
+
+ virtual ~OpenSSLTLSConnection() override
+ {
+ if (d_conn) {
+ SSL_free(d_conn);
+ }
+ }
+
+ void handleIORequest(int res, unsigned int timeout)
+ {
+ int error = SSL_get_error(d_conn, res);
+ if (error == SSL_ERROR_WANT_READ) {
+ res = waitForData(d_socket, timeout);
+ if (res <= 0) {
+ throw std::runtime_error("Error reading from TLS connection");
+ }
+ }
+ else if (error == SSL_ERROR_WANT_WRITE) {
+ res = waitForRWData(d_socket, false, timeout, 0);
+ if (res <= 0) {
+ throw std::runtime_error("Error waiting to write to TLS connection");
+ }
+ }
+ else {
+ throw std::runtime_error("Error writing to TLS connection");
+ }
+ }
+
+ size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout) override
+ {
+ size_t got = 0;
+ time_t start = 0;
+ unsigned int remainingTime = totalTimeout;
+ if (totalTimeout) {
+ start = time(nullptr);
+ }
+
+ do {
+ int res = SSL_read(d_conn, (reinterpret_cast<char *>(buffer) + got), static_cast<int>(bufferSize - got));
+ if (res == 0) {
+ throw std::runtime_error("Error reading from TLS connection");
+ }
+ else if (res < 0) {
+ handleIORequest(res, readTimeout);
+ }
+ else {
+ got += (size_t) res;
+ }
+
+ if (totalTimeout) {
+ time_t now = time(nullptr);
+ unsigned int elapsed = now - start;
+ if (now < start || elapsed >= remainingTime) {
+ throw runtime_error("Timeout while reading data");
+ }
+ start = now;
+ remainingTime -= elapsed;
+ }
+ }
+ while (got < bufferSize);
+
+ return got;
+ }
+
+ size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) override
+ {
+ size_t got = 0;
+ do {
+ int res = SSL_write(d_conn, (reinterpret_cast<const char *>(buffer) + got), static_cast<int>(bufferSize - got));
+ if (res == 0) {
+ throw std::runtime_error("Error writing to TLS connection");
+ }
+ else if (res < 0) {
+ handleIORequest(res, writeTimeout);
+ }
+ else {
+ got += (size_t) res;
+ }
+ }
+ while (got < bufferSize);
+
+ return got;
+ }
+ void close() override
+ {
+ if (d_conn) {
+ SSL_shutdown(d_conn);
+ }
+ }
+
+private:
+ SSL* d_conn{nullptr};
+};
+
+class OpenSSLTLSIOCtx: public TLSCtx
+{
+public:
+ OpenSSLTLSIOCtx(const TLSFrontend& fe): d_ticketKeys(fe.d_numberOfTicketsKeys)
+ {
+ d_ticketsKeyRotationDelay = fe.d_ticketsKeyRotationDelay;
+
+ static const int sslOptions =
+ SSL_OP_NO_SSLv2 |
+ SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_COMPRESSION |
+ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_SINGLE_ECDH_USE |
+ SSL_OP_CIPHER_SERVER_PREFERENCE;
+
+ if (s_users.fetch_add(1) == 0) {
+ ERR_load_crypto_strings();
+ OpenSSL_add_ssl_algorithms();
+ openssl_thread_setup();
+
+ s_ticketsKeyIndex = SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
+
+ if (s_ticketsKeyIndex == -1) {
+ throw std::runtime_error("Error getting an index for tickets key");
+ }
+ }
+
+ d_tlsCtx = SSL_CTX_new(SSLv23_server_method());
+ if (!d_tlsCtx) {
+ ERR_print_errors_fp(stderr);
+ throw std::runtime_error("Error creating TLS context on " + fe.d_addr.toStringWithPort());
+ }
+
+ /* use the internal built-in cache to store sessions */
+ SSL_CTX_set_session_cache_mode(d_tlsCtx, SSL_SESS_CACHE_SERVER);
+ /* use our own ticket keys handler so we can rotate them */
+ SSL_CTX_set_tlsext_ticket_key_cb(d_tlsCtx, &OpenSSLTLSIOCtx::ticketKeyCb);
+ SSL_CTX_set_ex_data(d_tlsCtx, s_ticketsKeyIndex, this);
+ SSL_CTX_set_options(d_tlsCtx, sslOptions);
+#if defined(SSL_CTX_set_ecdh_auto)
+ SSL_CTX_set_ecdh_auto(d_tlsCtx, 1);
+#endif
+ SSL_CTX_use_certificate_chain_file(d_tlsCtx, fe.d_certFile.c_str());
+ SSL_CTX_use_PrivateKey_file(d_tlsCtx, fe.d_keyFile.c_str(), SSL_FILETYPE_PEM);
+
+ if (!fe.d_ciphers.empty()) {
+ SSL_CTX_set_cipher_list(d_tlsCtx, fe.d_ciphers.c_str());
+ }
+
+ try {
+ if (fe.d_ticketKeyFile.empty()) {
+ handleTicketsKeyRotation(time(nullptr));
+ }
+ else {
+ loadTicketsKeys(fe.d_ticketKeyFile);
+ }
+ }
+ catch (const std::exception& e) {
+ SSL_CTX_free(d_tlsCtx);
+ throw;
+ }
+ }
+
+ virtual ~OpenSSLTLSIOCtx() override
+ {
+ if (d_tlsCtx) {
+ SSL_CTX_free(d_tlsCtx);
+ }
+
+ if (s_users.fetch_sub(1) == 1) {
+ ERR_free_strings();
+
+ EVP_cleanup();
+
+ CONF_modules_finish();
+ CONF_modules_free();
+ CONF_modules_unload(1);
+
+ CRYPTO_cleanup_all_ex_data();
+ openssl_thread_cleanup();
+ }
+ }
+
+ static int ticketKeyCb(SSL *s, unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc)
+ {
+ SSL_CTX* sslCtx = SSL_get_SSL_CTX(s);
+ if (sslCtx == nullptr) {
+ return -1;
+ }
+
+ OpenSSLTLSIOCtx* ctx = reinterpret_cast<OpenSSLTLSIOCtx*>(SSL_CTX_get_ex_data(sslCtx, s_ticketsKeyIndex));
+ if (ctx == nullptr) {
+ return -1;
+ }
+
+ if (enc) {
+ const auto key = ctx->d_ticketKeys.getEncryptionKey();
+ if (key == nullptr) {
+ return -1;
+ }
+
+ return key->encrypt(keyName, iv, ectx, hctx);
+ }
+
+ bool activeEncryptionKey = false;
+
+ const auto key = ctx->d_ticketKeys.getDecryptionKey(keyName, activeEncryptionKey);
+ if (key == nullptr) {
+ /* we don't know this key, just create a new ticket */
+ return 0;
+ }
+
+ if (key->decrypt(iv, ectx, hctx) == false) {
+ return -1;
+ }
+
+ if (!activeEncryptionKey) {
+ /* this key is not active, please encrypt the ticket content with the currently active one */
+ return 2;
+ }
+
+ return 1;
+ }
+
+ std::unique_ptr<TLSConnection> getConnection(int socket, unsigned int timeout, time_t now) override
+ {
+ handleTicketsKeyRotation(now);
+
+ return std::unique_ptr<OpenSSLTLSConnection>(new OpenSSLTLSConnection(socket, timeout, d_tlsCtx));
+ }
+
+ void rotateTicketsKey(time_t now) override
+ {
+ auto newKey = std::make_shared<OpenSSLTLSTicketKey>();
+ d_ticketKeys.addKey(newKey);
+
+ if (d_ticketsKeyRotationDelay > 0) {
+ d_ticketsKeyNextRotation = time(nullptr) + d_ticketsKeyRotationDelay;
+ }
+ }
+
+ void loadTicketsKeys(const std::string& keyFile) override
+ {
+ bool keyLoaded = false;
+ ifstream file(keyFile);
+ try {
+ do {
+ auto newKey = std::make_shared<OpenSSLTLSTicketKey>(file);
+ d_ticketKeys.addKey(newKey);
+ keyLoaded = true;
+ }
+ while (!file.fail());
+ }
+ catch (const std::exception& e) {
+ /* if we haven't been able to load at least one key, fail */
+ if (!keyLoaded) {
+ throw;
+ }
+ }
+
+ if (d_ticketsKeyRotationDelay > 0) {
+ d_ticketsKeyNextRotation = time(nullptr) + d_ticketsKeyRotationDelay;
+ }
+
+ file.close();
+ }
+
+ size_t getTicketsKeysCount() override
+ {
+ return d_ticketKeys.getKeysCount();
+ }
+
+private:
+ OpenSSLTLSTicketKeysRing d_ticketKeys;
+ SSL_CTX* d_tlsCtx{nullptr};
+ static std::atomic<uint64_t> s_users;
+};
+
+std::atomic<uint64_t> OpenSSLTLSIOCtx::s_users(0);
+
+#endif /* HAVE_LIBSSL */
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+class GnuTLSTicketsKey
+{
+public:
+ GnuTLSTicketsKey()
+ {
+ if (gnutls_session_ticket_key_generate(&d_key) != GNUTLS_E_SUCCESS) {
+ throw std::runtime_error("Error generating tickets key for TLS context");
+ }
+
+#ifdef HAVE_LIBSODIUM
+ sodium_mlock(d_key.data, d_key.size);
+#endif /* HAVE_LIBSODIUM */
+ }
+
+ GnuTLSTicketsKey(const std::string& keyFile)
+ {
+ /* to be sure we are loading the correct amount of data, which
+ may change between versions, let's generate a correct key first */
+ if (gnutls_session_ticket_key_generate(&d_key) != GNUTLS_E_SUCCESS) {
+ throw std::runtime_error("Error generating tickets key (before parsing key file) for TLS context");
+ }
+
+#ifdef HAVE_LIBSODIUM
+ sodium_mlock(d_key.data, d_key.size);
+#endif /* HAVE_LIBSODIUM */
+
+ try {
+ ifstream file(keyFile);
+ file.read(reinterpret_cast<char*>(d_key.data), d_key.size);
+
+ if (file.fail()) {
+ file.close();
+ throw std::runtime_error("Invalid GnuTLS tickets key file " + keyFile);
+ }
+
+ file.close();
+ }
+ catch (const std::exception& e) {
+#ifdef HAVE_LIBSODIUM
+ sodium_munlock(d_key.data, d_key.size);
+#endif /* HAVE_LIBSODIUM */
+ gnutls_free(d_key.data);
+ throw;
+ }
+ }
+
+ ~GnuTLSTicketsKey()
+ {
+ if (d_key.data != nullptr && d_key.size > 0) {
+#ifdef HAVE_LIBSODIUM
+ sodium_munlock(d_key.data, d_key.size);
+#else
+ gnutls_memset(d_key.data, 0, d_key.size);
+#endif /* HAVE_LIBSODIUM */
+ }
+ gnutls_free(d_key.data);
+ }
+ const gnutls_datum_t& getKey() const
+ {
+ return d_key;
+ }
+
+private:
+ gnutls_datum_t d_key{nullptr, 0};
+};
+
+class GnuTLSConnection: public TLSConnection
+{
+public:
+
+ GnuTLSConnection(int socket, unsigned int timeout, const gnutls_certificate_credentials_t creds, const gnutls_priority_t priorityCache, std::shared_ptr<GnuTLSTicketsKey> ticketsKey): d_ticketsKey(ticketsKey)
+ {
+ d_socket = socket;
+
+ if (gnutls_init(&d_conn, GNUTLS_SERVER
+#ifdef GNUTLS_NO_SIGNAL
+ | GNUTLS_NO_SIGNAL
+#endif
+ ) != GNUTLS_E_SUCCESS) {
+ throw std::runtime_error("Error creating TLS connection");
+ }
+
+ if (gnutls_credentials_set(d_conn, GNUTLS_CRD_CERTIFICATE, creds) != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(d_conn);
+ throw std::runtime_error("Error setting certificate and key to TLS connection");
+ }
+
+ if (gnutls_priority_set(d_conn, priorityCache) != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(d_conn);
+ throw std::runtime_error("Error setting ciphers to TLS connection");
+ }
+
+ if (d_ticketsKey) {
+ const gnutls_datum_t& key = d_ticketsKey->getKey();
+ if (gnutls_session_ticket_enable_server(d_conn, &key) != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(d_conn);
+ throw std::runtime_error("Error setting the tickets key to TLS connection");
+ }
+ }
+
+ gnutls_transport_set_int(d_conn, d_socket);
+
+ /* timeouts are in milliseconds */
+ gnutls_handshake_set_timeout(d_conn, timeout * 1000);
+ gnutls_record_set_timeout(d_conn, timeout * 1000);
+
+ int ret = 0;
+ do {
+ ret = gnutls_handshake(d_conn);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ }
+
+ virtual ~GnuTLSConnection() override
+ {
+ if (d_conn) {
+ gnutls_deinit(d_conn);
+ }
+ }
+
+ size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout) override
+ {
+ size_t got = 0;
+ time_t start = 0;
+ unsigned int remainingTime = totalTimeout;
+ if (totalTimeout) {
+ start = time(nullptr);
+ }
+
+ do {
+ ssize_t res = gnutls_record_recv(d_conn, (reinterpret_cast<char *>(buffer) + got), bufferSize - got);
+ if (res == 0) {
+ throw std::runtime_error("Error reading from TLS connection");
+ }
+ else if (res > 0) {
+ got += (size_t) res;
+ }
+ else if (res < 0) {
+ if (gnutls_error_is_fatal(res)) {
+ throw std::runtime_error("Error reading from TLS connection");
+ }
+ warnlog("Warning, non-fatal error while reading from TLS connection: %s", gnutls_strerror(res));
+ }
+
+ if (totalTimeout) {
+ time_t now = time(nullptr);
+ unsigned int elapsed = now - start;
+ if (now < start || elapsed >= remainingTime) {
+ throw runtime_error("Timeout while reading data");
+ }
+ start = now;
+ remainingTime -= elapsed;
+ }
+ }
+ while (got < bufferSize);
+
+ return got;
+ }
+
+ size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) override
+ {
+ size_t got = 0;
+
+ do {
+ ssize_t res = gnutls_record_send(d_conn, (reinterpret_cast<const char *>(buffer) + got), bufferSize - got);
+ if (res == 0) {
+ throw std::runtime_error("Error writing to TLS connection");
+ }
+ else if (res > 0) {
+ got += (size_t) res;
+ }
+ else if (res < 0) {
+ if (gnutls_error_is_fatal(res)) {
+ throw std::runtime_error("Error writing to TLS connection");
+ }
+ warnlog("Warning, non-fatal error while writing to TLS connection: %s", gnutls_strerror(res));
+ }
+ }
+ while (got < bufferSize);
+
+ return got;
+ }
+
+ void close() override
+ {
+ if (d_conn) {
+ gnutls_bye(d_conn, GNUTLS_SHUT_WR);
+ }
+ }
+
+private:
+ gnutls_session_t d_conn{nullptr};
+ std::shared_ptr<GnuTLSTicketsKey> d_ticketsKey;
+};
+
+class GnuTLSIOCtx: public TLSCtx
+{
+public:
+ GnuTLSIOCtx(const TLSFrontend& fe)
+ {
+ int rc = 0;
+ d_ticketsKeyRotationDelay = fe.d_ticketsKeyRotationDelay;
+
+ rc = gnutls_certificate_allocate_credentials(&d_creds);
+ if (rc != GNUTLS_E_SUCCESS) {
+ throw std::runtime_error("Error allocating credentials for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
+ }
+
+ rc = gnutls_certificate_set_x509_key_file(d_creds, fe.d_certFile.c_str(), fe.d_keyFile.c_str(), GNUTLS_X509_FMT_PEM);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_certificate_free_credentials(d_creds);
+ throw std::runtime_error("Error loading certificate and key for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
+ }
+
+#if GNUTLS_VERSION_NUMBER >= 0x030600
+ rc = gnutls_certificate_set_known_dh_params(d_creds, GNUTLS_SEC_PARAM_HIGH);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_certificate_free_credentials(d_creds);
+ throw std::runtime_error("Error setting DH params for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
+ }
+#endif
+
+ rc = gnutls_priority_init(&d_priorityCache, fe.d_ciphers.empty() ? "NORMAL" : fe.d_ciphers.c_str(), nullptr);
+ if (rc != GNUTLS_E_SUCCESS) {
+ warnlog("Error setting up TLS cipher preferences to %s (%s), skipping.", fe.d_ciphers.c_str(), gnutls_strerror(rc));
+ }
+
+ try {
+ if (fe.d_ticketKeyFile.empty()) {
+ handleTicketsKeyRotation(time(nullptr));
+ }
+ else {
+ loadTicketsKeys(fe.d_ticketKeyFile);
+ }
+ }
+ catch(const std::runtime_error& e) {
+ gnutls_certificate_free_credentials(d_creds);
+ throw std::runtime_error("Error generating tickets key for TLS context on " + fe.d_addr.toStringWithPort() + ": " + e.what());
+ }
+ }
+
+ virtual ~GnuTLSIOCtx() override
+ {
+ if (d_creds) {
+ gnutls_certificate_free_credentials(d_creds);
+ }
+ if (d_priorityCache) {
+ gnutls_priority_deinit(d_priorityCache);
+ }
+ }
+
+ std::unique_ptr<TLSConnection> getConnection(int socket, unsigned int timeout, time_t now) override
+ {
+ handleTicketsKeyRotation(now);
+
+ return std::unique_ptr<GnuTLSConnection>(new GnuTLSConnection(socket, timeout, d_creds, d_priorityCache, d_ticketsKey));
+ }
+
+ void rotateTicketsKey(time_t now) override
+ {
+ auto newKey = std::make_shared<GnuTLSTicketsKey>();
+ d_ticketsKey = newKey;
+ if (d_ticketsKeyRotationDelay > 0) {
+ d_ticketsKeyNextRotation = time(nullptr) + d_ticketsKeyRotationDelay;
+ }
+ }
+
+ void loadTicketsKeys(const std::string& file) override
+ {
+ auto newKey = std::make_shared<GnuTLSTicketsKey>(file);
+ d_ticketsKey = newKey;
+ if (d_ticketsKeyRotationDelay > 0) {
+ d_ticketsKeyNextRotation = time(nullptr) + d_ticketsKeyRotationDelay;
+ }
+ }
+
+ size_t getTicketsKeysCount() override
+ {
+ return d_ticketsKey != nullptr ? 1 : 0;
+ }
+
+private:
+ gnutls_certificate_credentials_t d_creds{nullptr};
+ gnutls_priority_t d_priorityCache{nullptr};
+ std::shared_ptr<GnuTLSTicketsKey> d_ticketsKey{nullptr};
+};
+
+#endif /* HAVE_GNUTLS */
+
+#endif /* HAVE_DNS_OVER_TLS */
+
+bool TLSFrontend::setupTLS()
+{
+#ifdef HAVE_DNS_OVER_TLS
+ /* get the "best" available provider */
+ if (!d_provider.empty()) {
+#ifdef HAVE_GNUTLS
+ if (d_provider == "gnutls") {
+ d_ctx = std::make_shared<GnuTLSIOCtx>(*this);
+ return true;
+ }
+#endif /* HAVE_GNUTLS */
+#ifdef HAVE_LIBSSL
+ if (d_provider == "openssl") {
+ d_ctx = std::make_shared<OpenSSLTLSIOCtx>(*this);
+ return true;
+ }
+#endif /* HAVE_LIBSSL */
+ }
+#ifdef HAVE_GNUTLS
+ d_ctx = std::make_shared<GnuTLSIOCtx>(*this);
+#else /* HAVE_GNUTLS */
+#ifdef HAVE_LIBSSL
+ d_ctx = std::make_shared<OpenSSLTLSIOCtx>(*this);
+#endif /* HAVE_LIBSSL */
+#endif /* HAVE_GNUTLS */
+
+#endif /* HAVE_DNS_OVER_TLS */
+ return true;
+}
--- /dev/null
+
+#pragma once
+#include <memory>
+
+#include "misc.hh"
+
+class TLSConnection
+{
+public:
+ virtual ~TLSConnection() { }
+ virtual size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout=0) = 0;
+ virtual size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) = 0;
+ virtual void close() = 0;
+
+protected:
+ int d_socket{-1};
+};
+
+class TLSCtx
+{
+public:
+ TLSCtx()
+ {
+ d_rotatingTicketsKey.clear();
+ }
+ virtual ~TLSCtx() {}
+ virtual std::unique_ptr<TLSConnection> getConnection(int socket, unsigned int timeout, time_t now) = 0;
+ virtual void rotateTicketsKey(time_t now) = 0;
+ virtual void loadTicketsKeys(const std::string& file)
+ {
+ throw std::runtime_error("This TLS backend does not have the capability to load a tickets key from a file");
+ }
+
+ void handleTicketsKeyRotation(time_t now)
+ {
+ if (d_ticketsKeyRotationDelay != 0 && now > d_ticketsKeyNextRotation) {
+ if (d_rotatingTicketsKey.test_and_set()) {
+ /* someone is already rotating */
+ return;
+ }
+ try {
+ rotateTicketsKey(now);
+ d_rotatingTicketsKey.clear();
+ }
+ catch(const std::runtime_error& e) {
+ d_rotatingTicketsKey.clear();
+ throw std::runtime_error("Error generating a new tickets key for TLS context");
+ }
+ }
+ }
+
+ time_t getNextTicketsKeyRotation() const
+ {
+ return d_ticketsKeyNextRotation;
+ }
+
+ virtual size_t getTicketsKeysCount() = 0;
+
+protected:
+ std::atomic_flag d_rotatingTicketsKey;
+ time_t d_ticketsKeyRotationDelay{0};
+ time_t d_ticketsKeyNextRotation{0};
+};
+
+class TLSFrontend
+{
+public:
+ bool setupTLS();
+
+ void rotateTicketsKey(time_t now)
+ {
+ if (d_ctx != nullptr) {
+ d_ctx->rotateTicketsKey(now);
+ }
+ }
+
+ void loadTicketsKeys(const std::string& file)
+ {
+ if (d_ctx != nullptr) {
+ d_ctx->loadTicketsKeys(file);
+ }
+ }
+
+ std::shared_ptr<TLSCtx> getContext()
+ {
+ return d_ctx;
+ }
+
+ void cleanup()
+ {
+ d_ctx.reset();
+ }
+
+ size_t getTicketsKeysCount()
+ {
+ if (d_ctx != nullptr) {
+ return d_ctx->getTicketsKeysCount();
+ }
+
+ return 0;
+ }
+
+ static std::string timeToString(time_t rotationTime)
+ {
+ char buf[20];
+ struct tm date_tm;
+
+ localtime_r(&rotationTime, &date_tm);
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &date_tm);
+
+ return std::string(buf);
+ }
+
+ time_t getTicketsKeyRotationDelay() const
+ {
+ return d_ticketsKeyRotationDelay;
+ }
+
+ std::string getNextTicketsKeyRotation() const
+ {
+ std::string res;
+
+ if (d_ctx != nullptr) {
+ res = timeToString(d_ctx->getNextTicketsKeyRotation());
+ }
+
+ return res;
+ }
+
+ std::set<int> d_cpus;
+ ComboAddress d_addr;
+ std::string d_certFile;
+ std::string d_keyFile;
+ std::string d_ciphers;
+ std::string d_provider;
+ std::string d_interface;
+ std::string d_ticketKeyFile;
+
+ time_t d_ticketsKeyRotationDelay{43200};
+ int d_tcpFastOpenQueueSize{0};
+ uint8_t d_numberOfTicketsKeys{5};
+ bool d_reusePort{false};
+
+private:
+ std::shared_ptr<TLSCtx> d_ctx{nullptr};
+};
+
+class TCPIOHandler
+{
+public:
+ TCPIOHandler(int socket, unsigned int timeout, std::shared_ptr<TLSCtx> ctx, time_t now): d_socket(socket)
+ {
+ if (ctx) {
+ d_conn = ctx->getConnection(d_socket, timeout, now);
+ }
+ }
+ ~TCPIOHandler()
+ {
+ if (d_conn) {
+ d_conn->close();
+ }
+ else if (d_socket != -1) {
+ shutdown(d_socket, SHUT_RDWR);
+ }
+ }
+ size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout=0)
+ {
+ if (d_conn) {
+ return d_conn->read(buffer, bufferSize, readTimeout, totalTimeout);
+ } else {
+ return readn2WithTimeout(d_socket, buffer, bufferSize, readTimeout, totalTimeout);
+ }
+ }
+ size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout)
+ {
+ if (d_conn) {
+ return d_conn->write(buffer, bufferSize, writeTimeout);
+ }
+ else {
+ return writen2WithTimeout(d_socket, buffer, bufferSize, writeTimeout);
+ }
+ }
+
+ bool writeSizeAndMsg(const void* buffer, size_t bufferSize, unsigned int writeTimeout)
+ {
+ if (d_conn) {
+ uint16_t size = htons(bufferSize);
+ if (d_conn->write(&size, sizeof(size), writeTimeout) != sizeof(size)) {
+ return false;
+ }
+ return (d_conn->write(buffer, bufferSize, writeTimeout) == bufferSize);
+ }
+ else {
+ return sendSizeAndMsgWithTimeout(d_socket, bufferSize, static_cast<const char*>(buffer), writeTimeout, nullptr, nullptr, 0, 0, 0);
+ }
+ }
+
+private:
+ std::unique_ptr<TLSConnection> d_conn{nullptr};
+ int d_socket{-1};
+};
--- /dev/null
+../xpf.cc
\ No newline at end of file
--- /dev/null
+../xpf.hh
\ No newline at end of file
ret+="\\.";
else if(p=='\\')
ret+="\\\\";
- else if(p > 0x21 && p < 0x7e)
+ else if(p > 0x20 && p < 0x7f)
ret.append(1, (char)p);
else {
ret+="\\" + (boost::format("%03d") % (unsigned int)p).str();
#include "dnsseckeeper.hh"
#include "dns.hh"
#include "dnsbackend.hh"
+#include "ednsoptions.hh"
#include "pdnsexception.hh"
#include "dnspacket.hh"
#include "logger.hh"
qdomainwild=orig.qdomainwild;
qdomainzone=orig.qdomainzone;
d_maxreplylen = orig.d_maxreplylen;
- d_ednsping = orig.d_ednsping;
d_wantsnsid = orig.d_wantsnsid;
d_anyLocal = orig.d_anyLocal;
d_eso = orig.d_eso;
bool DNSPacket::couldBeCached()
{
- return d_ednsping.empty() && !d_wantsnsid && qclass==QClass::IN && !d_havetsig;
+ return !d_wantsnsid && qclass==QClass::IN && !d_havetsig;
}
unsigned int DNSPacket::getMinTTL()
}
}
- if(!d_ednsping.empty()) {
- opts.push_back(make_pair(4, d_ednsping));
- }
-
-
if(!d_rrs.empty() || !opts.empty() || d_haveednssubnet || d_haveednssection) {
try {
uint8_t maxScopeMask=0;
r->qtype = qtype;
r->qclass = qclass;
r->d_maxreplylen = d_maxreplylen;
- r->d_ednsping = d_ednsping;
r->d_wantsnsid = d_wantsnsid;
r->d_dnssecOk = d_dnssecOk;
r->d_eso = d_eso;
return -1;
}
d_wantsnsid=false;
- d_ednsping.clear();
d_maxreplylen=512;
memcpy((void *)&d,(const void *)d_rawpacket.c_str(),12);
return 0;
d_wantsnsid=false;
d_dnssecOk=false;
- d_ednsping.clear();
d_havetsig = mdp.getTSIGPos();
d_haveednssubnet = false;
d_haveednssection = false;
for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin();
iter != edo.d_options.end();
++iter) {
- if(iter->first == 3) {// 'EDNS NSID'
- d_wantsnsid=1;
- }
- else if(iter->first == 5) {// 'EDNS PING'
- d_ednsping = iter->second;
+ if(iter->first == EDNSOptionCode::NSID) {
+ d_wantsnsid=true;
}
- else if(s_doEDNSSubnetProcessing && (iter->first == 8)) { // 'EDNS SUBNET'
+ else if(s_doEDNSSubnetProcessing && (iter->first == EDNSOptionCode::ECS)) { // 'EDNS SUBNET'
if(getEDNSSubnetOptsFromString(iter->second, &d_eso)) {
//cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl;
d_haveednssubnet=true;
vector<DNSZoneRecord> d_rrs; // 8
string d_rawpacket; // this is where everything lives 8
- string d_ednsping;
EDNSSubnetOpts d_eso;
int d_maxreplylen;
struct dnsrecordheader ah;
vector<unsigned char> record;
+ bool seenTSIG = false;
validPacket=true;
d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
dr.d_place=DNSResourceRecord::AUTHORITY;
else
dr.d_place=DNSResourceRecord::ADDITIONAL;
-
+
unsigned int recordStartPos=pr.d_pos;
DNSName name=pr.getName();
-
+
pr.getDnsrecordheader(ah);
dr.d_ttl=ah.d_ttl;
dr.d_type=ah.d_type;
dr.d_class=ah.d_class;
-
+
dr.d_name=name;
dr.d_clen=ah.d_clen;
d_answers.push_back(make_pair(dr, pr.d_pos));
+ /* XXX: XPF records should be allowed after TSIG as soon as the actual XPF option code has been assigned:
+ if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG && dr.d_type != QType::XPF)
+ */
+ if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
+ /* only XPF records are allowed after a TSIG */
+ throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has an unexpected record ("+std::to_string(dr.d_type)+") after a TSIG one.");
+ }
+
if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
- if(dr.d_place != DNSResourceRecord::ADDITIONAL || n != (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount) - 1) {
- throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
+ if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) {
+ throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
}
+ seenTSIG = true;
d_tsigPos = recordStartPos + sizeof(struct dnsheader);
}
}
-#if 0
+#if 0
if(pr.d_pos!=contentlen) {
throw MOADNSException("Packet ("+d_qname+"|#"+std::to_string(d_qtype)+") has trailing garbage ("+ std::to_string(pr.d_pos) + " < " +
std::to_string(contentlen) + ")");
}
-#endif
+#endif
}
catch(std::out_of_range &re) {
if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
#include "dnswriter.hh"
#include "dnsname.hh"
#include "pdnsexception.hh"
+#include "iputils.hh"
/** DNS records have three representations:
1) in the packet
xfrBlob(val, 16);
}
+ void xfrCAWithoutPort(uint8_t version, ComboAddress &val) {
+ string blob;
+ if (version == 4) xfrBlob(blob, 4);
+ else if (version == 6) xfrBlob(blob, 16);
+ else throw runtime_error("invalid IP protocol");
+ val = makeComboAddressFromRaw(version, blob);
+ }
+
+ void xfrCAPort(ComboAddress &val) {
+ uint16_t port;
+ xfr16BitInt(port);
+ val.sin4.sin_port = port;
+ }
+
void xfrTime(uint32_t& val)
{
xfr32BitInt(val);
uint16_t d_pos;
bool eof() { return true; };
+ const string getRemaining() const {
+ return "";
+ };
private:
uint16_t d_startrecordpos; // needed for getBlob later on
vector<string> addresses;
stringtok(addresses, remote, " ,\t");
- ComboAddress remaddr(addresses[0], 53);
-
- if((d_sock=socket(remaddr.sin4.sin_family, SOCK_DGRAM,0))<0)
+ d_remote = ComboAddress(addresses[0], 53);
+
+ if((d_sock=socket(d_remote.sin4.sin_family, SOCK_DGRAM,0))<0) {
throw PDNSException(string("socket: ")+strerror(errno));
-
+ }
+
ComboAddress local;
- if(remaddr.sin4.sin_family==AF_INET)
+ if(d_remote.sin4.sin_family==AF_INET) {
local = ComboAddress("0.0.0.0");
- else
+ }
+ else {
local = ComboAddress("::");
+ }
- int n=0;
+ unsigned int n=0;
for(;n<10;n++) {
local.sin4.sin_port = htons(10000+dns_random(50000));
throw PDNSException(string("binding dnsproxy socket: ")+strerror(errno));
}
- if(connect(d_sock, (sockaddr *)&remaddr, remaddr.getSocklen())<0)
- throw PDNSException("Unable to UDP connect to remote nameserver "+remaddr.toStringWithPort()+": "+stringerror());
+ if(connect(d_sock, (sockaddr *)&d_remote, d_remote.getSocklen())<0) {
+ throw PDNSException("Unable to UDP connect to remote nameserver "+d_remote.toStringWithPort()+": "+stringerror());
+ }
d_xor=dns_random(0xffff);
- L<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<remaddr.toStringWithPort()<<endl;
+ L<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<d_remote.toStringWithPort()<<endl;
}
void DNSProxy::go()
struct msghdr msgh;
struct iovec iov;
char cbuf[256];
+ ComboAddress fromaddr;
for(;;) {
- len=recv(d_sock, buffer, sizeof(buffer),0); // answer from our backend
+ socklen_t fromaddrSize = sizeof(fromaddr);
+ len=recvfrom(d_sock, buffer, sizeof(buffer),0, (struct sockaddr*) &fromaddr, &fromaddrSize); // answer from our backend
if(len<(ssize_t)sizeof(dnsheader)) {
if(len<0)
L<<Logger::Error<<"Error receiving packet from recursor backend: "<<stringerror()<<endl;
continue;
}
+ if (fromaddr != d_remote) {
+ L<<Logger::Error<<"Got answer from unexpected host "<<fromaddr.toStringWithPort()<<" instead of our recursor backend "<<d_remote.toStringWithPort()<<endl;
+ continue;
+ }
(*d_resanswers)++;
(*d_udpanswers)++;
dnsheader d;
typedef map<int,ConntrackEntry> map_t;
// Data
+ ComboAddress d_remote;
AtomicCounter* d_resanswers;
AtomicCounter* d_udpanswers;
AtomicCounter* d_resquestions;
}
+
ComboAddress ARecordContent::getCA(int port) const
{
ComboAddress ret;
d_st=st;
}
-boilerplate_conv(SOA, QType::SOA,
+boilerplate_conv(SOA, QType::SOA,
conv.xfrName(d_mname, true);
conv.xfrName(d_rname, true);
conv.xfr32BitInt(d_st.serial);
#include <set>
#include <bitset>
#include "namespaces.hh"
+#include "iputils.hh"
#define includeboilerplate(RNAME) RNAME##RecordContent(const DNSRecord& dr, PacketReader& pr); \
RNAME##RecordContent(const string& zoneData); \
void RNAME##RecordContent::xfrPacket(Convertor& conv, bool noDot) \
{ \
CONV; \
- if (conv.eof() == false) throw MOADNSException("All data was not consumed"); \
+ if (conv.eof() == false) throw MOADNSException("When parsing " #RNAME " trailing data was not parsed: '" + conv.getRemaining() + "'"); \
} \
struct EDNSOpts
pruneQids();
}
-
-bool g_rdSelector;
-
static void generateOptRR(const std::string& optRData, string& res)
{
const uint8_t name = 0;
}
}
+static bool g_rdSelector;
+static uint16_t g_pcapDnsPort;
-bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stamp)
+static bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stamp)
{
dnsheader* dh=(dnsheader*)pr.d_payload;
bool sent=false;
- if((ntohs(pr.d_udp->uh_dport)!=53 && ntohs(pr.d_udp->uh_sport)!=53) || dh->rd != g_rdSelector || (unsigned int)pr.d_len <= sizeof(dnsheader))
+ if((ntohs(pr.d_udp->uh_dport)!=g_pcapDnsPort && ntohs(pr.d_udp->uh_sport)!=g_pcapDnsPort) || dh->rd != g_rdSelector || (unsigned int)pr.d_len <= sizeof(dnsheader))
return sent;
QuestionData qd;
("help,h", "produce help message")
("version", "show version number")
("packet-limit", po::value<uint32_t>()->default_value(0), "stop after this many packets")
+ ("pcap-dns-port", po::value<uint16_t>()->default_value(53), "look at packets from or to this port in the PCAP (defaults to 53)")
("quiet", po::value<bool>()->default_value(true), "don't be too noisy")
("recursive", po::value<bool>()->default_value(true), "look at recursion desired packets, or not (defaults true)")
("speedup", po::value<float>()->default_value(1), "replay at this speedup")
uint32_t packetLimit = g_vm["packet-limit"].as<uint32_t>();
g_rdSelector = g_vm["recursive"].as<bool>();
+ g_pcapDnsPort = g_vm["pcap-dns-port"].as<uint16_t>();
g_quiet = g_vm["quiet"].as<bool>();
{
TSIGHashEnum algo;
if (!getTSIGHashEnum(trc.d_algoName, algo)) {
- throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc.d_algoName.toString());
+ throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc.d_algoName.toLogString());
}
string toSign = makeTSIGPayload(tsigprevious, reinterpret_cast<const char*>(pw.getContent().data()), pw.getContent().size(), tsigkeyname, trc, timersonly);
if (algo == TSIG_GSS) {
if (!gss_add_signature(tsigkeyname, toSign, trc.d_mac)) {
- throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname.toString()+string("'"));
+ throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname.toLogString()+string("'"));
}
} else {
trc.d_mac = calculateHMAC(tsigsecret, toSign, algo);
TSIGHashEnum algo;
if (!getTSIGHashEnum(trc.d_algoName, algo)) {
- throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc.d_algoName.toString());
+ throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc.d_algoName.toLogString());
}
TSIGHashEnum expectedAlgo;
if (!getTSIGHashEnum(tt.algo, expectedAlgo)) {
- throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt.algo.toString());
+ throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt.algo.toLogString());
}
if (algo != expectedAlgo) {
- throw std::runtime_error("Signature with TSIG key '"+tt.name.toString()+"' does not match the expected algorithm (" + tt.algo.toString() + " / " + trc.d_algoName.toString() + ")");
+ throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' does not match the expected algorithm (" + tt.algo.toLogString() + " / " + trc.d_algoName.toLogString() + ")");
}
string tsigMsg;
if (algo == TSIG_GSS) {
GssContext gssctx(tt.name);
if (!gss_verify_signature(tt.name, tsigMsg, theirMAC)) {
- throw std::runtime_error("Signature with TSIG key '"+tt.name.toString()+"' failed to validate");
+ throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate");
}
} else {
string ourMac = calculateHMAC(tt.secret, tsigMsg, algo);
if(!constantTimeStringEquals(ourMac, theirMAC)) {
- throw std::runtime_error("Signature with TSIG key '"+tt.name.toString()+"' failed to validate");
+ throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate");
}
}
class DNSPacket;
uint32_t localtime_format_YYYYMMDDSS(time_t t, uint32_t seq);
// for SOA-EDIT
-uint32_t calculateEditSOA(const DNSZoneRecord& rr, const string& kind);
-uint32_t calculateEditSOA(const SOAData& sd, const string& kind);
-bool editSOA(DNSSECKeeper& dk, const DNSName& qname, DNSPacket* dp);
-bool editSOARecord(DNSZoneRecord& rr, const string& kind);
+uint32_t calculateEditSOA(uint32_t old_serial, DNSSECKeeper& dk, const DNSName& zonename);
+uint32_t calculateEditSOA(uint32_t old_serial, const string& kind, const DNSName& zonename);
// for SOA-EDIT-DNSUPDATE/API
-uint32_t calculateIncreaseSOA(SOAData sd, const string& increaseKind, const string& editKind);
-bool increaseSOARecord(DNSResourceRecord& rr, const string& increaseKind, const string& editKind);
-bool increaseSOARecord(DNSZoneRecord& rr, const string& increaseKind, const string& editKind);
+bool increaseSOARecord(DNSResourceRecord& dr, const string& increaseKind, const string& editKind);
+bool makeIncreasedSOARecord(SOAData& sd, const string& increaseKind, const string& editKind, DNSResourceRecord& rrout);
#include "dns.hh"
#include "dnsname.hh"
#include "namespaces.hh"
+#include "iputils.hh"
#include <arpa/inet.h>
{
xfrBlob(val,16);
}
+
+ void xfrCAWithoutPort(uint8_t version, 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(ComboAddress &val)
+ {
+ uint16_t port;
+ port = val.sin4.sin_port;
+ xfr16BitInt(port);
+ }
+
void xfrTime(const uint32_t& val)
{
xfr32BitInt(val);
}
bool eof() { return true; } // we don't know how long the record should be
+ const string getRemaining() const {
+ return "";
+ }
private:
uint16_t lookupName(const DNSName& name, uint16_t* matchlen);
vector<uint16_t> d_namepositions;
struct EDNSOptionCode
{
- enum EDNSOptionCodeEnum {NSID=3, DAU=4, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13};
+ enum EDNSOptionCodeEnum {NSID=3, DAU=5, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13, KEYTAG=14};
};
/* extract a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
{
memset(destination, 0, sizeof(*destination));
+#ifdef __NetBSD__
+ struct cmsghdr* cmsg;
+#else
const struct cmsghdr* cmsg;
+#endif
for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
#if defined(IP_PKTINFO)
if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
#include <sys/endian.h>
#endif
+#if defined(__NetBSD__) && defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR)
+// The IP_PKTINFO option in NetBSD was incompatible with Linux until a
+// change that also introduced IP_SENDSRCADDR for FreeBSD compatibility.
+#undef IP_PKTINFO
+#endif
+
union ComboAddress {
struct sockaddr_in sin4;
struct sockaddr_in6 sin6;
sin4.sin_family=AF_INET;
sin4.sin_addr.s_addr=0;
sin4.sin_port=0;
+ sin6.sin6_scope_id = 0;
+ sin6.sin6_flowinfo = 0;
}
ComboAddress(const struct sockaddr *sa, socklen_t salen) {
string toString() const
{
char host[1024];
- if(sin4.sin_family && !getnameinfo((struct sockaddr*) this, getSocklen(), host, sizeof(host),0, 0, NI_NUMERICHOST))
+ int retval = 0;
+ if(sin4.sin_family && !(retval = getnameinfo((struct sockaddr*) this, getSocklen(), host, sizeof(host),0, 0, NI_NUMERICHOST)))
return host;
else
- return "invalid";
+ return "invalid "+string(gai_strerror(retval));
}
string toStringWithPort() const
return address;
}
+inline ComboAddress makeComboAddressFromRaw(uint8_t version, const char* raw, size_t len)
+{
+ ComboAddress address;
+
+ if (version == 4) {
+ address.sin4.sin_family = AF_INET;
+ if (len != sizeof(address.sin4.sin_addr)) throw NetmaskException("invalid raw address length");
+ memcpy(&address.sin4.sin_addr, raw, sizeof(address.sin4.sin_addr));
+ }
+ else if (version == 6) {
+ address.sin6.sin6_family = AF_INET6;
+ if (len != sizeof(address.sin6.sin6_addr)) throw NetmaskException("invalid raw address length");
+ memcpy(&address.sin6.sin6_addr, raw, sizeof(address.sin6.sin6_addr));
+ }
+ else throw NetmaskException("invalid address family");
+
+ return address;
+}
+
+inline ComboAddress makeComboAddressFromRaw(uint8_t version, const string &str)
+{
+ return makeComboAddressFromRaw(version, str.c_str(), str.size());
+}
+
/** This class represents a netmask and can be queried to see if a certain
IP address is matched by this mask */
class Netmask
#elif defined(IP_RECVDSTADDR)
#define GEN_IP_PKTINFO IP_RECVDSTADDR
#endif
+
bool IsAnyAddress(const ComboAddress& addr);
bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination);
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv);
auto sr = getRR<SOARecordContent>(records[pos]);
if (!sr) {
- throw std::runtime_error("Error getting the content of the first SOA record of this IXFR sequence for zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"'");
+ throw std::runtime_error("Error getting the content of the first SOA record of this IXFR sequence for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
}
// cerr<<"Serial is "<<sr->d_st.serial<<", final serial is "<<masterSOA->d_st.serial<<endl;
}
if (pos >= records.size()) {
- throw std::runtime_error("No SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+ throw std::runtime_error("No SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
}
sr = getRR<SOARecordContent>(records[pos]);
if (!sr) {
- throw std::runtime_error("Invalid SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+ throw std::runtime_error("Invalid SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
}
// this is the serial of the zone after the removals
}
if (pos >= records.size()) {
- throw std::runtime_error("No SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+ throw std::runtime_error("No SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
}
sr = getRR<SOARecordContent>(records[pos]);
if (!sr) {
- throw std::runtime_error("Invalid SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+ throw std::runtime_error("Invalid SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
}
if (sr->d_st.serial != newSerial) {
- throw std::runtime_error("Invalid serial (" + std::to_string(sr->d_st.serial) + ", expecting " + std::to_string(newSerial) + ") in the SOA record finishing the additions part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+ throw std::runtime_error("Invalid serial (" + std::to_string(sr->d_st.serial) + ", expecting " + std::to_string(newSerial) + ") in the SOA record finishing the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
}
if (newSerial == masterSOA->d_st.serial) {
// this was the last sequence
if (pos != (records.size() - 1)) {
- throw std::runtime_error("Trailing records after the last IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+ throw std::runtime_error("Trailing records after the last IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
}
}
try {
trc.d_algoName = getTSIGAlgoName(the);
} catch(PDNSException& pe) {
- throw std::runtime_error("TSIG algorithm '"+tt.algo.toString()+"' is unknown.");
+ throw std::runtime_error("TSIG algorithm '"+tt.algo.toLogString()+"' is unknown.");
}
trc.d_time = time((time_t*)NULL);
trc.d_fudge = 300;
break;
if (maxReceivedBytes > 0 && (maxReceivedBytes - receivedBytes) < (size_t) len)
- throw std::runtime_error("Reached the maximum number of received bytes in an IXFR delta for zone '"+zone.toString()+"' from master "+master.toStringWithPort());
+ throw std::runtime_error("Reached the maximum number of received bytes in an IXFR delta for zone '"+zone.toLogString()+"' from master "+master.toStringWithPort());
char reply[len];
readn2(s.getHandle(), reply, len);
MOADNSParser mdp(false, string(reply, len));
if(mdp.d_header.rcode)
- throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode));
+ throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode));
// cout<<"Got a response, rcode: "<<mdp.d_header.rcode<<", got "<<mdp.d_answers.size()<<" answers"<<endl;
if(!masterSOA) {
// we have not seen the first SOA record yet
if (r.first.d_type != QType::SOA) {
- throw std::runtime_error("The first record of the IXFR answer for zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"' is not a SOA ("+QType(r.first.d_type).getName()+")");
+ throw std::runtime_error("The first record of the IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"' is not a SOA ("+QType(r.first.d_type).getName()+")");
}
auto sr = getRR<SOARecordContent>(r.first);
if (!sr) {
- throw std::runtime_error("Error getting the content of the first SOA record of the IXFR answer for zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"'");
+ throw std::runtime_error("Error getting the content of the first SOA record of the IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
}
if(sr->d_st.serial == std::dynamic_pointer_cast<SOARecordContent>(oursr.d_content)->d_st.serial) {
if(r.first.d_type == QType::OPT)
continue;
- throw std::runtime_error("Unexpected record (" +QType(r.first.d_type).getName()+") in non-answer section ("+std::to_string(r.first.d_place)+")in IXFR response for zone '"+zone.toString()+"' from master '"+master.toStringWithPort());
+ throw std::runtime_error("Unexpected record (" +QType(r.first.d_type).getName()+") in non-answer section ("+std::to_string(r.first.d_place)+")in IXFR response for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort());
}
r.first.d_name.makeUsRelative(zone);
#include <unistd.h>
#include "misc.hh"
#include <sys/types.h>
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
#include <sys/event.h>
#endif
#include <sys/time.h>
#ifdef __FreeBSD__
# include <pthread_np.h>
#endif
+#ifdef __NetBSD__
+# include <pthread.h>
+# include <sched.h>
+#endif
bool g_singleThreaded;
pkt->ipi6_ifindex = itfIndex;
}
else {
-#ifdef IP_PKTINFO
+#if defined(IP_PKTINFO)
struct in_pktinfo *pkt;
msgh->msg_control = cmsgbuf;
memset(pkt, 0, sizeof(*pkt));
pkt->ipi_spec_dst = source->sin4.sin_addr;
pkt->ipi_ifindex = itfIndex;
-#endif
-#ifdef IP_SENDSRCADDR
+#elif defined(IP_SENDSRCADDR)
struct in_addr *in;
msgh->msg_control = cmsgbuf;
int mapThreadToCPUList(pthread_t tid, const std::set<int>& cpus)
{
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
-# ifdef __FreeBSD__
-# define cpu_set_t cpuset_t
-# endif
+# ifdef __NetBSD__
+ cpuset_t *cpuset;
+ cpuset = cpuset_create();
+ for (const auto cpuID : cpus) {
+ cpuset_set(cpuID, cpuset);
+ }
+
+ return pthread_setaffinity_np(tid,
+ cpuset_size(cpuset),
+ cpuset);
+# else
+# ifdef __FreeBSD__
+# define cpu_set_t cpuset_t
+# endif
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for (const auto cpuID : cpus) {
return pthread_setaffinity_np(tid,
sizeof(cpuset),
&cpuset);
+# endif
#endif /* HAVE_PTHREAD_SETAFFINITY_NP */
return ENOSYS;
}
This limit applies solely to the stack, the heap is not limited in any way. If threads need to allocate a lot of data,
the use of new/delete is suggested.
*/
- MTasker(size_t stacksize=8192) : d_tid(0), d_maxtid(0), d_stacksize(stacksize), d_waitstatus(Error)
+ MTasker(size_t stacksize=16*8192) : d_tid(0), d_maxtid(0), d_stacksize(stacksize), d_waitstatus(Error)
{
initMainStackBounds();
}
DNSZoneRecord rr;
SOAData sd;
- // string subdomain="";
- string soa;
int retargetcount=0;
set<DNSName> authSet;
if(p->qtype.getCode() == QType::SOA && sd.qname==p->qdomain) {
rr.dr.d_name=sd.qname;
rr.dr.d_type=QType::SOA;
+ sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname);
rr.dr.d_content=makeSOAContent(sd);
rr.dr.d_ttl=sd.ttl;
rr.domain_id=sd.domain_id;
/* Add in SOA if required */
if(target==sd.qname) {
+ rr.dr.d_name = sd.qname;
rr.dr.d_type = QType::SOA;
+ sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname);
rr.dr.d_content = makeSOAContent(sd);
- rr.dr.d_name = sd.qname;
rr.dr.d_ttl = sd.ttl;
rr.domain_id = sd.domain_id;
rr.auth = true;
return 0;
}
- editSOA(d_dk, sd.qname, r);
-
for(const auto& loopRR: r->getRRS()) {
if(loopRR.scopeMask) {
noCache=true;
#include "namespaces.hh"
+#include "xpf.hh"
+
typedef map<ComboAddress, uint32_t, ComboAddress::addressOnlyLessThan> tcpClientCounts_t;
static thread_local std::shared_ptr<RecursorLua4> t_pdl;
static AtomicCounter counter;
static std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
static std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
+static NetmaskGroup g_XPFAcl;
static size_t g_tcpMaxQueriesPerConn;
static uint64_t g_latencyStatSize;
static uint32_t g_disthashseed;
static unsigned int g_numWorkerThreads;
static int g_tcpTimeout;
static uint16_t g_udpTruncationThreshold;
+static uint16_t g_xpfRRCode{0};
static std::atomic<bool> statsWanted;
static std::atomic<bool> g_quiet;
static bool g_logCommonErrors;
bool g_logRPZChanges{false};
#define LOCAL_NETS "127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10"
+#define LOCAL_NETS_INVERSE "!127.0.0.0/8, !10.0.0.0/8, !100.64.0.0/10, !169.254.0.0/16, !192.168.0.0/16, !172.16.0.0/12, !::1/128, !fc00::/7, !fe80::/10"
// Bad Nets taken from both:
// http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
// and
DNSComboWriter(const char* data, uint16_t len, const struct timeval& now) : d_mdp(true, data, len), d_now(now),
d_tcp(false), d_socket(-1)
{}
- MOADNSParser d_mdp;
- void setRemote(const ComboAddress* sa)
+
+ void setRemote(const ComboAddress& sa)
{
- d_remote=*sa;
+ d_remote=sa;
+ }
+
+ void setSource(const ComboAddress& sa)
+ {
+ d_source=sa;
}
void setLocal(const ComboAddress& sa)
d_local=sa;
}
+ void setDestination(const ComboAddress& sa)
+ {
+ d_destination=sa;
+ }
void setSocket(int sock)
{
string getRemote() const
{
- return d_remote.toString();
+ if (d_source == d_remote) {
+ return d_source.toStringWithPort();
+ }
+ return d_source.toStringWithPort() + " (proxied by " + d_remote.toStringWithPort() + ")";
}
+ MOADNSParser d_mdp;
struct timeval d_now;
- ComboAddress d_remote, d_local;
+ /* Remote client, might differ from d_source
+ in case of XPF, in which case d_source holds
+ the IP of the client and d_remote of the proxy
+ */
+ ComboAddress d_remote;
+ ComboAddress d_source;
+ /* Destination address, might differ from
+ d_destination in case of XPF, in which case
+ d_destination holds the IP of the proxy and
+ d_local holds our own. */
+ ComboAddress d_local;
+ ComboAddress d_destination;
#ifdef HAVE_PROTOBUF
boost::uuids::uuid d_uuid;
string d_requestorId;
{
PacketID pident=*any_cast<PacketID>(&var);
char resp[512];
- ssize_t ret=recv(fd, resp, sizeof(resp), 0);
+ ComboAddress fromaddr;
+ socklen_t addrlen=sizeof(fromaddr);
+
+ ssize_t ret=recvfrom(fd, resp, sizeof(resp), 0, (sockaddr *)&fromaddr, &addrlen);
+ if (fromaddr != pident.remote) {
+ L<<Logger::Notice<<"Response received from the wrong remote host ("<<fromaddr.toStringWithPort()<<" instead of "<<pident.remote.toStringWithPort()<<"), discarding"<<endl;
+
+ }
+
t_fdm->removeReadFD(fd);
if(ret >= 0) {
string data(resp, (size_t) ret);
PacketID pident;
pident.sock=&s;
+ pident.remote=dest;
pident.type=0;
t_fdm->addReadFD(s.getHandle(), handleGenUDPQueryResponse, pident);
static string makeLoginfo(DNSComboWriter* dc)
try
{
- return "("+dc->d_mdp.d_qname.toLogString()+"/"+DNSRecordContent::NumberToType(dc->d_mdp.d_qtype)+" from "+(dc->d_remote.toString())+")";
+ return "("+dc->d_mdp.d_qname.toLogString()+"/"+DNSRecordContent::NumberToType(dc->d_mdp.d_qtype)+" from "+(dc->getRemote())+")";
}
catch(...)
{
RecProtoBufMessage pbMessage(RecProtoBufMessage::Response);
#ifdef HAVE_PROTOBUF
if (luaconfsLocal->protobufServer) {
- Netmask requestorNM(dc->d_remote, dc->d_remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
+ Netmask requestorNM(dc->d_source, dc->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
const ComboAddress& requestor = requestorNM.getMaskedNetwork();
- pbMessage.update(dc->d_uuid, &requestor, &dc->d_local, dc->d_tcp, dc->d_mdp.d_header.id);
+ pbMessage.update(dc->d_uuid, &requestor, &dc->d_destination, dc->d_tcp, dc->d_mdp.d_header.id);
pbMessage.setEDNSSubnet(dc->d_ednssubnet.source, dc->d_ednssubnet.source.isIpv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
pbMessage.setQuestion(dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass);
}
if(t_pdl) {
sr.setLuaEngine(t_pdl);
}
- sr.d_requestor=dc->d_remote; // ECS needs this too
if(g_dnssecmode != DNSSECMode::Off) {
sr.setDoDNSSEC(true);
sr.setInitialRequestId(dc->d_uuid);
#endif
- if (g_useIncomingECS) {
- sr.setIncomingECSFound(dc->d_ecsFound);
- if (dc->d_ecsFound) {
- sr.setIncomingECS(dc->d_ednssubnet);
- }
- }
+ sr.setQuerySource(dc->d_remote, g_useIncomingECS && !dc->d_ednssubnet.source.empty() ? boost::optional<const EDNSSubnetOpts&>(dc->d_ednssubnet) : boost::none);
bool tracedQuery=false; // we could consider letting Lua know about this too
bool variableAnswer = false;
int res = RCode::NoError;
DNSFilterEngine::Policy appliedPolicy;
DNSRecord spoofed;
- RecursorLua4::DNSQuestion dq(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_tcp, variableAnswer, wantsRPZ);
+ RecursorLua4::DNSQuestion dq(dc->d_source, dc->d_destination, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_tcp, variableAnswer, wantsRPZ);
dq.ednsFlags = &edo.d_Z;
dq.ednsOptions = &dc->d_ednsOpts;
dq.tag = dc->d_tag;
// Check if the query has a policy attached to it
if (wantsRPZ) {
- appliedPolicy = luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_remote, sr.d_discardedPolicies);
+ appliedPolicy = luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies);
}
// if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
if(!shouldNotValidate && sr.isDNSSECValidationRequested()) {
try {
if(sr.doLog()) {
- L<<Logger::Warning<<"Starting validation of answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<endl;
+ L<<Logger::Warning<<"Starting validation of answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->getRemote()<<endl;
}
auto state = sr.getValidationState();
if(state == Secure) {
if(sr.doLog()) {
- L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<" validates correctly"<<endl;
+ L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->getRemote()<<" validates correctly"<<endl;
}
// Is the query source interested in the value of the ad-bit?
}
else if(state == Insecure) {
if(sr.doLog()) {
- L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<" validates as Insecure"<<endl;
+ L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->getRemote()<<" validates as Insecure"<<endl;
}
pw.getHeader()->ad=0;
}
else if(state == Bogus) {
if(g_dnssecLogBogus || sr.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
- L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<" validates as Bogus"<<endl;
+ L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->getRemote()<<" validates as Bogus"<<endl;
}
// Does the query or validation mode sending out a SERVFAIL on validation errors?
if(ret.size()) {
orderAndShuffle(ret);
- if(auto sl = luaconfsLocal->sortlist.getOrderCmp(dc->d_remote)) {
+ if(auto sl = luaconfsLocal->sortlist.getOrderCmp(dc->d_source)) {
stable_sort(ret.begin(), ret.end(), *sl);
variableAnswer=true;
}
}
g_rs.submitResponse(dc->d_mdp.d_qtype, packet.size(), !dc->d_tcp);
- updateResponseStats(res, dc->d_remote, packet.size(), &dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
+ updateResponseStats(res, dc->d_source, packet.size(), &dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
#ifdef HAVE_PROTOBUF
if (luaconfsLocal->protobufServer && (!luaconfsLocal->protobufTaggedOnly || (appliedPolicy.d_name && !appliedPolicy.d_name->empty()) || !dc->d_policyTags.empty())) {
pbMessage.setBytes(packet.size());
addCMsgSrcAddr(&msgh, cbuf, &dc->d_local, 0);
}
if(sendmsg(dc->d_socket, &msgh, 0) < 0 && g_logCommonErrors)
- L<<Logger::Warning<<"Sending UDP reply to client "<<dc->d_remote.toStringWithPort()<<" failed with: "<<strerror(errno)<<endl;
+ L<<Logger::Warning<<"Sending UDP reply to client "<<dc->getRemote()<<" failed with: "<<strerror(errno)<<endl;
if(!SyncRes::s_nopacketcache && !variableAnswer && !sr.wasVariable() ) {
t_packetCache->insertResponsePacket(dc->d_tag, dc->d_qhash, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass,
string((const char*)&*packet.begin(), packet.size()),
}
}
-static bool getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass, EDNSSubnetOpts* ednssubnet, std::map<uint16_t, EDNSOptionView>* options)
+static void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
+ bool& foundECS, EDNSSubnetOpts* ednssubnet, std::map<uint16_t, EDNSOptionView>* options,
+ bool& foundXPF, ComboAddress* xpfSource, ComboAddress* xpfDest)
{
- bool found = false;
- const struct dnsheader* dh = (struct dnsheader*)question.c_str();
+ const bool lookForXPF = xpfSource != nullptr && g_xpfRRCode != 0;
+ const bool lookForECS = ednssubnet != nullptr;
+ const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(question.c_str());
size_t questionLen = question.length();
unsigned int consumed=0;
*dnsname=DNSName(question.c_str(), questionLen, sizeof(dnsheader), false, qtype, qclass, &consumed);
size_t pos= sizeof(dnsheader)+consumed+4;
- /* at least OPT root label (1), type (2), class (2) and ttl (4) + OPT RR rdlen (2)
- = 11 */
- if(ntohs(dh->arcount) == 1 && questionLen > pos + 11) { // this code can extract one (1) EDNS Subnet option
+ const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
+ const uint16_t arcount = ntohs(dh->arcount);
+
+ for (uint16_t arpos = 0; arpos < arcount && questionLen > (pos + headerSize) && ((lookForECS && !foundECS) || (lookForXPF && !foundXPF)); arpos++) {
+ if (question.at(pos) != 0) {
+ /* not an OPT or a XPF, bye. */
+ return;
+ }
+
+ pos += 1;
+ const dnsrecordheader* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos));
+ pos += sizeof(dnsrecordheader);
+
+ if (pos >= questionLen) {
+ return;
+ }
+
/* OPT root label (1) followed by type (2) */
- if(question.at(pos)==0 && question.at(pos+1)==0 && question.at(pos+2)==QType::OPT) {
+ if(lookForECS && ntohs(drh->d_type) == QType::OPT) {
if (!options) {
char* ecsStart = nullptr;
size_t ecsLen = 0;
- int res = getEDNSOption((char*)question.c_str()+pos+9, questionLen - pos - 9, EDNSOptionCode::ECS, &ecsStart, &ecsLen);
+ /* we need to pass the record len */
+ int res = getEDNSOption(const_cast<char*>(reinterpret_cast<const char*>(&question.at(pos - sizeof(drh->d_clen)))), questionLen - pos + sizeof(drh->d_clen), EDNSOptionCode::ECS, &ecsStart, &ecsLen);
if (res == 0 && ecsLen > 4) {
EDNSSubnetOpts eso;
if(getEDNSSubnetOptsFromString(ecsStart + 4, ecsLen - 4, &eso)) {
*ednssubnet=eso;
- found = true;
+ foundECS = true;
}
}
}
else {
- int res = getEDNSOptions((char*)question.c_str()+pos+9, questionLen - pos - 9, *options);
+ /* we need to pass the record len */
+ int res = getEDNSOptions(reinterpret_cast<const char*>(&question.at(pos -sizeof(drh->d_clen))), questionLen - pos + (sizeof(drh->d_clen)), *options);
if (res == 0) {
const auto& it = options->find(EDNSOptionCode::ECS);
if (it != options->end() && it->second.content != nullptr && it->second.size > 0) {
EDNSSubnetOpts eso;
if(getEDNSSubnetOptsFromString(it->second.content, it->second.size, &eso)) {
*ednssubnet=eso;
- found = true;
+ foundECS = true;
}
}
}
}
}
+ else if (lookForXPF && ntohs(drh->d_type) == g_xpfRRCode && ntohs(drh->d_class) == QClass::IN && drh->d_ttl == 0) {
+ if ((questionLen - pos) < ntohs(drh->d_clen)) {
+ return;
+ }
+
+ foundXPF = parseXPFPayload(reinterpret_cast<const char*>(&question.at(pos)), ntohs(drh->d_clen), *xpfSource, xpfDest);
+ }
+
+ pos += ntohs(drh->d_clen);
}
- return found;
}
static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
}
if(!bytes || bytes < 0) {
if(g_logCommonErrors)
- L<<Logger::Error<<"TCP client "<< conn->d_remote.toString() <<" disconnected after first byte"<<endl;
+ L<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected after first byte"<<endl;
t_fdm->removeReadFD(fd);
return;
}
else if(conn->state==TCPConnection::GETQUESTION) {
ssize_t bytes=recv(conn->getFD(), conn->data + conn->bytesread, conn->qlen - conn->bytesread, 0);
if(!bytes || bytes < 0 || bytes > std::numeric_limits<std::uint16_t>::max()) {
- L<<Logger::Error<<"TCP client "<< conn->d_remote.toString() <<" disconnected while reading question body"<<endl;
+ L<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected while reading question body"<<endl;
t_fdm->removeReadFD(fd);
return;
}
catch(MOADNSException &mde) {
g_stats.clientParseError++;
if(g_logCommonErrors)
- L<<Logger::Error<<"Unable to parse packet from TCP client "<< conn->d_remote.toString() <<endl;
+ L<<Logger::Error<<"Unable to parse packet from TCP client "<< conn->d_remote.toStringWithPort() <<endl;
return;
}
dc->d_tcpConnection = conn; // carry the torch
dc->setSocket(conn->getFD()); // this is the only time a copy is made of the actual fd
dc->d_tcp=true;
- dc->setRemote(&conn->d_remote);
+ dc->setRemote(conn->d_remote);
+ dc->setSource(conn->d_remote);
ComboAddress dest;
memset(&dest, 0, sizeof(dest));
dest.sin4.sin_family = conn->d_remote.sin4.sin_family;
socklen_t len = dest.getSocklen();
getsockname(conn->getFD(), (sockaddr*)&dest, &len); // if this fails, we're ok with it
dc->setLocal(dest);
+ dc->setDestination(dest);
DNSName qname;
uint16_t qtype=0;
uint16_t qclass=0;
bool needECS = false;
+ bool needXPF = g_XPFAcl.match(conn->d_remote);
string requestorId;
string deviceId;
#ifdef HAVE_PROTOBUF
}
#endif
- if(needECS || (t_pdl && t_pdl->d_gettag)) {
+ if(needECS || needXPF || (t_pdl && t_pdl->d_gettag)) {
try {
std::map<uint16_t, EDNSOptionView> ednsOptions;
+ bool xpfFound = false;
dc->d_ecsParsed = true;
- dc->d_ecsFound = getQNameAndSubnet(std::string(conn->data, conn->qlen), &qname, &qtype, &qclass, &dc->d_ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr);
+ dc->d_ecsFound = false;
+ getQNameAndSubnet(std::string(conn->data, conn->qlen), &qname, &qtype, &qclass,
+ dc->d_ecsFound, &dc->d_ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr,
+ xpfFound, needXPF ? &dc->d_source : nullptr, needXPF ? &dc->d_destination : nullptr);
if(t_pdl && t_pdl->d_gettag) {
try {
- dc->d_tag = t_pdl->gettag(conn->d_remote, dc->d_ednssubnet.source, dest, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId, deviceId);
+ dc->d_tag = t_pdl->gettag(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId, deviceId);
}
catch(std::exception& e) {
if(g_logCommonErrors)
const struct dnsheader* dh = (const struct dnsheader*) conn->data;
if (!luaconfsLocal->protobufTaggedOnly) {
- protobufLogQuery(luaconfsLocal->protobufServer, luaconfsLocal->protobufMaskV4, luaconfsLocal->protobufMaskV6, dc->d_uuid, conn->d_remote, dest, dc->d_ednssubnet.source, true, dh->id, conn->qlen, qname, qtype, qclass, dc->d_policyTags, dc->d_requestorId, dc->d_deviceId);
+ protobufLogQuery(luaconfsLocal->protobufServer, luaconfsLocal->protobufMaskV4, luaconfsLocal->protobufMaskV6, dc->d_uuid, dc->d_source, dc->d_destination, dc->d_ednssubnet.source, true, dh->id, conn->qlen, qname, qtype, qclass, dc->d_policyTags, dc->d_requestorId, dc->d_deviceId);
}
}
catch(std::exception& e) {
}
#endif
if(dc->d_mdp.d_header.qr) {
- delete dc;
g_stats.ignoredCount++;
- L<<Logger::Error<<"Ignoring answer from TCP client "<< conn->d_remote.toString() <<" on server socket!"<<endl;
+ L<<Logger::Error<<"Ignoring answer from TCP client "<< dc->getRemote() <<" on server socket!"<<endl;
+ delete dc;
return;
}
if(dc->d_mdp.d_header.opcode) {
- delete dc;
g_stats.ignoredCount++;
- L<<Logger::Error<<"Ignoring non-query opcode from TCP client "<< conn->d_remote.toString() <<" on server socket!"<<endl;
+ L<<Logger::Error<<"Ignoring non-query opcode from TCP client "<< dc->getRemote() <<" on server socket!"<<endl;
+ delete dc;
return;
}
else {
unsigned int ctag=0;
uint32_t qhash = 0;
bool needECS = false;
+ bool needXPF = g_XPFAcl.match(fromaddr);
std::vector<std::string> policyTags;
LuaContext::LuaObject data;
+ ComboAddress source = fromaddr;
+ ComboAddress destination = destaddr;
string requestorId;
string deviceId;
#ifdef HAVE_PROTOBUF
*/
#endif
- if(needECS || (t_pdl && t_pdl->d_gettag)) {
+ if(needECS || needXPF || (t_pdl && t_pdl->d_gettag)) {
try {
std::map<uint16_t, EDNSOptionView> ednsOptions;
- ecsFound = getQNameAndSubnet(question, &qname, &qtype, &qclass, &ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr);
+ bool xpfFound = false;
+
+ ecsFound = false;
+
+ getQNameAndSubnet(question, &qname, &qtype, &qclass,
+ ecsFound, &ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr,
+ xpfFound, needXPF ? &source : nullptr, needXPF ? &destination : nullptr);
+
qnameParsed = true;
ecsParsed = true;
if(t_pdl && t_pdl->d_gettag) {
try {
- ctag=t_pdl->gettag(fromaddr, ednssubnet.source, destaddr, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId);
+ ctag=t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId);
}
catch(std::exception& e) {
if(g_logCommonErrors)
#ifdef HAVE_PROTOBUF
if(luaconfsLocal->protobufServer) {
if (!luaconfsLocal->protobufTaggedOnly || !policyTags.empty()) {
- protobufLogQuery(luaconfsLocal->protobufServer, luaconfsLocal->protobufMaskV4, luaconfsLocal->protobufMaskV6, uniqueId, fromaddr, destaddr, ednssubnet.source, false, dh->id, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId);
+ protobufLogQuery(luaconfsLocal->protobufServer, luaconfsLocal->protobufMaskV4, luaconfsLocal->protobufMaskV6, uniqueId, source, destination, ednssubnet.source, false, dh->id, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId);
}
}
#endif /* HAVE_PROTOBUF */
if (cacheHit) {
#ifdef HAVE_PROTOBUF
if(luaconfsLocal->protobufServer && (!luaconfsLocal->protobufTaggedOnly || !pbMessage.getAppliedPolicy().empty() || !pbMessage.getPolicyTags().empty())) {
- Netmask requestorNM(fromaddr, fromaddr.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
+ Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
const ComboAddress& requestor = requestorNM.getMaskedNetwork();
- pbMessage.update(uniqueId, &requestor, &destaddr, false, dh->id);
+ pbMessage.update(uniqueId, &requestor, &destination, false, dh->id);
pbMessage.setEDNSSubnet(ednssubnet.source, ednssubnet.source.isIpv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec);
pbMessage.setRequestorId(requestorId);
}
#endif /* HAVE_PROTOBUF */
if(!g_quiet)
- L<<Logger::Notice<<t_id<< " question answered from packet cache tag="<<ctag<<" from "<<fromaddr.toString()<<endl;
+ L<<Logger::Notice<<t_id<< " question answered from packet cache tag="<<ctag<<" from "<<source.toStringWithPort()<<(source != fromaddr ? " (via "+fromaddr.toStringWithPort()+")" : "")<<endl;
g_stats.packetCacheHits++;
SyncRes::s_queries++;
addCMsgSrcAddr(&msgh, cbuf, &destaddr, 0);
}
if(sendmsg(fd, &msgh, 0) < 0 && g_logCommonErrors)
- L<<Logger::Warning<<"Sending UDP reply to client "<<fromaddr.toStringWithPort()<<" failed with: "<<strerror(errno)<<endl;
+ L<<Logger::Warning<<"Sending UDP reply to client "<<source.toStringWithPort()<<(source != fromaddr ? " (via "+fromaddr.toStringWithPort()+")" : "")<<" failed with: "<<strerror(errno)<<endl;
if(response.length() >= sizeof(struct dnsheader)) {
struct dnsheader tmpdh;
memcpy(&tmpdh, response.c_str(), sizeof(tmpdh));
- updateResponseStats(tmpdh.rcode, fromaddr, response.length(), 0, 0);
+ updateResponseStats(tmpdh.rcode, source, response.length(), 0, 0);
}
g_stats.avgLatencyUsec=(1-1.0/g_latencyStatSize)*g_stats.avgLatencyUsec + 0.0; // we assume 0 usec
g_stats.avgLatencyOursUsec=(1-1.0/g_latencyStatSize)*g_stats.avgLatencyOursUsec + 0.0; // we assume 0 usec
}
if(t_pdl) {
- if(t_pdl->ipfilter(fromaddr, destaddr, *dh)) {
+ if(t_pdl->ipfilter(source, destination, *dh)) {
if(!g_quiet)
- L<<Logger::Notice<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] DROPPED question from "<<fromaddr.toStringWithPort()<<" based on policy"<<endl;
+ L<<Logger::Notice<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] DROPPED question from "<<source.toStringWithPort()<<(source != fromaddr ? " (via "+fromaddr.toStringWithPort()+")" : "")<<" based on policy"<<endl;
g_stats.policyDrops++;
return 0;
}
if(MT->numProcesses() > g_maxMThreads) {
if(!g_quiet)
- L<<Logger::Notice<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] DROPPED question from "<<fromaddr.toStringWithPort()<<", over capacity"<<endl;
+ L<<Logger::Notice<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] DROPPED question from "<<source.toStringWithPort()<<(source != fromaddr ? " (via "+fromaddr.toStringWithPort()+")" : "")<<", over capacity"<<endl;
g_stats.overCapacityDrops++;
return 0;
dc->d_tag=ctag;
dc->d_qhash=qhash;
dc->d_query = question;
- dc->setRemote(&fromaddr);
+ dc->setRemote(fromaddr);
+ dc->setSource(source);
dc->setLocal(destaddr);
+ dc->setDestination(destination);
dc->d_tcp=false;
dc->d_policyTags = policyTags;
dc->d_data = data;
}
#ifdef TCP_DEFER_ACCEPT
- if(setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &tmp, sizeof tmp) >= 0) {
+ if(setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &tmp, sizeof tmp) >= 0) {
if(i==locals.begin())
L<<Logger::Error<<"Enabled TCP data-ready filter for (slight) DoS protection"<<endl;
}
}
}
+ SyncRes::parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
+ SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
+ g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
+
+ g_XPFAcl.toMasks(::arg()["xpf-allow-from"]);
+ g_xpfRRCode = ::arg().asNum("xpf-rr-code");
+
g_networkTimeoutMsec = ::arg().asNum("network-timeout");
g_initialDomainMap = parseAuthAndForwards();
makeTCPServerSockets(0);
}
- SyncRes::parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
- g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
-
int forks;
for(forks = 0; forks < ::arg().asNum("processes") - 1; ++forks) {
if(!fork()) // we are child
for(expired_t::iterator i=expired.begin() ; i != expired.end(); ++i) {
shared_ptr<TCPConnection> conn=any_cast<shared_ptr<TCPConnection> >(i->second);
if(g_logCommonErrors)
- L<<Logger::Warning<<"Timeout from remote TCP client "<< conn->d_remote.toString() <<endl;
+ L<<Logger::Warning<<"Timeout from remote TCP client "<< conn->d_remote.toStringWithPort() <<endl;
t_fdm->removeReadFD(i->first);
}
}
::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet")="24";
::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet")="56";
::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for")="";
+ ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added")="0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE;
::arg().set("ecs-scope-zero-address", "Address to send to whitelisted authoritative servers for incoming queries with ECS prefix-length source of 0")="";
::arg().setSwitch( "use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information")="no";
::arg().setSwitch( "pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads")="yes";
::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level")="no";
+ ::arg().set("xpf-allow-from","XPF information is only processed from these subnets")="";
+ ::arg().set("xpf-rr-code","XPF option code to use")="0";
+
::arg().setCmd("help","Provide a helpful message");
::arg().setCmd("version","Print version string");
::arg().setCmd("config","Output blank configuration");
}
cleanSlashes(configname);
+ if(!::arg().getCommands().empty()) {
+ cerr<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl;
+ exit(99);
+ }
+
if(::arg().mustDo("config")) {
cout<<::arg().configstring()<<endl;
exit(0);
// Keep this line below all ::arg().set() statements
if (! ::arg().laxFile(configname.c_str()))
- cerr<<"Warning: unable to read configuration file '"<<configname<<"'."<<endl;
+ cerr<<"Warning: unable to read configuration file '"<<configname<<"': "<<strerror(errno)<<endl;
seedRandom(::arg()["entropy-source"]);
cerr<<"Serial increase of presigned zone '"<<zone<<"' is not allowed."<<endl;
return -1;
}
-
+
string soaEditKind;
dk.getSoaEdit(zone, soaEditKind);
- sd.db->lookup(QType(QType::SOA), zone);
- vector<DNSResourceRecord> rrs;
DNSResourceRecord rr;
- DNSZoneRecord szr;
- while (sd.db->get(rr)) {
- if (rr.qtype.getCode() == QType::SOA) {
- rrs.push_back(rr);
- szr.dr=DNSRecord(rr) ;
- }
- }
-
- if (rrs.size() > 1) {
- cerr<<rrs.size()<<" SOA records found for "<<zone<<"!"<<endl;
- return -1;
- }
- if (rrs.size() < 1) {
- cerr<<zone<<" not found!"<<endl;
- }
-
- if (soaEditKind.empty()) {
- sd.serial++;
- }
- else if(pdns_iequals(soaEditKind,"INCREMENT-WEEKS")) {
- sd.serial++;
- }
- else if(pdns_iequals(soaEditKind,"INCEPTION-INCREMENT")) {
- uint32_t today_serial = localtime_format_YYYYMMDDSS(time(NULL), 1);
-
- if (sd.serial < today_serial) {
- sd.serial = today_serial;
- }
- else {
- sd.serial++;
- }
- }
- else {
- sd.serial = calculateEditSOA(szr, soaEditKind) + 1;
- }
- rrs[0].content = serializeSOAData(sd);
+ makeIncreasedSOARecord(sd, "SOA-EDIT-INCREASE", soaEditKind, rr);
sd.db->startTransaction(zone, -1);
- if (! sd.db->replaceRRSet(sd.domain_id, zone, rr.qtype, rrs)) {
+ if (!sd.db->replaceRRSet(sd.domain_id, zone, rr.qtype, vector<DNSResourceRecord>(1, rr))) {
sd.db->abortTransaction();
cerr<<"Backend did not replace SOA record. Backend might not support this operation."<<endl;
return -1;
} else
ordername=zone;
if(g_verbose)
- cerr<<"'"<<rrs[0].qname<<"' -> '"<< ordername <<"'"<<endl;
- sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, rrs[0].qname, ordername, true);
+ cerr<<"'"<<rr.qname<<"' -> '"<< ordername <<"'"<<endl;
+ sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, rr.qname, ordername, true);
}
sd.db->commitTransaction();
).str();
SOAData sd;
fillSOAData(soa, sd); // fills out default values for us
- rr.content = DNSRecordContent::mastermake(rr.qtype.getCode(), 1, serializeSOAData(sd))->getZoneRepresentation(true);
+ rr.content = makeSOAContent(sd)->getZoneRepresentation(true);
rr.domain_id = di.id;
di.backend->startTransaction(zone, di.id);
di.backend->feedRecord(rr, DNSName());
#include "misc.hh"
#include "utility.hh"
#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/classification.hpp>
+
#include <iostream>
#include "base32.hh"
#include "base64.hh"
#include "namespaces.hh"
-RecordTextReader::RecordTextReader(const string& str, const string& zone) : d_string(str), d_zone(zone), d_pos(0), d_end(str.size())
+RecordTextReader::RecordTextReader(const string& str, const string& zone) : d_string(str), d_zone(zone), d_pos(0)
{
+ /* remove whitespace */
+ boost::trim_if(d_string, boost::algorithm::is_space());
+ d_end = d_string.size();
}
void RecordTextReader::xfr48BitInt(uint64_t &val)
d_pos += len;
}
+void RecordTextReader::xfrCAWithoutPort(uint8_t version, ComboAddress &val)
+{
+ if (version == 4) {
+ uint32_t ip;
+ xfrIP(ip);
+ val = makeComboAddressFromRaw(4, string((const char*) &ip, 4));
+ }
+ else if (version == 6) {
+ string ip;
+ xfrIP6(ip);
+ val = makeComboAddressFromRaw(6, ip);
+ }
+ else throw RecordTextException("invalid address family");
+}
+
+void RecordTextReader::xfrCAPort(ComboAddress &val)
+{
+ uint16_t port;
+ xfr16BitInt(port);
+ val.sin4.sin_port = port;
+}
+
bool RecordTextReader::eof()
{
return d_pos==d_end;
d_string += std::string(addrbuf);
}
+void RecordTextWriter::xfrCAWithoutPort(uint8_t version, ComboAddress &val)
+{
+ string ip = val.toString();
+
+ if(!d_string.empty())
+ d_string.append(1,' ');
+
+ d_string += ip;
+}
+
+void RecordTextWriter::xfrCAPort(ComboAddress &val)
+{
+ xfr16BitInt(val.sin4.sin_port);
+}
+
void RecordTextWriter::xfrTime(const uint32_t& val)
{
if(!d_string.empty())
#include "namespaces.hh"
#include "dnsname.hh"
+#include "iputils.hh"
class RecordTextException : public runtime_error
{
void xfrType(uint16_t& val);
void xfrIP(uint32_t& val);
void xfrIP6(std::string& val);
+ void xfrCAWithoutPort(uint8_t version, ComboAddress &val);
+ void xfrCAPort(ComboAddress &val);
void xfrTime(uint32_t& val);
void xfrName(DNSName& val, bool compress=false, bool noDot=false);
void xfrBlobNoSpaces(string& val, int len=-1);
void xfrBlob(string& val, int len=-1);
+ const string getRemaining() const {
+ return d_string.substr(d_pos);
+ }
+
bool eof();
private:
string d_string;
void xfr8BitInt(const uint8_t& val);
void xfrIP(const uint32_t& val);
void xfrIP6(const std::string& val);
+ void xfrCAWithoutPort(uint8_t version, ComboAddress &val);
+ void xfrCAPort(ComboAddress &val);
void xfrTime(const uint32_t& val);
void xfrBase32HexBlob(const string& val);
void xfrBlob(const string& val, int len=-1);
void xfrHexBlob(const string& val, bool keepReading=false);
bool eof() { return true; };
+
+ const string getRemaining() const {
+ return "";
+ }
private:
string& d_string;
bool d_nodot;
webserver.cc webserver.hh \
ws-api.cc ws-api.hh \
ws-recursor.cc ws-recursor.hh \
+ xpf.cc xpf.hh \
zoneparser-tng.cc zoneparser-tng.hh
if !HAVE_LUA_HPP
test-signers.cc \
test-syncres_cc.cc \
test-tsig.cc \
+ test-xpf_cc.cc \
testrunner.cc \
tsigverifier.cc tsigverifier.hh \
unix_utility.cc \
validate.cc validate.hh \
validate-recursor.cc validate-recursor.hh \
+ xpf.cc xpf.hh \
zoneparser-tng.cc zoneparser-tng.hh
testrunner_LDFLAGS = \
Changelogs for 4.1.x
====================
+.. changelog::
+ :version: 4.1.1
+ :released: 22nd of January 2018
+
+ This is the second release in the 4.1 train.
+
+ This release fixes PowerDNS Security Advisory :doc:`2018-01 <../security-advisories/powerdns-advisory-2018-01>`.
+
+ The full release notes can be read `on the blog <https://blog.powerdns.com/2018/01/22/powerdns-recursor-4-1-1/>`_.
+
+ This is a release on the stable branch, containing a fix for the
+ abovementioned security issue and several bug fixes from the
+ development branch.
+
+ .. change::
+ :tags: DNSSEC, Bug Fixes
+ :pullreq: 6215
+
+ Correctly handle ancestor delegation NSEC{,3} for children. Fixes
+ the DNSSEC validation issue found in Knot Resolver, where a NSEC{3}
+ ancestor delegation is wrongly use to prove the non-existence of a
+ RR below the delegation.
+ We already had the correct check for the exact owner name, but not
+ for RRs below the delegation.
+ (Security Advisory :doc:`2018-01 <../security-advisories/powerdns-advisory-2018-01>`)
+
+ .. change::
+ :tags: Internals, Bug Fixes
+ :pullreq: 6209
+ :tickets: 6212
+
+ Fix to make ``primeHints`` threadsafe, otherwise there's a small
+ chance on startup that the root-server IPs will be incorrect.
+
+ .. change::
+ :tags: Internals, Improvements
+ :pullreq: 6085
+ :tickets: 6198
+
+ Don't process records for another class than IN. We don't use
+ records of another class than IN, but we used to store some of them
+ in the cache which is useless. Just skip them.
+
+ .. change::
+ :tags: DNSSEC, Bug Fixes
+ :pullreq: 6092
+ :tickets: 6199
+
+ Fix the computation of the closest encloser for positive
+ answers. When the positive answer is expanded from a wildcard with
+ NSEC3, the closest encloser is not always parent of the qname,
+ depending on the number of labels in the initial wildcard.
+
+ .. change::
+ :tags: DNSSEC, Bug Fixes
+ :pullreq: 6095
+ :tickets: 6200
+
+ Pass the correct buffer size to ``arecvfrom()``. The incorrect size
+ could possibly cause DNSSEC failures.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 6137
+ :tickets: 6201
+
+ Don't validate signature for "glue" CNAME, since anything else than
+ the initial CNAME can't be considered authoritative.
+
.. changelog::
:version: 4.1.0
:released: 4th of December 2017
^^^^^^^^^^^^
An indication of the number of expected entries in the zone, speeding up the loading of huge zones by reserving space in advance.
-Extra Settings for rzpMaster
+Extra settings for rpzMaster
----------------------------
In addition to the settings above the settings for :func:`rpzMaster` may contain:
--- /dev/null
+PowerDNS Security Advisory 2018-01: Insufficient validation of DNSSEC signatures
+================================================================================
+
+- CVE: CVE-2018-1000003
+- Date: January 22nd 2018
+- Credit: CZ.NIC
+- Affects: PowerDNS Recursor 4.1.0
+- Not affected: PowerDNS Recursor < 4.1.0, 4.1.1
+- Severity: Low
+- Impact: Denial of existence spoofing
+- Exploit: This problem can be triggered by an attacker in position of
+ man-in-the-middle
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+
+An issue has been found in the DNSSEC validation component of PowerDNS Recursor,
+allowing an ancestor delegation NSEC or NSEC3 record to be used to wrongfully
+prove the non-existence of a RR below the owner name of that record. This would
+allow an attacker in position of man-in-the-middle to send a NXDOMAIN answer
+for a name that does exist. This issue has been assigned CVE-2018-1000003.
+
+PowerDNS Recursor 4.1.0 is affected.
+
+For those unable to upgrade to a new version, a minimal patch is
+`available <https://downloads.powerdns.com/patches/2018-01>`__
+
+We would like to thank CZ.NIC for finding and subsequently reporting this
+issue! Please also see https://lists.nic.cz/pipermail/knot-dns-users/2018-January/001309.html
Queries to addresses for zones as configured in any of the settings `forward-zones`_, `forward-zones-file`_ or `forward-zones-recurse`_ are performed regardless of these limitations.
+.. _setting-ecs-add-for:
+
+``ecs-add-for``
+--------------------------
+.. versionadded:: 4.2.0
+
+- Comma separated list of netmasks
+- Default: 0.0.0.0/0, ::, !127.0.0.0/8, !10.0.0.0/8, !100.64.0.0/10, !169.254.0.0/16, !192.168.0.0/16, !172.16.0.0/12, !::1/128, !fc00::/7, !fe80::/10
+
+List of requestor netmasks for which the requestor IP Address should be used as the :rfc:`EDNS Client Subnet <7871>` for outgoing queries. Outgoing queries for requestors that do not match this list will use the `ecs-scope-zero-address`_ instead.
+Valid incoming ECS values from `use-incoming-edns-subnet`_ are not replaced.
+
+Regardless of the value of this setting, ECS values are only sent for outgoing queries matching the conditions in the `edns-subnet-whitelist`_ setting. This setting only controls the actual value being sent.
+
+This defaults to not using the requestor address inside RFC1918 and similar "private" IP address spaces.
+
.. _setting-ecs-ipv4-bits:
``ecs-ipv4-bits``
- Default: (none)
List of netmasks and domains that :rfc:`EDNS Client Subnet <7871>` should be enabled for in outgoing queries.
-For example, an EDNS Client Subnet option containing the address of the initial requestor will be added to an outgoing query sent to server 192.0.2.1 for domain X if 192.0.2.1 matches one of the supplied netmasks, or if X matches one of the supplied domains.
-The initial requestor address will be truncated to 24 bits for IPv4 and to 56 bits for IPv6, as recommended in the privacy section of RFC 7871.
+
+For example, an EDNS Client Subnet option containing the address of the initial requestor (but see `ecs-add-for`_) will be added to an outgoing query sent to server 192.0.2.1 for domain X if 192.0.2.1 matches one of the supplied netmasks, or if X matches one of the supplied domains.
+The initial requestor address will be truncated to 24 bits for IPv4 (see `ecs-ipv4-bits`_) and to 56 bits for IPv6 (see `ecs-ipv6-bits`_), as recommended in the privacy section of RFC 7871.
+
By default, this option is empty, meaning no EDNS Client Subnet information is sent.
.. _setting-entropy-source:
- Default: yes
If a PID file should be written to `socket-dir`_
+
+.. _setting-xpf-allow-from:
+
+``xpf-allow-from``
+------------------
+.. versionadded:: 4.2.0
+
+- IP ranges, separated by commas
+- Default: empty
+
+.. note::
+ This is an experimental implementation of `draft-bellis-dnsop-xpf <https://datatracker.ietf.org/doc/draft-bellis-dnsop-xpf/>`_.
+
+The server will trust XPF records found in queries sent from those netmasks (both IPv4 and IPv6),
+and will adjust queries' source and destination accordingly. This is especially useful when the recursor
+is placed behind a proxy like `dnsdist <https://dnsdist.org>`_.
+Note that the ref:`setting-allow-from` setting is still applied to the original source address, and thus access restriction
+should be done on the proxy.
+
+.. _setting-xpf-rr-code:
+
+``xpf-rr-code``
+-------------------
+.. versionadded:: 4.2.0
+
+- Integer
+- Default: 0
+
+.. note::
+ This is an experimental implementation of `draft-bellis-dnsop-xpf <https://datatracker.ietf.org/doc/draft-bellis-dnsop-xpf/>`_.
+
+This option sets the resource record code to use for XPF records, as long as an official code has not been assigned to it.
+0 means that XPF is disabled.
Before upgrading, it is advised to read the :doc:`changelog/index`.
When upgrading several versions, please read **all** notes applying to the upgrade.
+4.1.x to 4.2.0 or master
+------------------------
+
+Two new settings have been added:
+
+- :ref:`setting-xpf-allow-from` can contain a list of IP addresses ranges from which `XPF (X-Proxied-For) <https://datatracker.ietf.org/doc/draft-bellis-dnsop-xpf/>`_ records will be trusted.
+- :ref:`setting-xpf-rr-code` should list the number of the XPF record to use (in lieu of an assigned code).
+
4.0.x to 4.1.0
--------------
#include "negcache.hh"
#include "misc.hh"
#include "cachecleaner.hh"
+#include "utility.hh"
/*!
* Set ne to the NegCacheEntry for the last label in qname and return true if there
*/
uint64_t NegCache::dumpToFile(FILE* fp) {
uint64_t ret(0);
- time_t now = time(0);
+ struct timeval now;
+ Utility::gettimeofday(&now, nullptr);
+
negcache_sequence_t& sidx = d_negcache.get<1>();
for(const NegCacheEntry& ne : sidx) {
ret++;
- fprintf(fp, "%s %d IN %s VIA %s ; (%s)\n", ne.d_name.toString().c_str(), (unsigned int) (ne.d_ttd - now), ne.d_qtype.getName().c_str(), ne.d_auth.toString().c_str(), vStates[ne.d_validationState]);
+ fprintf(fp, "%s %" PRId64 " IN %s VIA %s ; (%s)\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), ne.d_qtype.getName().c_str(), ne.d_auth.toString().c_str(), vStates[ne.d_validationState]);
for (const auto& rec : ne.DNSSECRecords.records) {
- fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now), DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStates[ne.d_validationState]);
+ fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStates[ne.d_validationState]);
}
for (const auto& sig : ne.DNSSECRecords.signatures) {
- fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now), sig.d_content->getZoneRepresentation().c_str());
+ fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), sig.d_content->getZoneRepresentation().c_str());
}
}
return ret;
#include "ednssubnet.hh"
#include "iputils.hh"
-/* extract a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
-int getEDNSOption(char* optRR, size_t len, uint16_t wantedOption, char ** optionValue, size_t * optionValueSize);
-
BOOST_AUTO_TEST_SUITE(ednsoptions_cc)
static void getRawQueryWithECSAndCookie(const DNSName& name, const Netmask& ecs, const std::string& clientCookie, const std::string& serverCookie, std::vector<uint8_t>& query)
arr.d_ttl=aaaarr.d_ttl=nsrr.d_ttl=time(nullptr)+3600000;
for(char c='a';c<='m';++c) {
- static char templ[40];
+ char templ[40];
strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1);
templ[sizeof(templ)-1] = '\0';
*templ=c;
arr.d_content=std::make_shared<ARecordContent>(ComboAddress(rootIps4[c-'a']));
vector<DNSRecord> aset;
aset.push_back(arr);
- t_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true); // auth, nuke it all
+ t_RC->replace(time(nullptr), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true); // auth, nuke it all
if (rootIps6[c-'a'] != NULL) {
aaaarr.d_content=std::make_shared<AAAARecordContent>(ComboAddress(rootIps6[c-'a']));
vector<DNSRecord> aaaaset;
aaaaset.push_back(aaaarr);
- t_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true);
+ t_RC->replace(time(nullptr), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true);
}
nsset.push_back(nsrr);
}
- t_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false); // and stuff in the cache
+ t_RC->replace(time(nullptr), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false); // and stuff in the cache
}
LuaConfigItems::LuaConfigItems()
SyncRes::s_rootNXTrust = true;
SyncRes::s_minimumTTL = 0;
SyncRes::s_serverID = "PowerDNS Unit Tests Server ID";
- SyncRes::clearEDNSSubnets();
+ SyncRes::clearEDNSLocalSubnets();
+ SyncRes::addEDNSLocalSubnet("0.0.0.0/0");
+ SyncRes::addEDNSLocalSubnet("::/0");
+ SyncRes::clearEDNSRemoteSubnets();
SyncRes::clearEDNSDomains();
SyncRes::clearDelegationOnly();
SyncRes::clearDontQuery();
+ SyncRes::setECSScopeZeroAddress(Netmask("127.0.0.1/32"));
SyncRes::clearNSSpeeds();
BOOST_CHECK_EQUAL(SyncRes::getNSSpeedsSize(), 0);
const auto it = keys.find(signer);
if (it == keys.cend()) {
- throw std::runtime_error("No DNSKEY found for " + signer.toString() + ", unable to compute the requested RRSIG");
+ throw std::runtime_error("No DNSKEY found for " + signer.toLogString() + ", unable to compute the requested RRSIG");
}
size_t recordsCount = records.size();
{
const auto it = keys.find(signer);
if (it == keys.cend()) {
- throw std::runtime_error("No DNSKEY found for " + signer.toString());
+ throw std::runtime_error("No DNSKEY found for " + signer.toLogString());
}
DNSRecord rec;
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(downServers.size(), 4);
+ time_t now = sr->getNow().tv_sec;
for (const auto& server : downServers) {
BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1);
- BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
+ BOOST_CHECK(SyncRes::isThrottled(now, server, target, QType::A));
}
}
BOOST_CHECK_EQUAL(ret.size(), 0);
BOOST_CHECK_EQUAL(downServers.size(), 4);
+ time_t now = sr->getNow().tv_sec;
for (const auto& server : downServers) {
BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1);
- BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
+ BOOST_CHECK(SyncRes::isThrottled(now, server, target, QType::A));
}
}
BOOST_CHECK_EQUAL(downServers.size(), 3);
/* Error is reported as "OS limit error" (-2) so the servers should _NOT_ be marked down */
+ time_t now = sr->getNow().tv_sec;
for (const auto& server : downServers) {
BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 0);
- BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), server, target, QType::A));
+ BOOST_CHECK(!SyncRes::isThrottled(now, server, target, QType::A));
}
}
EDNSSubnetOpts incomingECS;
incomingECS.source = Netmask("192.0.2.128/32");
- sr->setIncomingECSFound(true);
- sr->setIncomingECS(incomingECS);
+ sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
primeHints();
const DNSName target("powerdns.com.");
- SyncRes::addEDNSSubnet(Netmask("192.0.2.1/32"));
+ SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
EDNSSubnetOpts incomingECS;
incomingECS.source = Netmask("2001:DB8::FF/128");
- sr->setIncomingECSFound(true);
- sr->setIncomingECS(incomingECS);
+ sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
BOOST_CHECK_EQUAL(ret[0].d_name, target);
}
+BOOST_AUTO_TEST_CASE(test_ecs_use_requestor) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ primeHints();
+
+ const DNSName target("powerdns.com.");
+ SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
+ // No incoming ECS data
+ sr->setQuerySource(ComboAddress("192.0.2.127"), boost::none);
+
+ sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ if (isRootServer(ip)) {
+ BOOST_REQUIRE(!srcmask);
+
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ } else if (ip == ComboAddress("192.0.2.1:53")) {
+
+ BOOST_REQUIRE(srcmask);
+ BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
+
+ setLWResult(res, 0, true, false, false);
+ addRecordToLW(res, domain, QType::A, "192.0.2.2");
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(ret[0].d_name, target);
+}
+
+BOOST_AUTO_TEST_CASE(test_ecs_use_scope_zero) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ primeHints();
+
+ const DNSName target("powerdns.com.");
+ SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
+ SyncRes::clearEDNSLocalSubnets();
+ SyncRes::addEDNSLocalSubnet("192.0.2.254/32");
+ // No incoming ECS data, Requestor IP not in ecs-add-for
+ sr->setQuerySource(ComboAddress("192.0.2.127"), boost::none);
+
+ sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ if (isRootServer(ip)) {
+ BOOST_REQUIRE(!srcmask);
+
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ } else if (ip == ComboAddress("192.0.2.1:53")) {
+
+ BOOST_REQUIRE(srcmask);
+ BOOST_CHECK_EQUAL(srcmask->toString(), "127.0.0.1/32");
+
+ setLWResult(res, 0, true, false, false);
+ addRecordToLW(res, domain, QType::A, "192.0.2.2");
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(ret[0].d_name, target);
+}
+
+BOOST_AUTO_TEST_CASE(test_ecs_honor_incoming_mask) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ primeHints();
+
+ const DNSName target("powerdns.com.");
+ SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
+ SyncRes::clearEDNSLocalSubnets();
+ SyncRes::addEDNSLocalSubnet("192.0.2.254/32");
+ EDNSSubnetOpts incomingECS;
+ incomingECS.source = Netmask("192.0.0.0/16");
+ sr->setQuerySource(ComboAddress("192.0.2.127"), boost::optional<const EDNSSubnetOpts&>(incomingECS));
+
+ sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ if (isRootServer(ip)) {
+ BOOST_REQUIRE(!srcmask);
+
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ } else if (ip == ComboAddress("192.0.2.1:53")) {
+
+ BOOST_REQUIRE(srcmask);
+ BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.0.0/16");
+
+ setLWResult(res, 0, true, false, false);
+ addRecordToLW(res, domain, QType::A, "192.0.2.2");
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(ret[0].d_name, target);
+}
+
+BOOST_AUTO_TEST_CASE(test_ecs_honor_incoming_mask_zero) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ primeHints();
+
+ const DNSName target("powerdns.com.");
+ SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
+ SyncRes::clearEDNSLocalSubnets();
+ SyncRes::addEDNSLocalSubnet("192.0.2.254/32");
+ EDNSSubnetOpts incomingECS;
+ incomingECS.source = Netmask("0.0.0.0/0");
+ sr->setQuerySource(ComboAddress("192.0.2.127"), boost::optional<const EDNSSubnetOpts&>(incomingECS));
+
+ sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ if (isRootServer(ip)) {
+ BOOST_REQUIRE(!srcmask);
+
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ } else if (ip == ComboAddress("192.0.2.1:53")) {
+
+ BOOST_REQUIRE(srcmask);
+ BOOST_CHECK_EQUAL(srcmask->toString(), "127.0.0.1/32");
+
+ setLWResult(res, 0, true, false, false);
+ addRecordToLW(res, domain, QType::A, "192.0.2.2");
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(ret[0].d_name, target);
+}
+
BOOST_AUTO_TEST_CASE(test_following_cname) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
});
/* mark ns as down */
- SyncRes::doThrottle(time(nullptr), ns, SyncRes::s_serverdownthrottletime, 10000);
+ time_t now = sr->getNow().tv_sec;
+ SyncRes::doThrottle(now, ns, SyncRes::s_serverdownthrottletime, 10000);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
const size_t blocks = 10;
/* mark ns as down for 'blocks' queries */
- SyncRes::doThrottle(time(nullptr), ns, SyncRes::s_serverdownthrottletime, blocks);
+ time_t now = sr->getNow().tv_sec;
+ SyncRes::doThrottle(now, ns, SyncRes::s_serverdownthrottletime, blocks);
for (size_t idx = 0; idx < blocks; idx++) {
- BOOST_CHECK(SyncRes::isThrottled(time(nullptr), ns));
+ BOOST_CHECK(SyncRes::isThrottled(now, ns));
}
/* we have been throttled 'blocks' times, we should not be throttled anymore */
- BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), ns));
+ BOOST_CHECK(!SyncRes::isThrottled(now, ns));
}
BOOST_AUTO_TEST_CASE(test_throttled_server_time) {
const size_t seconds = 1;
/* mark ns as down for 'seconds' seconds */
- SyncRes::doThrottle(time(nullptr), ns, seconds, 10000);
-
- BOOST_CHECK(SyncRes::isThrottled(time(nullptr), ns));
+ time_t now = sr->getNow().tv_sec;
+ SyncRes::doThrottle(now, ns, seconds, 10000);
- sleep(seconds + 1);
+ BOOST_CHECK(SyncRes::isThrottled(now, ns));
/* we should not be throttled anymore */
- BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), ns));
+ BOOST_CHECK(!SyncRes::isThrottled(now + 2, ns));
}
BOOST_AUTO_TEST_CASE(test_dont_query_server) {
EDNSSubnetOpts incomingECS;
incomingECS.source = Netmask("192.0.2.128/32");
- sr->setIncomingECSFound(true);
- sr->setIncomingECS(incomingECS);
+ sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
sr->setAsyncCallback([target,cnameTarget](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
return 0;
});
- struct timeval now;
- gettimeofday(&now, 0);
+ struct timeval now = sr->getNow();
/* make pdns-public-ns2.powerdns.com. the fastest NS, with its IPv6 address faster than the IPV4 one,
then pdns-public-ns1.powerdns.com. on IPv4 */
BOOST_CHECK_EQUAL(queriesCount, 2);
}
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_unsigned_ds) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target("com.");
+ const ComboAddress targetAddr("192.0.2.42");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::SHA384, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+ queriesCount++;
+
+ DNSName auth = domain;
+
+ if (type == QType::DS || type == QType::DNSKEY) {
+ if (genericDSAndDNSKEYHandler(res, domain, auth, type, keys) == 0) {
+ return 0;
+ }
+
+ if (type == QType::DS && domain == target) {
+ /* remove the last record, which is the DS's RRSIG */
+ res->d_records.pop_back();
+ }
+
+ return 1;
+ }
+
+ if (isRootServer(ip)) {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+ /* Include the DS but omit the RRSIG*/
+ addDS(DNSName("com."), 300, res->d_records, keys);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ }
+
+ if (ip == ComboAddress("192.0.2.1:53")) {
+ setLWResult(res, RCode::NoError, true, false, true);
+ addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+ addRRSIG(keys, res->d_records, auth, 300);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 2);
+ BOOST_CHECK_EQUAL(queriesCount, 4);
+
+ /* again, to test the cache */
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 2);
+ BOOST_CHECK_EQUAL(queriesCount, 4);
+
+ /* now we ask directly for the DS */
+ ret.clear();
+ res = sr->beginResolve(DNSName("com."), QType(QType::DS), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK_EQUAL(queriesCount, 4);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_unsigned_ds_direct) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target("com.");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::SHA384, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+ queriesCount++;
+
+ DNSName auth = domain;
+
+ if (type == QType::DS || type == QType::DNSKEY) {
+ if (genericDSAndDNSKEYHandler(res, domain, auth, type, keys) == 0) {
+ return 0;
+ }
+
+ if (type == QType::DS && domain == target) {
+ /* remove the last record, which is the DS's RRSIG */
+ res->d_records.pop_back();
+ }
+
+ return 1;
+ }
+
+ if (isRootServer(ip)) {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+ /* Include the DS but omit the RRSIG*/
+ addDS(DNSName("com."), 300, res->d_records, keys);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(DNSName("com."), QType(QType::DS), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
BOOST_CHECK_EQUAL(queriesCount, 11);
}
+BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_cname_glue) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target("powerdns.com.");
+ const DNSName targetCName1("cname.sub.powerdns.com.");
+ const DNSName targetCName2("cname2.sub.powerdns.com.");
+ const ComboAddress targetCName2Addr("192.0.2.42");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+ generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([target,targetCName1,targetCName2,targetCName2Addr,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+ queriesCount++;
+
+ if (type == QType::DS || type == QType::DNSKEY) {
+ if (domain == DNSName("sub.powerdns.com")) {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
+ addNSECRecordToLW(domain, DNSName("z.power-dns.com."), { QType::NS }, 600, res->d_records);
+ addRRSIG(keys, res->d_records, DNSName("com."), 300);
+ return 1;
+ }
+ else {
+ return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
+ }
+ }
+ else {
+ if (isRootServer(ip)) {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+ addDS(DNSName("com."), 300, res->d_records, keys);
+ addRRSIG(keys, res->d_records, DNSName("."), 300);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ }
+ else if (ip == ComboAddress("192.0.2.1:53")) {
+ setLWResult(res, 0, false, false, true);
+ if (domain == DNSName("com.")) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
+ addRRSIG(keys, res->d_records, DNSName("com."), 300);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ addRRSIG(keys, res->d_records, DNSName("com."), 300);
+ }
+ else {
+ addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
+ if (domain == DNSName("powerdns.com.")) {
+ addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
+ }
+ else if (domain == DNSName("sub.powerdns.com")) {
+ addNSECRecordToLW(domain, DNSName("z.power-dns.com."), { QType::NS }, 600, res->d_records);
+ }
+ addRRSIG(keys, res->d_records, DNSName("com."), 300);
+ addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+ }
+
+ return 1;
+ }
+ else if (ip == ComboAddress("192.0.2.2:53")) {
+ setLWResult(res, 0, true, false, true);
+
+ if (type == QType::NS) {
+ addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
+ if (domain == DNSName("powerdns.com.")) {
+ addRRSIG(keys, res->d_records, domain, 300);
+ }
+ addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+ if (domain == DNSName("powerdns.com.")) {
+ addRRSIG(keys, res->d_records, domain, 300);
+ }
+ }
+ else {
+ if (domain == DNSName("powerdns.com.")) {
+ addRecordToLW(res, domain, QType::CNAME, targetCName1.toString());
+ addRRSIG(keys, res->d_records, domain, 300);
+ /* add the CNAME target as a glue, with no RRSIG since the sub zone is insecure */
+ addRecordToLW(res, targetCName1, QType::CNAME, targetCName2.toString());
+ addRecordToLW(res, targetCName2, QType::A, targetCName2Addr.toString());
+ }
+ else if (domain == targetCName1) {
+ addRecordToLW(res, domain, QType::CNAME, targetCName2.toString());
+ }
+ else if (domain == targetCName2) {
+ addRecordToLW(res, domain, QType::A, targetCName2Addr.toString());
+ }
+ }
+
+ return 1;
+ }
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_REQUIRE_EQUAL(ret.size(), 4);
+ BOOST_CHECK_EQUAL(queriesCount, 11);
+
+ /* again, to test the cache */
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_REQUIRE_EQUAL(ret.size(), 4);
+ BOOST_CHECK_EQUAL(queriesCount, 11);
+}
+
BOOST_AUTO_TEST_CASE(test_dnssec_insecure_to_secure_cname) {
std::unique_ptr<SyncRes> sr;
initSR(sr, true);
delegation NSEC can only deny the DS */
BOOST_CHECK_EQUAL(denialState, NODATA);
+ /* it can not be used to deny any RRs below that owner name either */
+ denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, false);
+ BOOST_CHECK_EQUAL(denialState, NODATA);
+
denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
BOOST_CHECK_EQUAL(denialState, NXQTYPE);
}
denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+
+ /* it can not be used to deny any RRs below that owner name either */
+ /* Add NSEC3 for the next closer */
+ recordContents.clear();
+ signatureContents.clear();
+ records.clear();
+ addNSEC3NarrowRecordToLW(DNSName("sub.a."), DNSName("."), { QType::A, QType::TXT, QType::RRSIG, QType::NSEC3 }, 600, records);
+ recordContents.push_back(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+
+ /* add wildcard denial */
+ recordContents.clear();
+ signatureContents.clear();
+ records.clear();
+ addNSEC3NarrowRecordToLW(DNSName("*.a."), DNSName("."), { QType::A, QType::TXT, QType::RRSIG, QType::NSEC3 }, 600, records);
+ recordContents.push_back(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+
+ denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, true);
+ BOOST_CHECK_EQUAL(denialState, NODATA);
}
BOOST_AUTO_TEST_CASE(test_nsec3_denial_too_many_iterations) {
--- /dev/null
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include "xpf.hh"
+
+BOOST_AUTO_TEST_SUITE(xpf_cc)
+
+BOOST_AUTO_TEST_CASE(test_generateXPFPayload) {
+
+ /* Mixing v4 with v6 should throw */
+ BOOST_CHECK_THROW(generateXPFPayload(false, ComboAddress("192.0.2.1"), ComboAddress("2001:db8::1")), std::runtime_error);
+ BOOST_CHECK_THROW(generateXPFPayload(false, ComboAddress("2001:db8::1"), ComboAddress("192.0.2.1")), std::runtime_error);
+
+ {
+ /* v4 payload over UDP */
+ ComboAddress source("192.0.2.1:53");
+ ComboAddress destination("192.0.2.2:65535");
+
+ auto payload = generateXPFPayload(false, source, destination);
+ BOOST_CHECK_EQUAL(payload.size(), 14);
+ BOOST_CHECK_EQUAL(payload.at(0), 4);
+ BOOST_CHECK_EQUAL(payload.at(1), 17);
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination));
+ BOOST_CHECK_EQUAL(parsedSource.toStringWithPort(), source.toStringWithPort());
+ BOOST_CHECK_EQUAL(parsedDestination.toStringWithPort(), destination.toStringWithPort());
+ }
+
+ {
+ /* v4 payload over TCP */
+ ComboAddress source("192.0.2.1:53");
+ ComboAddress destination("192.0.2.2:65535");
+
+ auto payload = generateXPFPayload(true, source, destination);
+ BOOST_CHECK_EQUAL(payload.size(), 14);
+ BOOST_CHECK_EQUAL(payload.at(0), 4);
+ BOOST_CHECK_EQUAL(payload.at(1), 6);
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination));
+ BOOST_CHECK_EQUAL(parsedSource.toStringWithPort(), source.toStringWithPort());
+ BOOST_CHECK_EQUAL(parsedDestination.toStringWithPort(), destination.toStringWithPort());
+ }
+
+ {
+ /* v6 payload over UDP */
+ ComboAddress source("[2001:db8::1]:42");
+ ComboAddress destination("[::1]:65535");
+
+ auto payload = generateXPFPayload(false, source, destination);
+ BOOST_CHECK_EQUAL(payload.size(), 38);
+ BOOST_CHECK_EQUAL(payload.at(0), 6);
+ BOOST_CHECK_EQUAL(payload.at(1), 17);
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination));
+ BOOST_CHECK_EQUAL(parsedSource.toStringWithPort(), source.toStringWithPort());
+ BOOST_CHECK_EQUAL(parsedDestination.toStringWithPort(), destination.toStringWithPort());
+ }
+
+ {
+ /* v6 payload over TCP */
+ ComboAddress source("[2001:db8::1]:42");
+ ComboAddress destination("[::1]:65535");
+
+ auto payload = generateXPFPayload(true, source, destination);
+ BOOST_CHECK_EQUAL(payload.size(), 38);
+ BOOST_CHECK_EQUAL(payload.at(0), 6);
+ BOOST_CHECK_EQUAL(payload.at(1), 6);
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination));
+ BOOST_CHECK_EQUAL(parsedSource.toStringWithPort(), source.toStringWithPort());
+ BOOST_CHECK_EQUAL(parsedDestination.toStringWithPort(), destination.toStringWithPort());
+ }
+
+}
+
+BOOST_AUTO_TEST_CASE(test_parseXPFPayload) {
+
+ /* invalid sizes */
+ {
+ ComboAddress source;
+ ComboAddress destination;
+
+ BOOST_CHECK_EQUAL(parseXPFPayload(nullptr, 0, source, &destination), false);
+ BOOST_CHECK_EQUAL(parseXPFPayload(nullptr, 13, source, &destination), false);
+ BOOST_CHECK_EQUAL(parseXPFPayload(nullptr, 15, source, &destination), false);
+ BOOST_CHECK_EQUAL(parseXPFPayload(nullptr, 37, source, &destination), false);
+ BOOST_CHECK_EQUAL(parseXPFPayload(nullptr, 39, source, &destination), false);
+ }
+
+
+ {
+ /* invalid protocol */
+ ComboAddress source("[2001:db8::1]:42");
+ ComboAddress destination("[::1]:65535");
+
+ auto payload = generateXPFPayload(true, source, destination);
+ /* set protocol to 0 */
+ payload.at(1) = 0;
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK_EQUAL(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination), false);
+ }
+
+ {
+ /* invalid version */
+ ComboAddress source("[2001:db8::1]:42");
+ ComboAddress destination("[::1]:65535");
+
+ auto payload = generateXPFPayload(true, source, destination);
+ /* set version to 0 */
+ payload.at(0) = 0;
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK_EQUAL(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination), false);
+ }
+
+ {
+ /* payload too short (v6 size with v4 payload) */
+ ComboAddress source("192.0.2.1:53");
+ ComboAddress destination("192.0.2.2:65535");
+
+
+ auto payload = generateXPFPayload(true, source, destination);
+ /* set version to 6 */
+ payload.at(0) = 6;
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK_EQUAL(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination), false);
+ }
+
+ {
+ /* payload too long (v6 size with v4 payload) */
+ ComboAddress source("[2001:db8::1]:42");
+ ComboAddress destination("[::1]:65535");
+
+
+ auto payload = generateXPFPayload(true, source, destination);
+ /* set version to 4 */
+ payload.at(0) = 4;
+
+ ComboAddress parsedSource;
+ ComboAddress parsedDestination;
+ BOOST_CHECK_EQUAL(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, &parsedDestination), false);
+ }
+
+ {
+ /* v4 payload over UDP */
+ ComboAddress source("192.0.2.1:53");
+ ComboAddress destination("192.0.2.2:65535");
+
+ auto payload = generateXPFPayload(false, source, destination);
+ BOOST_CHECK_EQUAL(payload.size(), 14);
+ BOOST_CHECK_EQUAL(payload.at(0), 4);
+ BOOST_CHECK_EQUAL(payload.at(1), 17);
+
+ ComboAddress parsedSource;
+ BOOST_CHECK(parseXPFPayload(payload.c_str(), payload.size(), parsedSource, nullptr));
+ BOOST_CHECK_EQUAL(parsedSource.toStringWithPort(), source.toStringWithPort());
+ }
+
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
--- /dev/null
+../xpf.cc
\ No newline at end of file
--- /dev/null
+../xpf.hh
\ No newline at end of file
arr.d_ttl=aaaarr.d_ttl=nsrr.d_ttl=time(0)+3600000;
for(char c='a';c<='m';++c) {
- static char templ[40];
+ char templ[40];
strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1);
templ[sizeof(templ)-1] = '\0';
*templ=c;
}
else {
totremove++;
- L<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName.toStringNoDot()<<endl;
+ L<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
RPZRecordToPolicy(rr, newZone, false, defpol, maxTTL);
}
}
}
else {
totadd++;
- L<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName.toStringNoDot()<<endl;
+ L<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
RPZRecordToPolicy(rr, newZone, true, defpol, maxTTL);
}
}
dr.d_place=DNSResourceRecord::ANSWER;
}
catch(std::exception &e) {
- throw PDNSException("Error parsing record '"+rr.qname.toString()+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"': "+e.what());
+ throw PDNSException("Error parsing record '"+rr.qname.toLogString()+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"': "+e.what());
}
catch(...) {
- throw PDNSException("Error parsing record '"+rr.qname.toString()+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"'");
+ throw PDNSException("Error parsing record '"+rr.qname.toLogString()+"' of type "+rr.qtype.getName()+" in zone '"+headers.first+"' from file '"+headers.second+"'");
}
ad.d_records.insert(dr);
close(d_socket);
d_socket = -1;
}
+ d_connected = false;
try {
d_socket = SSocket(d_remote.sin4.sin_family, SOCK_STREAM, 0);
setNonBlocking(d_socket);
#endif
return false;
}
+ d_connected = true;
return true;
}
+void RemoteLogger::busyReconnectLoop()
+{
+ while (!reconnect()) {
+ sleep(d_reconnectWaitTime);
+ }
+}
+
void RemoteLogger::worker()
{
if (d_asyncConnect) {
- reconnect();
+ busyReconnectLoop();
}
while(true) {
d_writeQueue.pop();
}
+ if (!d_connected) {
+ busyReconnectLoop();
+ }
+
try {
uint16_t len = static_cast<uint16_t>(data.length());
sendSizeAndMsgWithTimeout(d_socket, len, data.c_str(), static_cast<int>(d_timeout), nullptr, nullptr, 0, 0, 0);
#else
vinfolog("Error sending data to remote logger (%s): %s", d_remote.toStringWithPort(), e.what());
#endif
- while (!reconnect()) {
- sleep(d_reconnectWaitTime);
- }
+ busyReconnectLoop();
}
}
}
if (d_socket >= 0) {
close(d_socket);
d_socket = -1;
+ d_connected = false;
}
d_queueCond.notify_one();
d_thread.join();
return d_remote.toStringWithPort();
}
private:
+ void busyReconnectLoop();
bool reconnect();
void worker();
uint8_t d_reconnectWaitTime;
std::atomic<bool> d_exiting{false};
bool d_asyncConnect{false};
+ bool d_connected{false};
std::thread d_thread;
};
}
uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& local,
- const DNSName &domain, int type, bool dnssecOK,
+ const DNSName &domain, int type, int *localsock, bool dnssecOK,
const DNSName& tsigkeyname, const DNSName& tsigalgorithm,
const string& tsigsecret)
{
} else {
std::string lstr = local.toString();
std::map<std::string, int>::iterator lptr;
- // see if there is a local
+ // reuse an existing local socket or make a new one
if ((lptr = locals.find(lstr)) != locals.end()) {
sock = lptr->second;
} else {
// try to make socket
sock = makeQuerySocket(local, true);
if (sock < 0)
- throw ResolverException("Unable to create socket to "+remote.toStringWithPort()+": "+stringerror());
+ throw ResolverException("Unable to create local socket on "+lstr+" to "+remote.toStringWithPort()+": "+stringerror());
setNonBlocking( sock );
locals[lstr] = sock;
}
}
+ if (localsock != nullptr) {
+ *localsock = sock;
+ }
if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
throw ResolverException("Unable to ask query of "+remote.toStringWithPort()+": "+stringerror());
}
return randomid;
}
-uint16_t Resolver::sendResolve(const ComboAddress& remote, const DNSName &domain,
- int type, bool dnssecOK,
- const DNSName& tsigkeyname, const DNSName& tsigalgorithm,
- const string& tsigsecret)
-{
- ComboAddress local;
- local.sin4.sin_family = 0;
- return this->sendResolve(remote, local, domain, type, dnssecOK, tsigkeyname, tsigalgorithm, tsigsecret);
-}
-
static int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
{
result->clear();
if(mdp.d_header.qdcount != 1)
throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
if(mdp.d_qname != origQname)
- throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toString()+"!="+ origQname.toString()+".)");
+ throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
}
vector<DNSResourceRecord> ret;
return 0;
}
-bool Resolver::tryGetSOASerial(DNSName *domain, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
+bool Resolver::tryGetSOASerial(DNSName *domain, ComboAddress* remote, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
{
auto fds = std::unique_ptr<struct pollfd[]>(new struct pollfd[locals.size()]);
size_t i = 0, k;
if (sock < 0) return false; // false alarm
int err;
- ComboAddress fromaddr;
- socklen_t addrlen=fromaddr.getSocklen();
+ remote->sin6.sin6_family = AF_INET6; // make sure getSocklen() below returns a large enough value
+ socklen_t addrlen=remote->getSocklen();
char buf[3000];
- err = recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr*)(&fromaddr), &addrlen);
+ err = recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr*)(remote), &addrlen);
if(err < 0) {
if(errno == EAGAIN)
return false;
*domain = mdp.d_qname;
if(domain->empty())
- throw ResolverException("SOA query to '" + fromaddr.toStringWithPort() + "' produced response without domain name (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
+ throw ResolverException("SOA query to '" + remote->toStringWithPort() + "' produced response without domain name (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
if(mdp.d_answers.empty())
- throw ResolverException("Query to '" + fromaddr.toStringWithPort() + "' for SOA of '" + domain->toString() + "' produced no results (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
+ throw ResolverException("Query to '" + remote->toStringWithPort() + "' for SOA of '" + domain->toLogString() + "' produced no results (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
if(mdp.d_qtype != QType::SOA)
- throw ResolverException("Query to '" + fromaddr.toStringWithPort() + "' for SOA of '" + domain->toString() + "' returned wrong record type");
+ throw ResolverException("Query to '" + remote->toStringWithPort() + "' for SOA of '" + domain->toLogString() + "' returned wrong record type");
*theirInception = *theirExpire = 0;
bool gotSOA=false;
}
}
if(!gotSOA)
- throw ResolverException("Query to '" + fromaddr.toString() + "' for SOA of '" + domain->toString() + "' did not return a SOA");
+ throw ResolverException("Query to '" + remote->toString() + "' for SOA of '" + domain->toLogString() + "' did not return a SOA");
return true;
}
try {
ComboAddress to(ipport, 53);
- int id = sendResolve(to, local, domain, type);
- int sock;
-
- // choose socket based on local
- if (local.sin4.sin_family == 0) {
- // up to us.
- sock = to.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
- } else {
- std::string lstr = local.toString();
- std::map<std::string, int>::iterator lptr;
- // see if there is a local
-
- if ((lptr = locals.find(lstr)) != locals.end()) sock = lptr->second;
- else throw ResolverException("sendResolve did not create socket for " + lstr);
- }
-
+ int sock = -1;
+ int id = sendResolve(to, local, domain, type, &sock);
int err=waitForData(sock, 0, 3000000);
if(!err) {
socklen_t addrlen = sizeof(from);
char buffer[3000];
int len;
-
+
if((len=recvfrom(sock, buffer, sizeof(buffer), 0,(struct sockaddr*)(&from), &addrlen)) < 0)
throw ResolverException("recvfrom error waiting for answer: "+stringerror());
-
+
+ if (from != to) {
+ throw ResolverException("Got answer from the wrong peer while resolving ("+from.toStringWithPort()+" instead of "+to.toStringWithPort()+", discarding");
+ }
+
MOADNSParser mdp(false, buffer, len);
return parseResult(mdp, domain, type, id, res);
}
int ret = resolve(ipport, domain, QType::SOA, &res);
if(ret || res.empty())
- throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toString() + "' produced no answers");
+ throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toLogString() + "' produced no answers");
if(res[0].qtype.getCode() != QType::SOA)
- throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toString() + "' produced a "+res[0].qtype.getName()+" record");
+ throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toLogString() + "' produced a "+res[0].qtype.getName()+" record");
vector<string>parts;
stringtok(parts, res[0].content);
if(parts.size()<3)
- throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toString() + "' produced an unparseable response");
+ throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toLogString() + "' produced an unparseable response");
try {
*serial=pdns_stou(parts[2]);
}
catch(const std::out_of_range& oor) {
- throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toString() + "' produced an unparseable serial");
+ throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain.toLogString() + "' produced an unparseable serial");
}
}
int resolve(const string &ip, const DNSName &domain, int type, res_t* result);
//! only send out a resolution request
- uint16_t sendResolve(const ComboAddress& remote, const ComboAddress& local, const DNSName &domain, int type, bool dnssecOk=false,
- const DNSName& tsigkeyname=DNSName(), const DNSName& tsigalgorithm=DNSName(), const string& tsigsecret="");
-
- uint16_t sendResolve(const ComboAddress& remote, const DNSName &domain, int type, bool dnssecOk=false,
+ uint16_t sendResolve(const ComboAddress& remote, const ComboAddress& local, const DNSName &domain, int type, int *localsock, bool dnssecOk=false,
const DNSName& tsigkeyname=DNSName(), const DNSName& tsigalgorithm=DNSName(), const string& tsigsecret="");
//! see if we got a SOA response from our sendResolve
- bool tryGetSOASerial(DNSName *theirDomain, uint32_t* theirSerial, uint32_t* theirInception, uint32_t* theirExpire, uint16_t* id);
+ bool tryGetSOASerial(DNSName *theirDomain, ComboAddress* remote, uint32_t* theirSerial, uint32_t* theirInception, uint32_t* theirExpire, uint16_t* id);
//! convenience function that calls resolve above
void getSoaSerial(const string &, const DNSName &, uint32_t *);
if (rr->d_place == DNSResourceRecord::ANSWER) {
int res = checkUpdatePrerequisites(rr, &di);
if (res>0) {
- L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check, returning "<<res<<endl;
+ L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check, returning "<<RCode::to_s(res)<<endl;
di.backend->abortTransaction();
return res;
}
}
}
if (matchRR != foundRR || foundRR != vec->size()) {
- L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check, returning NXRRSet"<<endl;
+ L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check (RRs differ), returning NXRRSet"<<endl;
di.backend->abortTransaction();
return RCode::NXRRSet;
}
}
void PacketHandler::increaseSerial(const string &msgPrefix, const DomainInfo *di, bool haveNSEC3, bool narrow, const NSEC3PARAMRecordContent *ns3pr) {
- DNSResourceRecord rec, newRec;
- di->backend->lookup(QType(QType::SOA), di->zone);
- bool foundSOA=false;
- while (di->backend->get(rec)) {
- newRec = rec;
- foundSOA=true;
- }
- if (!foundSOA) {
+ SOAData sd;
+ if (!di->backend->getSOA(di->zone, sd, true)) {
throw PDNSException("SOA-Serial update failed because there was no SOA. Wowie.");
}
- SOAData soa2Update;
- fillSOAData(rec.content, soa2Update);
- uint32_t oldSerial = soa2Update.serial;
+ uint32_t oldSerial = sd.serial;
if (oldSerial == 0) { // using Autoserial, leave the serial alone.
- L<<Logger::Notice<<msgPrefix<<"AutoSerial being used, not updating SOA serial."<<endl;
+ L<<Logger::Notice<<msgPrefix<<"AutoSerial in use in domain \""<<di->zone.toLogString()<<"\", not updating SOA serial."<<endl;
return;
}
string soaEditSetting;
d_dk.getSoaEdit(di->zone, soaEditSetting);
if (soaEditSetting.empty()) {
- L<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-DNSUPDATE increase on DNS update, but SOA-EDIT is not set for domain \""<< di->zone <<"\". Using DEFAULT for SOA-EDIT-DNSUPDATE"<<endl;
+ L<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-DNSUPDATE increase on DNS update, but SOA-EDIT is not set for domain \""<< di->zone.toLogString() <<"\". Using DEFAULT for SOA-EDIT-DNSUPDATE"<<endl;
soaEdit2136 = "DEFAULT";
} else
soaEdit = soaEditSetting;
}
}
- soa2Update.serial = calculateIncreaseSOA(soa2Update, soaEdit2136, soaEdit);
-
- newRec.content = serializeSOAData(soa2Update);
- vector<DNSResourceRecord> rrset;
- rrset.push_back(newRec);
- di->backend->replaceRRSet(di->id, newRec.qname, newRec.qtype, rrset);
- L<<Logger::Notice<<msgPrefix<<"Increasing SOA serial ("<<oldSerial<<" -> "<<soa2Update.serial<<")"<<endl;
-
- //Correct ordername + auth flag
- if (haveNSEC3 && narrow)
- di->backend->updateDNSSECOrderNameAndAuth(di->id, newRec.qname, DNSName(), true);
- else if (haveNSEC3) {
- DNSName ordername;
- if (!narrow)
- ordername = DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr, newRec.qname)));
-
- di->backend->updateDNSSECOrderNameAndAuth(di->id, newRec.qname, ordername, true);
- }
- else { // NSEC
- DNSName ordername=newRec.qname.makeRelative(di->zone);
- di->backend->updateDNSSECOrderNameAndAuth(di->id, newRec.qname, ordername, true);
+ DNSResourceRecord rr;
+ if (makeIncreasedSOARecord(sd, soaEdit2136, soaEdit, rr)) {
+ di->backend->replaceRRSet(di->id, rr.qname, rr.qtype, vector<DNSResourceRecord>(1, rr));
+ L << Logger::Notice << msgPrefix << "Increasing SOA serial (" << oldSerial << " -> " << sd.serial << ")" << endl;
+
+ //Correct ordername + auth flag
+ if (haveNSEC3) {
+ DNSName ordername;
+ if (!narrow)
+ ordername = DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr, rr.qname)));
+
+ di->backend->updateDNSSECOrderNameAndAuth(di->id, rr.qname, ordername, true);
+ } else { // NSEC
+ DNSName ordername = rr.qname.makeRelative(di->zone);
+ di->backend->updateDNSSECOrderNameAndAuth(di->id, rr.qname, ordername, true);
+ }
}
}
* Terrible right?
*/
if(parts.size() < 2 || parts.size() > 9)
- throw PDNSException("Invalid IP address in RPZ: "+name.toString());
+ throw PDNSException("Invalid IP address in RPZ: "+name.toLogString());
bool isV6 = (stoi(parts[0]) > 32);
bool hadZZ = false;
if (pdns_iequals(part,"zz")) {
if (hadZZ)
- throw PDNSException("more than one 'zz' label found in RPZ name"+name.toString());
+ throw PDNSException("more than one 'zz' label found in RPZ name"+name.toLogString());
part = "";
isV6 = true;
hadZZ = true;
}
if (isV6 && parts.size() < 9 && !hadZZ)
- throw PDNSException("No 'zz' label found in an IPv6 RPZ name shorter than 9 elements: "+name.toString());
+ throw PDNSException("No 'zz' label found in an IPv6 RPZ name shorter than 9 elements: "+name.toLogString());
if (parts.size() == 5 && !isV6)
return Netmask(parts[4]+"."+parts[3]+"."+parts[2]+"."+parts[1]+"/"+parts[0]);
else if(!crcTarget.empty() && !crcTarget.isRoot() && crcTarget.getRawLabel(crcTarget.countLabels() - 1).compare(0, rpzPrefix.length(), rpzPrefix) == 0) {
/* this is very likely an higher format number or a configuration error,
let's just ignore it. */
- L<<Logger::Info<<"Discarding unsupported RPZ entry "<<crcTarget.toString()<<" for "<<dr.d_name<<endl;
+ L<<Logger::Info<<"Discarding unsupported RPZ entry "<<crcTarget<<" for "<<dr.d_name<<endl;
return;
}
else {
}
}
catch(const PDNSException& pe) {
- throw PDNSException("Issue parsing '"+drr.qname.toString()+"' '"+drr.content+"' at "+zpt.getLineOfFile()+": "+pe.reason);
+ throw PDNSException("Issue parsing '"+drr.qname.toLogString()+"' '"+drr.content+"' at "+zpt.getLineOfFile()+": "+pe.reason);
}
}
}
#include "config.h"
#endif
#include "dnsparser.hh"
+#include "ednsoptions.hh"
#include "sstuff.hh"
#include "misc.hh"
#include "dnswriter.hh"
void usage() {
cerr<<"sdig"<<endl;
- cerr<<"Syntax: sdig IP-ADDRESS PORT QUESTION QUESTION-TYPE [dnssec] [recurse] [showflags] [hidesoadetails] [hidettl] [tcp] [ednssubnet SUBNET/MASK]"<<endl;
+ cerr<<"Syntax: sdig IP-ADDRESS PORT QUESTION QUESTION-TYPE [dnssec] [recurse] [showflags] [hidesoadetails] [hidettl] [tcp] [ednssubnet SUBNET/MASK] [xpf XPFDATA]"<<endl;
}
int main(int argc, char** argv)
bool showflags=false;
bool hidesoadetails=false;
boost::optional<Netmask> ednsnm;
-
+ uint16_t xpfcode = 0, xpfversion = 0, xpfproto = 0;
+ char *xpfsrc = NULL, *xpfdst = NULL;
for(int i=1; i<argc; i++) {
if ((string) argv[i] == "--help") {
if (strcmp(argv[i], "tcp") == 0)
tcp=true;
if (strcmp(argv[i], "ednssubnet") == 0) {
+ if(argc < i+2) {
+ cerr<<"ednssubnet needs an argument"<<endl;
+ exit(EXIT_FAILURE);
+ }
ednsnm=Netmask(argv[++i]);
}
+ if (strcmp(argv[i], "xpf") == 0) {
+ if(argc < i+6) {
+ cerr<<"xpf needs five arguments"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ xpfcode = atoi(argv[++i]);
+ xpfversion = atoi(argv[++i]);
+ xpfproto = atoi(argv[++i]);
+ xpfsrc = argv[++i];
+ xpfdst = argv[++i];
+ }
}
}
if(ednsnm) {
EDNSSubnetOpts eo;
eo.source = *ednsnm;
- opts.push_back(make_pair(8, makeEDNSSubnetOptsString(eo)));
+ opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)));
}
pw.addOpt(bufsize, 0, dnssec ? EDNSOpts::DNSSECOK : 0, opts);
pw.commit();
}
+ if(xpfcode)
+ {
+ ComboAddress src(xpfsrc), dst(xpfdst);
+ pw.startRecord(DNSName("."), xpfcode, 0, 1, DNSResourceRecord::ADDITIONAL);
+ // xpf->toPacket(pw);
+ pw.xfr8BitInt(xpfversion);
+ pw.xfr8BitInt(xpfproto);
+ pw.xfrCAWithoutPort(xpfversion, src);
+ pw.xfrCAWithoutPort(xpfversion, dst);
+ pw.xfrCAPort(src);
+ pw.xfrCAPort(dst);
+ pw.commit();
+ }
+
if(recurse)
{
pw.getHeader()->rd=true;
for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin();
iter != edo.d_options.end();
++iter) {
- if(iter->first == 5) {// 'EDNS PING'
- cerr<<"Have ednsping: '"<<iter->second<<"'\n";
- //if(iter->second == ping)
- // cerr<<"It is correct!"<<endl;
- }
- if(iter->first == 8) {// 'EDNS subnet'
+ if(iter->first == EDNSOptionCode::ECS) {// 'EDNS subnet'
EDNSSubnetOpts reso;
if(getEDNSSubnetOptsFromString(iter->second, &reso)) {
cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
#include "dnspacket.hh"
#include "namespaces.hh"
-
uint32_t localtime_format_YYYYMMDDSS(time_t t, uint32_t seq)
{
struct tm tm;
+ seq;
}
-bool editSOA(DNSSECKeeper& dk, const DNSName& qname, DNSPacket* dp)
-{
- for(auto& rr : dp->getRRS()) {
- if(rr.dr.d_type == QType::SOA && rr.dr.d_name == qname) {
- string kind;
- dk.getSoaEdit(qname, kind);
- return editSOARecord(rr, kind);
- }
- }
- return false;
-}
-
-bool editSOARecord(DNSZoneRecord& rr, const string& kind) {
- if(kind.empty())
- return false;
- auto src = getRR<SOARecordContent>(rr.dr);
- src->d_st.serial=calculateEditSOA(rr, kind);
-
- return true;
-}
-
-uint32_t calculateEditSOA(const DNSZoneRecord& rr, const string& kind)
+uint32_t calculateEditSOA(uint32_t old_serial, const string& kind, const DNSName& zonename)
{
- auto src = getRR<SOARecordContent>(rr.dr);
if(pdns_iequals(kind,"INCEPTION-INCREMENT")) {
time_t inception = getStartOfWeek();
uint32_t inception_serial = localtime_format_YYYYMMDDSS(inception, 1);
uint32_t dont_increment_after = localtime_format_YYYYMMDDSS(inception + 2*86400, 99);
- if(src->d_st.serial < inception_serial - 1) { /* less than <inceptionday>00 */
+ if(old_serial < inception_serial - 1) { /* less than <inceptionday>00 */
return inception_serial; /* return <inceptionday>01 (skipping <inceptionday>00 as possible value) */
- } else if(src->d_st.serial <= dont_increment_after) { /* >= <inceptionday>00 but <= <inceptionday+2>99 */
- return (src->d_st.serial + 2); /* "<inceptionday>00" and "<inceptionday>01" are reserved for inception increasing, so increment sd.serial by two */
+ } else if (old_serial < inception_serial+1) {
+ /* "<inceptionday>00" and "<inceptionday>01" are reserved for inception increasing, so jump to "<inceptionday>02" */
+ return inception_serial+1;
+ } else if(old_serial <= dont_increment_after) { /* >= <inceptionday>00 but <= <inceptionday+2>99 */
+ return old_serial + 1;
}
}
else if(pdns_iequals(kind,"INCREMENT-WEEKS")) {
time_t inception = getStartOfWeek();
- return (src->d_st.serial + (inception / (7*86400)));
+ return (old_serial + (inception / (7*86400)));
}
else if(pdns_iequals(kind,"EPOCH")) {
return time(0);
}
else if(pdns_iequals(kind,"INCEPTION-EPOCH")) {
uint32_t inception = getStartOfWeek();
- if (src->d_st.serial < inception)
+ if (old_serial < inception)
return inception;
} else if(!kind.empty()) {
- L<<Logger::Warning<<"SOA-EDIT type '"<<kind<<"' for zone "<<rr.dr.d_name<<" is unknown."<<endl;
+ L<<Logger::Warning<<"SOA-EDIT type '"<<kind<<"' for zone "<<zonename<<" is unknown."<<endl;
}
- return src->d_st.serial;
+ return old_serial;
}
-uint32_t calculateEditSOA(const SOAData& sd, const string& kind)
-{
- DNSZoneRecord dzr;
- dzr.dr.d_name=sd.qname;
- struct soatimes st;
- st.serial = sd.serial;
- dzr.dr.d_content = std::make_shared<SOARecordContent>(sd.nameserver, sd.hostmaster, st);
- return calculateEditSOA(dzr, kind);
+uint32_t calculateEditSOA(uint32_t old_serial, DNSSECKeeper& dk, const DNSName& zonename) {
+ string kind;
+ dk.getSoaEdit(zonename, kind);
+ if(kind.empty())
+ return old_serial;
+ return calculateEditSOA(old_serial, kind, zonename);
}
-// Used for SOA-EDIT-DNSUPDATE and SOA-EDIT-API.
-uint32_t calculateIncreaseSOA(DNSZoneRecord& dzr, const string& increaseKind, const string& editKind) {
- auto src = getRR<SOARecordContent>(dzr.dr);
- // These only work when SOA-EDIT is set, otherwise fall back to default.
- if (!editKind.empty()) {
- if (pdns_iequals(increaseKind, "SOA-EDIT-INCREASE")) {
- uint32_t new_serial = calculateEditSOA(dzr, editKind);
- if (new_serial <= src->d_st.serial) {
- new_serial = src->d_st.serial + 1;
- }
- return new_serial;
+/** Used for SOA-EDIT-DNSUPDATE and SOA-EDIT-API. */
+static uint32_t calculateIncreaseSOA(uint32_t old_serial, const string& increaseKind, const string& editKind, const DNSName& zonename) {
+ if (pdns_iequals(increaseKind, "SOA-EDIT-INCREASE")) {
+ uint32_t new_serial = old_serial;
+ if (!editKind.empty()) {
+ new_serial = calculateEditSOA(old_serial, editKind, zonename);
}
- else if (pdns_iequals(increaseKind, "SOA-EDIT")) {
- return calculateEditSOA(dzr, editKind);
+ if (new_serial <= old_serial) {
+ new_serial = old_serial + 1;
}
+ return new_serial;
}
-
- if (pdns_iequals(increaseKind, "INCREASE")) {
- return src->d_st.serial + 1;
+ else if (pdns_iequals(increaseKind, "SOA-EDIT")) {
+ return calculateEditSOA(old_serial, editKind, zonename);
+ }
+ else if (pdns_iequals(increaseKind, "INCREASE")) {
+ return old_serial + 1;
}
else if (pdns_iequals(increaseKind, "EPOCH")) {
return time(0);
}
-
- // DEFAULT case
- time_t now = time(0);
- struct tm tm;
- localtime_r(&now, &tm);
- boost::format fmt("%04d%02d%02d%02d");
- string newdate = (fmt % (tm.tm_year + 1900) % (tm.tm_mon + 1) % tm.tm_mday % 1).str();
- uint32_t new_serial = pdns_stou(newdate);
- if (new_serial <= src->d_st.serial) {
- new_serial = src->d_st.serial + 1;
+ else if (pdns_iequals(increaseKind, "DEFAULT")) {
+ time_t now = time(0);
+ uint32_t new_serial = localtime_format_YYYYMMDDSS(now, 1);
+ if (new_serial <= old_serial) {
+ new_serial = old_serial + 1;
+ }
+ return new_serial;
+ } else if(!increaseKind.empty()) {
+ L<<Logger::Warning<<"SOA-EDIT-API/DNSUPDATE type '"<<increaseKind<<"' for zone "<<zonename<<" is unknown."<<endl;
}
- return new_serial;
-}
-
-// Used for SOA-EDIT-DNSUPDATE and SOA-EDIT-API.
-uint32_t calculateIncreaseSOA(SOAData sd, const string& increaseKind, const string& editKind) {
- DNSZoneRecord dzr;
- dzr.dr.d_name=sd.qname;
- struct soatimes st;
- st.serial = sd.serial;
- dzr.dr.d_content = std::make_shared<SOARecordContent>(sd.nameserver, sd.hostmaster, st);
- return calculateIncreaseSOA(dzr, increaseKind, editKind);
+ return old_serial;
}
-
-
+/** Used for SOA-EDIT-DNSUPDATE and SOA-EDIT-API.
+ * Good if you already *have* a DNSResourceRecord.
+ * Content in rr is suitable for writing into a backend.
+ *
+ * @return true if changes may have been made
+ */
bool increaseSOARecord(DNSResourceRecord& rr, const string& increaseKind, const string& editKind) {
if (increaseKind.empty())
return false;
SOAData sd;
fillSOAData(rr.content, sd);
- sd.serial = calculateIncreaseSOA(sd, increaseKind, editKind);
- rr.content = serializeSOAData(sd);
+
+ sd.serial = calculateIncreaseSOA(sd.serial, increaseKind, editKind, rr.qname);
+ rr.content = makeSOAContent(sd)->getZoneRepresentation(true);
+ return true;
+}
+
+/** Used for SOA-EDIT-DNSUPDATE and SOA-EDIT-API.
+ * Makes a mostly reset DNSResourceRecord for you in @param rrout.
+ * Content in rrout is suitable for writing into a backend.
+ *
+ * @return true if rrout is now valid
+ */
+bool makeIncreasedSOARecord(SOAData& sd, const string& increaseKind, const string& editKind, DNSResourceRecord& rrout) {
+ if (increaseKind.empty())
+ return false;
+
+ sd.serial = calculateIncreaseSOA(sd.serial, increaseKind, editKind, sd.qname);
+ rrout.qname = sd.qname;
+ rrout.content = makeSOAContent(sd)->getZoneRepresentation(true);
+ rrout.qtype = QType::SOA;
+ rrout.domain_id = sd.domain_id;
+ rrout.auth = 1;
+ rrout.ttl = sd.ttl;
+
return true;
}
}
}
namespace {
-struct QueryInfo
-{
- struct timeval query_ttd;
- uint16_t id;
-};
-
struct DomainNotificationInfo
{
DomainInfo di;
struct SlaveSenderReceiver
{
- typedef pair<DNSName, uint16_t> Identifier;
+ typedef std::tuple<DNSName, ComboAddress, uint16_t> Identifier;
struct Answer {
uint32_t theirSerial;
{
random_shuffle(dni.di.masters.begin(), dni.di.masters.end());
try {
- ComboAddress remote(*dni.di.masters.begin());
- if (dni.localaddr.sin4.sin_family == 0) {
- return make_pair(dni.di.zone,
- d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53),
- dni.di.zone,
- QType::SOA,
- dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
+ ComboAddress remote(*dni.di.masters.begin(), 53);
+ return std::make_tuple(dni.di.zone,
+ remote,
+ d_resolver.sendResolve(remote,
+ dni.localaddr,
+ dni.di.zone,
+ QType::SOA,
+ nullptr,
+ dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
);
- } else {
- return make_pair(dni.di.zone,
- d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), dni.localaddr,
- dni.di.zone,
- QType::SOA,
- dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
- );
- }
}
catch(PDNSException& e) {
- throw runtime_error("While attempting to query freshness of '"+dni.di.zone.toString()+"': "+e.reason);
+ throw runtime_error("While attempting to query freshness of '"+dni.di.zone.toLogString()+"': "+e.reason);
}
}
bool receive(Identifier& id, Answer& a)
{
- if(d_resolver.tryGetSOASerial(&id.first, &a.theirSerial, &a.theirInception, &a.theirExpire, &id.second)) {
+ if(d_resolver.tryGetSOASerial(&(std::get<0>(id)), &(std::get<1>(id)), &a.theirSerial, &a.theirInception, &a.theirExpire, &(std::get<2>(id)))) {
return 1;
}
return 0;
}
}
}
-
-// stub for PowerDNSLua linking
-int directResolve(const std::string& qname, const QType& qtype, int qclass, vector<DNSResourceRecord>& ret)
-{
- return -1;
-}
-
-
std::unordered_set<DNSName> SyncRes::s_delegationOnly;
std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
-NetmaskGroup SyncRes::s_ednssubnets;
+NetmaskGroup SyncRes::s_ednslocalsubnets;
+NetmaskGroup SyncRes::s_ednsremotesubnets;
SuffixMatchNode SyncRes::s_ednsdomains;
EDNSSubnetOpts SyncRes::s_ecsScopeZero;
string SyncRes::s_serverID;
if(done) {
if(j==1 && s_doIPv6) { // we got an A record, see if we have some AAAA lying around
vector<DNSRecord> cset;
- if(t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_incomingECSFound ? d_incomingECSNetwork : d_requestor) > 0) {
+ if(t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote) > 0) {
for(auto k=cset.cbegin();k!=cset.cend();++k) {
if(k->d_ttl > (unsigned int)d_now.tv_sec ) {
if (auto drc = getRR<AAAARecordContent>(*k)) {
vector<DNSRecord> ns;
*flawedNSSet = false;
- if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_incomingECSFound ? d_incomingECSNetwork : d_requestor) > 0) {
+ if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote) > 0) {
for(auto k=ns.cbegin();k!=ns.cend(); ++k) {
if(k->d_ttl > (unsigned int)d_now.tv_sec ) {
vector<DNSRecord> aset;
const DNSRecord& dr=*k;
auto nrr = getRR<NSRecordContent>(dr);
if(nrr && (!nrr->getNS().isPartOf(subdomain) || t_RC->get(d_now.tv_sec, nrr->getNS(), s_doIPv6 ? QType(QType::ADDR) : QType(QType::A),
- false, doLog() ? &aset : 0, d_incomingECSFound ? d_incomingECSNetwork : d_requestor) > 5)) {
+ false, doLog() ? &aset : 0, d_cacheRemote) > 5)) {
bestns.push_back(dr);
LOG(prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<nrr->getNS()<<"'"<<endl);
LOG(prefix<<qname<<": within bailiwick: "<< nrr->getNS().isPartOf(subdomain));
vector<std::shared_ptr<RRSIGRecordContent>> signatures;
vector<std::shared_ptr<DNSRecord>> authorityRecs;
bool wasAuth;
- if(t_RC->get(d_now.tv_sec, qname, QType(QType::CNAME), d_requireAuthData, &cset, d_incomingECSFound ? d_incomingECSNetwork : d_requestor, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) {
+ if(t_RC->get(d_now.tv_sec, qname, QType(QType::CNAME), d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) {
for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) {
if (j->d_class != QClass::IN) {
state = SyncRes::validateRecordsWithSigs(depth, qname, QType(QType::CNAME), qname, cset, signatures);
if (state != Indeterminate) {
LOG(prefix<<qname<<": got Indeterminate state from the CNAME cache, new validation result is "<<vStates[state]<<endl);
- t_RC->updateValidationStatus(d_now.tv_sec, qname, QType(QType::CNAME), d_incomingECSFound ? d_incomingECSNetwork : d_requestor, d_requireAuthData, state);
+ t_RC->updateValidationStatus(d_now.tv_sec, qname, QType(QType::CNAME), d_cacheRemote, d_requireAuthData, state);
}
}
}
vector<std::shared_ptr<DNSRecord>> authorityRecs;
uint32_t ttl=0;
bool wasCachedAuth;
- if(t_RC->get(d_now.tv_sec, sqname, sqt, d_requireAuthData, &cset, d_incomingECSFound ? d_incomingECSNetwork : d_requestor, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) {
+ if(t_RC->get(d_now.tv_sec, sqname, sqt, d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) {
LOG(prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ");
if (cachedState != Indeterminate) {
LOG(prefix<<qname<<": got Indeterminate state from the cache, validation result is "<<vStates[cachedState]<<endl);
- t_RC->updateValidationStatus(d_now.tv_sec, sqname, sqt, d_incomingECSFound ? d_incomingECSNetwork : d_requestor, d_requireAuthData, cachedState);
+ t_RC->updateValidationStatus(d_now.tv_sec, sqname, sqt, d_cacheRemote, d_requireAuthData, cachedState);
}
}
LOG(endl<<prefix<<" ");
}
}
- LOG((i->empty() ? string("<empty>") : i->toString())<<"(" << (boost::format("%0.2f") % (speeds[*i]/1000.0)).str() <<"ms)");
+ LOG(i->toLogString()<<"(" << (boost::format("%0.2f") % (speeds[*i]/1000.0)).str() <<"ms)");
}
LOG(endl);
}
return rnameservers;
}
+inline vector<ComboAddress> SyncRes::shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd)
+{
+ vector<ComboAddress> nameservers = rnameservers;
+ map<ComboAddress, double> speeds;
+
+ for(const auto& val: nameservers) {
+ double speed;
+ DNSName nsName = DNSName(val.toStringWithPort());
+ speed=t_sstorage.nsSpeeds[nsName].get(&d_now);
+ speeds[val]=speed;
+ }
+ random_shuffle(nameservers.begin(),nameservers.end(), dns_random);
+ speedOrderCA so(speeds);
+ stable_sort(nameservers.begin(),nameservers.end(), so);
+
+ if(doLog()) {
+ LOG(prefix<<"Nameservers: ");
+ for(vector<ComboAddress>::const_iterator i=nameservers.cbegin();i!=nameservers.cend();++i) {
+ if(i!=nameservers.cbegin()) {
+ LOG(", ");
+ if(!((i-nameservers.cbegin())%3)) {
+ LOG(endl<<prefix<<" ");
+ }
+ }
+ LOG((wasRd ? string("+") : string("-")) << i->toStringWithPort() <<"(" << (boost::format("%0.2f") % (speeds[*i]/1000.0)).str() <<"ms)");
+ }
+ LOG(endl);
+ }
+ return nameservers;
+}
+
static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<RRSIGRecordContent>& rrsig)
{
uint32_t res = 0;
else {
LOG(prefix<<qname<<": Domain has hardcoded nameserver");
- result = nameservers[*tns].first;
- if(result.size() > 1) {
+ if(nameservers[*tns].first.size() > 1) {
LOG("s");
}
LOG(endl);
sendRDQuery = nameservers[*tns].second;
+ result = shuffleForwardSpeed(nameservers[*tns].first, doLog() ? (prefix+qname.toString()+": ") : string(), sendRDQuery);
pierceDontQuery=true;
}
return result;
void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned int depth)
{
if(!begin.isPartOf(end)) {
- LOG(d_prefix<<" "<<begin.toLogString()<<" is not part of "<<end.toString()<<endl);
- throw PDNSException(begin.toLogString() + " is not part of " + end.toString());
+ LOG(d_prefix<<" "<<begin.toLogString()<<" is not part of "<<end.toLogString()<<endl);
+ throw PDNSException(begin.toLogString() + " is not part of " + end.toLogString());
}
if (d_cutStates.count(begin) != 0) {
set the TC bit solely because these RRSIG RRs didn't fit."
*/
bool isAA = lwr.d_aabit && i->first.place != DNSResourceRecord::ADDITIONAL;
- if (isAA && isCNAMEAnswer && (i->first.place != DNSResourceRecord::ANSWER || i->first.type != QType::CNAME)) {
+ if (isAA && isCNAMEAnswer && (i->first.place != DNSResourceRecord::ANSWER || i->first.type != QType::CNAME || i->first.name != qname)) {
/*
rfc2181 states:
Note that the answer section of an authoritative answer normally
}
}
else {
+ recordState = Indeterminate;
+
/* in a non authoritative answer, we only care about the DS record (or lack of) */
if ((i->first.type == QType::DS || i->first.type == QType::NSEC || i->first.type == QType::NSEC3) && i->first.place == DNSResourceRecord::AUTHORITY) {
LOG(d_prefix<<"Validating DS record for "<<i->first.name<<endl);
}
}
- if (initialState == Secure && state != recordState) {
+ if (initialState == Secure && state != recordState && isAA) {
updateValidationState(state, recordState);
}
}
updateValidationState(state, st);
/* we already stored the record with a different validation status, let's fix it */
- t_RC->updateValidationStatus(d_now.tv_sec, qname, qtype, d_incomingECSFound ? d_incomingECSNetwork : d_requestor, lwr.d_aabit, st);
+ t_RC->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, lwr.d_aabit, st);
}
}
}
bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated)
{
- int resolveret;
+ int resolveret = RCode::NoError;
s_outqueries++;
d_outqueries++;
LOG(prefix<<qname<<": query handled by Lua"<<endl);
}
else {
- ednsmask=getEDNSSubnetMask(d_requestor, qname, remoteIP);
+ ednsmask=getEDNSSubnetMask(qname, remoteIP);
if(ednsmask) {
LOG(prefix<<qname<<": Adding EDNS Client Subnet Mask "<<ednsmask->toString()<<" to query"<<endl);
s_ecsqueries++;
}
if(resolveret != -2) { // don't account for resource limits, they are our own fault
- t_sstorage.nsSpeeds[nsName].submit(remoteIP, 1000000, &d_now); // 1 sec
+ t_sstorage.nsSpeeds[nsName.empty()? DNSName(remoteIP.toStringWithPort()) : nsName].submit(remoteIP, 1000000, &d_now); // 1 sec
// code below makes sure we don't filter COM or the root
if (s_serverdownmaxfails > 0 && (auth != g_rootdnsname) && t_sstorage.fails.incr(remoteIP) >= s_serverdownmaxfails) {
*/
// cout<<"msec: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
- t_sstorage.nsSpeeds[*tns].submit(*remoteIP, lwr.d_usec, &d_now);
+ t_sstorage.nsSpeeds[tns->empty()? DNSName(remoteIP->toStringWithPort()) : *tns].submit(*remoteIP, lwr.d_usec, &d_now);
/* we have received an answer, are we done ? */
bool done = processAnswer(depth, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, state);
return -1;
}
-void SyncRes::setIncomingECS(boost::optional<const EDNSSubnetOpts&> incomingECS)
+void SyncRes::setQuerySource(const ComboAddress& requestor, boost::optional<const EDNSSubnetOpts&> incomingECS)
{
- d_incomingECS = incomingECS;
- if (incomingECS) {
- if (d_incomingECS->source.getBits() == 0) {
+ d_requestor = requestor;
+
+ if (incomingECS && incomingECS->source.getBits() > 0) {
+ d_cacheRemote = incomingECS->source.getMaskedNetwork();
+ uint8_t bits = std::min(incomingECS->source.getBits(), (incomingECS->source.isIpv4() ? s_ecsipv4limit : s_ecsipv6limit));
+ ComboAddress trunc = incomingECS->source.getNetwork();
+ trunc.truncate(bits);
+ d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
+ } else {
+ d_cacheRemote = d_requestor;
+ if(!incomingECS && s_ednslocalsubnets.match(d_requestor)) {
+ ComboAddress trunc = d_requestor;
+ uint8_t bits = d_requestor.isIPv4() ? 32 : 128;
+ bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
+ trunc.truncate(bits);
+ d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
+ } else if (s_ecsScopeZero.source.getBits() > 0) {
/* RFC7871 says we MUST NOT send any ECS if the source scope is 0.
But using an empty ECS in that case would mean inserting
a non ECS-specific entry into the cache, preventing any further
indicator of the applicable scope. Subsequent Stub Resolver queries
for /0 can then be answered from this cached response.
*/
- d_incomingECS = s_ecsScopeZero;
- d_incomingECSNetwork = s_ecsScopeZero.source.getMaskedNetwork();
- }
- else {
- uint8_t bits = std::min(incomingECS->source.getBits(), (incomingECS->source.isIpv4() ? s_ecsipv4limit : s_ecsipv6limit));
- d_incomingECS->source = Netmask(incomingECS->source.getNetwork(), bits);
- d_incomingECSNetwork = d_incomingECS->source.getMaskedNetwork();
+ d_outgoingECSNetwork = boost::optional<Netmask>(s_ecsScopeZero.source.getMaskedNetwork());
+ d_cacheRemote = s_ecsScopeZero.source.getNetwork();
+ } else {
+ // ECS disabled because no scope-zero address could be derived.
+ d_outgoingECSNetwork = boost::none;
}
}
- else {
- d_incomingECSNetwork = ComboAddress();
- }
}
-boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem)
+boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& dn, const ComboAddress& rem)
{
- boost::optional<Netmask> result;
- ComboAddress trunc;
- uint8_t bits;
- if(d_incomingECSFound) {
- trunc = d_incomingECSNetwork;
- bits = d_incomingECS->source.getBits();
- }
- else if(!local.isIPv4() || local.sin4.sin_addr.s_addr) { // detect unset 'requestor'
- trunc = local;
- bits = local.isIPv4() ? 32 : 128;
- bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
- }
- else {
- /* nothing usable */
- return result;
- }
-
- if(s_ednsdomains.check(dn) || s_ednssubnets.match(rem)) {
- trunc.truncate(bits);
- return boost::optional<Netmask>(Netmask(trunc, bits));
+ if(d_outgoingECSNetwork && (s_ednsdomains.check(dn) || s_ednsremotesubnets.match(rem))) {
+ return d_outgoingECSNetwork;
}
-
- return result;
+ return boost::none;
}
void SyncRes::parseEDNSSubnetWhitelist(const std::string& wlist)
stringtok(parts, wlist, ",; ");
for(const auto& a : parts) {
try {
- s_ednssubnets.addMask(Netmask(a));
+ s_ednsremotesubnets.addMask(Netmask(a));
}
catch(...) {
s_ednsdomains.add(DNSName(a));
}
}
+void SyncRes::parseEDNSSubnetAddFor(const std::string& subnetlist)
+{
+ vector<string> parts;
+ stringtok(parts, subnetlist, ",; ");
+ for(const auto& a : parts) {
+ s_ednslocalsubnets.addMask(a);
+ }
+}
+
// used by PowerDNSLua - note that this neglects to add the packet count & statistics back to pdns_ercursor.cc
int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<DNSRecord>& ret)
{
s_dontQuery = nullptr;
}
static void parseEDNSSubnetWhitelist(const std::string& wlist);
- static void addEDNSSubnet(const Netmask& subnet)
+ static void parseEDNSSubnetAddFor(const std::string& subnetlist);
+ static void addEDNSLocalSubnet(const std::string& subnet)
{
- s_ednssubnets.addMask(subnet);
+ s_ednslocalsubnets.addMask(subnet);
+ }
+ static void addEDNSRemoteSubnet(const std::string& subnet)
+ {
+ s_ednsremotesubnets.addMask(subnet);
}
static void addEDNSDomain(const DNSName& domain)
{
s_ednsdomains.add(domain);
}
- static void clearEDNSSubnets()
+ static void clearEDNSLocalSubnets()
{
- s_ednssubnets.clear();
+ s_ednslocalsubnets.clear();
+ }
+ static void clearEDNSRemoteSubnets()
+ {
+ s_ednsremotesubnets.clear();
}
static void clearEDNSDomains()
{
return d_wantsRPZ;
}
- void setIncomingECSFound(bool state=true)
- {
- d_incomingECSFound=state;
- }
-
string getTrace() const
{
return d_trace.str();
d_skipCNAMECheck = skip;
}
- void setIncomingECS(boost::optional<const EDNSSubnetOpts&> incomingECS);
+ void setQuerySource(const ComboAddress& requestor, boost::optional<const EDNSSubnetOpts&> incomingECS);
#ifdef HAVE_PROTOBUF
void setInitialRequestId(boost::optional<const boost::uuids::uuid&> initialRequestId)
unsigned int d_timeouts;
unsigned int d_unreachables;
unsigned int d_totUsec;
- ComboAddress d_requestor;
private:
+ ComboAddress d_requestor;
+ ComboAddress d_cacheRemote;
static std::unordered_set<DNSName> s_delegationOnly;
- static NetmaskGroup s_ednssubnets;
+ static NetmaskGroup s_ednslocalsubnets;
+ static NetmaskGroup s_ednsremotesubnets;
static SuffixMatchNode s_ednsdomains;
static EDNSSubnetOpts s_ecsScopeZero;
static LogMode s_lm;
DNSName getBestNSNamesFromCache(const DNSName &qname, const QType &qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>&beenthere);
inline vector<DNSName> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
+ inline vector<ComboAddress> shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd);
bool moreSpecificThan(const DNSName& a, const DNSName &b) const;
vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly);
int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res) const;
- boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem);
+ boost::optional<Netmask> getEDNSSubnetMask(const DNSName&dn, const ComboAddress& rem);
bool validationEnabled() const;
uint32_t computeLowestTTD(const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures, uint32_t signaturesTTL) const;
zonesStates_t d_cutStates;
ostringstream d_trace;
shared_ptr<RecursorLua4> d_pdl;
- boost::optional<EDNSSubnetOpts> d_incomingECS;
- ComboAddress d_incomingECSNetwork;
+ boost::optional<Netmask> d_outgoingECSNetwork;
#ifdef HAVE_PROTOBUF
boost::optional<const boost::uuids::uuid&> d_initialRequestId;
#endif
bool d_doDNSSEC;
bool d_DNSSECValidationRequested{false};
bool d_doEDNS0{true};
- bool d_incomingECSFound{false};
bool d_requireAuthData{true};
bool d_skipCNAMECheck{false};
bool d_updatingRootNS{false};
bool d_auth;
};
- DNSResourceRecord makeDNSRRFromSOAData(const SOAData& sd)
+ DNSZoneRecord makeEditedDNSZRFromSOAData(DNSSECKeeper& dk, const SOAData& sd)
{
- DNSResourceRecord soa;
- soa.qname= sd.qname;
- soa.qtype=QType::SOA;
- soa.content=serializeSOAData(sd);
- soa.ttl=sd.ttl;
- soa.domain_id=sd.domain_id;
- soa.auth = true;
- return soa;
+ SOAData edited = sd;
+ edited.serial = calculateEditSOA(sd.serial, dk, sd.qname);
+
+ DNSRecord soa;
+ soa.d_name = sd.qname;
+ soa.d_type = QType::SOA;
+ soa.d_ttl = sd.ttl;
+ soa.d_place = DNSResourceRecord::ANSWER;
+ soa.d_content = makeSOAContent(edited);
+
+ DNSZoneRecord dzr;
+ dzr.auth = true;
+ dzr.dr = soa;
+
+ return dzr;
}
shared_ptr<DNSPacket> getFreshAXFRPacket(shared_ptr<DNSPacket> q)
// SOA *must* go out first, our signing pipe might reorder
DLOG(L<<"Sending out SOA"<<endl);
- DNSResourceRecord soa = makeDNSRRFromSOAData(sd);
- DNSZoneRecord dzrsoa;
- dzrsoa.auth=true;
- dzrsoa.dr=DNSRecord(soa);
-
- string kind;
- dk.getSoaEdit(sd.qname, kind);
- editSOARecord(dzrsoa, kind);
-
- outpacket->addRecord(dzrsoa);
+ DNSZoneRecord soa = makeEditedDNSZRFromSOAData(dk, sd);
+ outpacket->addRecord(soa);
if(securedZone && !presignedZone) {
set<DNSName> authSet;
authSet.insert(target);
DLOG(L<<"Done writing out records"<<endl);
/* and terminate with yet again the SOA record */
outpacket=getFreshAXFRPacket(q);
- outpacket->addRecord(dzrsoa);
+ outpacket->addRecord(soa);
if(haveTSIGDetails && !tsigkeyname.empty())
outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true);
return 0;
}
- string soaedit;
- dk.getSoaEdit(target, soaedit);
- if (!rfc1982LessThan(serial, calculateEditSOA(sd, soaedit))) {
+ if (!rfc1982LessThan(serial, calculateEditSOA(sd.serial, dk, sd.qname))) {
TSIGRecordContent trc;
DNSName tsigkeyname;
string tsigsecret;
// SOA *must* go out first, our signing pipe might reorder
DLOG(L<<"Sending out SOA"<<endl);
- DNSResourceRecord soa = makeDNSRRFromSOAData(sd);
- DNSZoneRecord dzrsoa;
- dzrsoa.dr=DNSRecord(soa);
- dzrsoa.auth=true;
-
- outpacket->addRecord(dzrsoa);
- editSOA(dk, sd.qname, outpacket.get());
+ DNSZoneRecord soa = makeEditedDNSZRFromSOAData(dk, sd);
+ outpacket->addRecord(soa);
if(securedZone) {
set<DNSName> authSet;
authSet.insert(target);
bool found = PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key);
BOOST_CHECK_EQUAL(found, false);
- PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0);
+ PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0, boost::none);
found = PC.get(dq, a.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, 0, true);
if (found == true) {
}
}
+BOOST_AUTO_TEST_CASE(test_PacketCacheServFailTTL) {
+ const size_t maxEntries = 150000;
+ DNSDistPacketCache PC(maxEntries, 86400, 1);
+
+ ComboAddress remote;
+ try {
+ DNSName a = DNSName("servfail");
+ BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
+
+ vector<uint8_t> query;
+ DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
+ pwQ.getHeader()->rd = 1;
+
+ vector<uint8_t> response;
+ DNSPacketWriter pwR(response, a, QType::A, QClass::IN, 0);
+ pwR.getHeader()->rd = 1;
+ pwR.getHeader()->ra = 0;
+ pwR.getHeader()->qr = 1;
+ pwR.getHeader()->rcode = RCode::ServFail;
+ pwR.getHeader()->id = pwQ.getHeader()->id;
+ pwR.commit();
+ uint16_t responseLen = response.size();
+
+ char responseBuf[4096];
+ uint16_t responseBufSize = sizeof(responseBuf);
+ uint32_t key = 0;
+ DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, (struct dnsheader*) query.data(), query.size(), query.size(), false);
+ bool found = PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key);
+ BOOST_CHECK_EQUAL(found, false);
+
+ // Insert with failure-TTL of 0 (-> should not enter cache).
+ PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, RCode::ServFail, boost::optional<uint32_t>(0));
+ found = PC.get(dq, a.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, 0, true);
+ BOOST_CHECK_EQUAL(found, false);
+
+ // Insert with failure-TTL non-zero (-> should enter cache).
+ PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, RCode::ServFail, boost::optional<uint32_t>(300));
+ found = PC.get(dq, a.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, 0, true);
+ BOOST_CHECK_EQUAL(found, true);
+ }
+ catch(PDNSException& e) {
+ cerr<<"Had error: "<<e.reason<<endl;
+ throw;
+ }
+}
+
static DNSDistPacketCache PC(500000);
static void *threadMangler(void* off)
DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, (struct dnsheader*) query.data(), query.size(), query.size(), false);
PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key);
- PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0);
+ PC.insert(key, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0, boost::none);
}
}
catch(PDNSException& e) {
BOOST_CHECK_EQUAL(unset.toString(), "www.powerdns\\.com.com.");
+ DNSName rfc4343_2_1("~!.example.");
DNSName rfc4343_2_2(R"(Donald\032E\.\032Eastlake\0323rd.example.)");
DNSName example("example.");
+ BOOST_CHECK(rfc4343_2_1.isPartOf(example));
BOOST_CHECK(rfc4343_2_2.isPartOf(example));
+ BOOST_CHECK_EQUAL(rfc4343_2_1.toString(), "~!.example.");
auto labels=rfc4343_2_2.getRawLabels();
BOOST_CHECK_EQUAL(*labels.begin(), "Donald E. Eastlake 3rd");
BOOST_CHECK_EQUAL(*labels.rbegin(), "example");
BOOST_CHECK_EQUAL(labels.size(), 2);
-
DNSName build;
build.appendRawLabel("Donald E. Eastlake 3rd");
build.appendRawLabel("example");
}
sort(vec.begin(), vec.end(), CanonDNSNameCompare());
// for(const auto& v : vec)
- // cerr<<'"'<<v.toString()<<'"'<<endl;
+ // cerr<<'"'<<v<<'"'<<endl;
vector<DNSName> right;
for(const auto& b: {"bert.com.", "Aleph1.powerdns.com.",
#include <boost/scoped_ptr.hpp>
#include "dnsrecords.hh"
-#define CASE_L(type, inval, zoneval, lineval, broken) case_t(type, std::string(inval), std::string(zoneval), std::string(lineval, sizeof(lineval)-1), broken)
-#define CASE_S(type, zoneval, lineval, broken) CASE_L(type, zoneval, zoneval, lineval, broken)
-BOOST_AUTO_TEST_SUITE(test_dnsrecords_cc)
+namespace {
+ enum class broken_marker {
+ WORKING,
+ BROKEN,
+ };
+}
-#define REC_CHECK_EQUAL(a,b) { if (val.get<4>()) { BOOST_WARN_EQUAL(a,b); } else { BOOST_CHECK_EQUAL(a,b); } }
-#define REC_CHECK_MESSAGE(cond,msg) { if (val.get<4>()) { BOOST_WARN_MESSAGE(cond,msg); } else { BOOST_CHECK_MESSAGE(cond,msg); } }
-#define REC_FAIL_XSUCCESS(msg) { if (val.get<4>()) { BOOST_CHECK_MESSAGE(false, std::string("Test has unexpectedly passed: ") + msg); } } // fail if test succeeds
-#define REC_FAIL_XSUCCESS2(msg) { if (val.get<3>()) { BOOST_CHECK_MESSAGE(false, std::string("Test has unexpectedly passed: ") + msg); } } // fail if test succeeds (for the bad records test case)
+// use a user-defined literal operator instead? should be supported in
+// C++11, but only C++14 added the `s` suffix.
+#define BINARY(s) (std::string(s, sizeof(s) - 1))
+
+#define _CASE_L(type, inval, zoneval, lineval, broken) case_t(type, BINARY(inval), BINARY(zoneval), BINARY(lineval), broken)
+#define CASE_L(type, inval, zoneval, lineval) _CASE_L(type, inval, zoneval, lineval, broken_marker::WORKING)
+#define CASE_S(type, zoneval, lineval) _CASE_L(type, zoneval, zoneval, lineval, broken_marker::WORKING)
+#define BROKEN_CASE_L(type, inval, zoneval, lineval) _CASE_L(type, inval, zoneval, lineval, broken_marker::BROKEN)
+#define BROKEN_CASE_S(type, zoneval, lineval) _CASE_L(type, zoneval, zoneval, lineval, broken_marker::BROKEN)
+BOOST_AUTO_TEST_SUITE(test_dnsrecords_cc)
-enum class case_type_enum_t { zone, wire };
-static const auto zone = case_type_enum_t::zone;
-static const auto wire = case_type_enum_t::wire;
+#define REC_CHECK_EQUAL(a,b) { if (broken_marker::BROKEN == broken) { BOOST_WARN_EQUAL(a,b); } else { BOOST_CHECK_EQUAL(a,b); } }
+#define REC_CHECK_MESSAGE(cond,msg) { if (broken_marker::BROKEN == broken) { BOOST_WARN_MESSAGE(cond,msg); } else { BOOST_CHECK_MESSAGE(cond,msg); } }
+#define REC_FAIL_XSUCCESS(msg) { if (broken_marker::BROKEN == broken) { BOOST_CHECK_MESSAGE(false, std::string("Test has unexpectedly passed: ") + msg); } } // fail if test succeeds
BOOST_AUTO_TEST_CASE(test_record_types) {
// tuple contains <type, user value, zone representation, line value, broken>
- typedef boost::tuple<const QType::typeenum, const std::string, const std::string, const std::string, bool> case_t;
+ typedef boost::tuple<QType::typeenum, std::string, std::string, std::string, broken_marker> case_t;
typedef std::list<case_t> cases_t;
reportAllTypes();
MRRecordContent::report();
// why yes, they are unordered by name, how nice of you to notice
- cases_t cases = boost::assign::list_of
- (CASE_S(QType::A, "127.0.0.1", "\x7F\x00\x00\x01",false))
+ const cases_t cases = boost::assign::list_of
+ (CASE_S(QType::A, "127.0.0.1", "\x7F\x00\x00\x01"))
// local nameserver
- (CASE_S(QType::NS, "ns.rec.test.", "\x02ns\xc0\x11",false))
+ (CASE_S(QType::NS, "ns.rec.test.", "\x02ns\xc0\x11"))
// non-local nameserver
- (CASE_S(QType::NS, "ns.example.com.", "\x02ns\x07""example\x03""com\x00",false))
+ (CASE_S(QType::NS, "ns.example.com.", "\x02ns\x07""example\x03""com\x00"))
// local alias
- (CASE_S(QType::CNAME, "name.rec.test.", "\x04name\xc0\x11",false))
+ (CASE_S(QType::CNAME, "name.rec.test.", "\x04name\xc0\x11"))
// non-local alias
- (CASE_S(QType::CNAME, "name.example.com.", "\x04name\x07""example\x03""com\x00",false))
+ (CASE_S(QType::CNAME, "name.example.com.", "\x04name\x07""example\x03""com\x00"))
// max label length (63)
- (CASE_S(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.example.com.", "\x3f""123456789012345678901234567890123456789012345678901234567890123\x07""example\x03""com\x00", false))
+ (CASE_S(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.example.com.", "\x3f""123456789012345678901234567890123456789012345678901234567890123\x07""example\x03""com\x00"))
// local max name length (255)
- (CASE_S(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012.rec.test.", "\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x34""1234567890123456789012345678901234567890123456789012\xc0\x11", false))
+ (CASE_S(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012.rec.test.", "\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x34""1234567890123456789012345678901234567890123456789012\xc0\x11"))
// non-local max name length (255)
- (CASE_S(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901.", "\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x3d""1234567890123456789012345678901234567890123456789012345678901\x00", false))
+ (CASE_S(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901.", "\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x3f""123456789012345678901234567890123456789012345678901234567890123\x3d""1234567890123456789012345678901234567890123456789012345678901\x00"))
// local names
- (CASE_S(QType::SOA, "ns.rec.test. hostmaster.test.rec. 2013051201 3600 3600 604800 120", "\x02ns\xc0\x11\x0ahostmaster\x04test\x03rec\x00\x77\xfc\xb9\x41\x00\x00\x0e\x10\x00\x00\x0e\x10\x00\x09\x3a\x80\x00\x00\x00\x78",false))
+ (CASE_S(QType::SOA, "ns.rec.test. hostmaster.test.rec. 2013051201 3600 3600 604800 120", "\x02ns\xc0\x11\x0ahostmaster\x04test\x03rec\x00\x77\xfc\xb9\x41\x00\x00\x0e\x10\x00\x00\x0e\x10\x00\x09\x3a\x80\x00\x00\x00\x78"))
// non-local names
- (CASE_S(QType::SOA, "ns.example.com. hostmaster.example.com. 2013051201 3600 3600 604800 120", "\x02ns\x07""example\x03""com\x00\x0ahostmaster\xc0\x28\x77\xfc\xb9\x41\x00\x00\x0e\x10\x00\x00\x0e\x10\x00\x09\x3a\x80\x00\x00\x00\x78",false))
+ (CASE_S(QType::SOA, "ns.example.com. hostmaster.example.com. 2013051201 3600 3600 604800 120", "\x02ns\x07""example\x03""com\x00\x0ahostmaster\xc0\x28\x77\xfc\xb9\x41\x00\x00\x0e\x10\x00\x00\x0e\x10\x00\x09\x3a\x80\x00\x00\x00\x78"))
-// BROKEN TESTS (2) (deprecated)
// local name
- (CASE_S(QType::MR, "newmailbox.rec.test.", "\x0anewmailbox\xc0\x11",false))
+ (CASE_S(QType::MR, "newmailbox.rec.test.", "\x0anewmailbox\xc0\x11"))
// non-local name
- (CASE_S(QType::MR, "newmailbox.example.com.", "\x0anewmailbox\x07""example\x03""com\x00",false))
+ (CASE_S(QType::MR, "newmailbox.example.com.", "\x0anewmailbox\x07""example\x03""com\x00"))
// local name
- (CASE_S(QType::PTR, "ptr.rec.test.", "\x03ptr\xc0\x11",false))
+ (CASE_S(QType::PTR, "ptr.rec.test.", "\x03ptr\xc0\x11"))
// non-local name
- (CASE_S(QType::PTR, "ptr.example.com.", "\x03ptr\x07""example\x03""com\x00",false))
- (CASE_S(QType::HINFO, "\"i686\" \"Linux\"", "\x04i686\x05Linux",false))
- (CASE_L(QType::HINFO, "i686 \"Linux\"", "\"i686\" \"Linux\"", "\x04i686\x05Linux",true))
- (CASE_L(QType::HINFO, "\"i686\" Linux", "\"i686\" \"Linux\"", "\x04i686\x05Linux",false))
- (CASE_L(QType::HINFO, "i686 Linux", "\"i686\" \"Linux\"", "\x04i686\x05Linux",true))
+ (CASE_S(QType::PTR, "ptr.example.com.", "\x03ptr\x07""example\x03""com\x00"))
+ (CASE_S(QType::HINFO, "\"i686\" \"Linux\"", "\x04i686\x05Linux"))
+ (BROKEN_CASE_L(QType::HINFO, "i686 \"Linux\"", "\"i686\" \"Linux\"", "\x04i686\x05Linux"))
+ (CASE_L(QType::HINFO, "\"i686\" Linux", "\"i686\" \"Linux\"", "\x04i686\x05Linux"))
+ (BROKEN_CASE_L(QType::HINFO, "i686 Linux", "\"i686\" \"Linux\"", "\x04i686\x05Linux"))
// local name
- (CASE_S(QType::MX, "10 mx.rec.test.", "\x00\x0a\02mx\xc0\x11",false))
+ (CASE_S(QType::MX, "10 mx.rec.test.", "\x00\x0a\x02mx\xc0\x11"))
// non-local name
- (CASE_S(QType::MX, "20 mx.example.com.", "\x00\x14\02mx\x07""example\x03""com\x00",false))
+ (CASE_S(QType::MX, "20 mx.example.com.", "\x00\x14\x02mx\x07""example\x03""com\x00"))
// root label
- (CASE_S(QType::MX, "20 .", "\x00\x14\x00",false))
-
- (CASE_S(QType::TXT, "\"short text\"", "\x0ashort text",false))
- (CASE_S(QType::TXT, "\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\"", "\xff""long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a""2222222222",false))
- (CASE_L(QType::TXT, "\"long record test 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222\"", "\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\"", "\xff""long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a""2222222222",false))
- (CASE_S(QType::TXT, "\"\\195\\133LAND ISLANDS\"", "\x0e\xc3\x85LAND ISLANDS", false))
- (CASE_L(QType::TXT, "\"\xc3\x85LAND ISLANDS\"", "\"\\195\\133LAND ISLANDS\"", "\x0e\xc3\x85LAND ISLANDS", false))
- (CASE_S(QType::TXT, "\"nonbreakingtxt\"", "\x0enonbreakingtxt",false))
+ (CASE_S(QType::MX, "20 .", "\x00\x14\x00"))
+
+ (CASE_S(QType::TXT, "\"short text\"", "\x0ashort text"))
+ (CASE_S(QType::TXT, "\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\"", "\xff""long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a""2222222222"))
+ (CASE_L(QType::TXT, "\"long record test 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222\"", "\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\"", "\xff""long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a""2222222222"))
+ (CASE_S(QType::TXT, "\"\\195\\133LAND ISLANDS\"", "\x0e\xc3\x85LAND ISLANDS"))
+ (CASE_L(QType::TXT, "\"\xc3\x85LAND ISLANDS\"", "\"\\195\\133LAND ISLANDS\"", "\x0e\xc3\x85LAND ISLANDS"))
+ (CASE_S(QType::TXT, "\"nonbreakingtxt\"", "\x0enonbreakingtxt"))
// local name
- (CASE_S(QType::RP, "admin.rec.test. admin-info.rec.test.", "\x05""admin\x03rec\x04test\x00\x0a""admin-info\x03rec\x04test\x00",false))
+ (CASE_S(QType::RP, "admin.rec.test. admin-info.rec.test.", "\x05""admin\x03rec\x04test\x00\x0a""admin-info\x03rec\x04test\x00"))
// non-local name
- (CASE_S(QType::RP, "admin.example.com. admin-info.example.com.", "\x05""admin\x07""example\x03""com\x00\x0a""admin-info\x07""example\x03""com\x00",false))
+ (CASE_S(QType::RP, "admin.example.com. admin-info.example.com.", "\x05""admin\x07""example\x03""com\x00\x0a""admin-info\x07""example\x03""com\x00"))
// local name
- (CASE_S(QType::AFSDB, "1 afs-server.rec.test.", "\x00\x01\x0a""afs-server\x03rec\x04test\x00",false))
+ (CASE_S(QType::AFSDB, "1 afs-server.rec.test.", "\x00\x01\x0a""afs-server\x03rec\x04test\x00"))
// non-local name
- (CASE_S(QType::AFSDB, "1 afs-server.example.com.", "\x00\x01\x0a""afs-server\x07""example\x03""com\x00",false))
-// deprecated (and i don't know what i am doing wrong)
- (CASE_S(QType::KEY, "0 3 3 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x00\x00\x03\x03\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52",false))
- (CASE_L(QType::LOC, "32 7 19 S 116 2 25 E", "32 7 19.000 S 116 2 25.000 E 0.00m 1.00m 10000.00m 10.00m", "\x00\x12\x16\x13\x79\x1b\x7d\x28\x98\xe6\x48\x68\x00\x98\x96\x80",false))
- (CASE_L(QType::LOC, "32 7 19 S 116 2 25 E 10m", "32 7 19.000 S 116 2 25.000 E 10.00m 1.00m 10000.00m 10.00m", "\x00\x12\x16\x13\x79\x1b\x7d\x28\x98\xe6\x48\x68\x00\x98\x9a\x68",false))
- (CASE_L(QType::LOC, "42 21 54 N 71 06 18 W -24m 30m", "42 21 54.000 N 71 6 18.000 W -24.00m 30.00m 10000.00m 10.00m", "\x00\x33\x16\x13\x89\x17\x2d\xd0\x70\xbe\x15\xf0\x00\x98\x8d\x20",false))
- (CASE_L(QType::LOC, "42 21 43.952 N 71 5 6.344 W -24m 1m 200m", "42 21 43.952 N 71 5 6.344 W -24.00m 1.00m 200.00m 10.00m", "\x00\x12\x24\x13\x89\x17\x06\x90\x70\xbf\x2d\xd8\x00\x98\x8d\x20",false))
- (CASE_S(QType::AAAA, "fe80::250:56ff:fe9b:114", "\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14",false))
- (CASE_S(QType::AAAA, "2a02:1b8:10:2::151", "\x2a\x02\x01\xb8\x00\x10\x00\x02\x00\x00\x00\x00\x00\x00\x01\x51",false))
- (CASE_S(QType::AAAA, "::1", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",false))
+ (CASE_S(QType::AFSDB, "1 afs-server.example.com.", "\x00\x01\x0a""afs-server\x07""example\x03""com\x00"))
+ (CASE_S(QType::KEY, "0 3 3 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x00\x00\x03\x03\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52"))
+ (CASE_S(QType::AAAA, "fe80::250:56ff:fe9b:114", "\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14"))
+ (CASE_S(QType::AAAA, "2a02:1b8:10:2::151", "\x2a\x02\x01\xb8\x00\x10\x00\x02\x00\x00\x00\x00\x00\x00\x01\x51"))
+ (CASE_S(QType::AAAA, "::1", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"))
+ (CASE_L(QType::LOC, "32 7 19 S 116 2 25 E", "32 7 19.000 S 116 2 25.000 E 0.00m 1.00m 10000.00m 10.00m", "\x00\x12\x16\x13\x79\x1b\x7d\x28\x98\xe6\x48\x68\x00\x98\x96\x80"))
+ (CASE_L(QType::LOC, "32 7 19 S 116 2 25 E 10m", "32 7 19.000 S 116 2 25.000 E 10.00m 1.00m 10000.00m 10.00m", "\x00\x12\x16\x13\x79\x1b\x7d\x28\x98\xe6\x48\x68\x00\x98\x9a\x68"))
+ (CASE_L(QType::LOC, "42 21 54 N 71 06 18 W -24m 30m", "42 21 54.000 N 71 6 18.000 W -24.00m 30.00m 10000.00m 10.00m", "\x00\x33\x16\x13\x89\x17\x2d\xd0\x70\xbe\x15\xf0\x00\x98\x8d\x20"))
+ (CASE_L(QType::LOC, "42 21 43.952 N 71 5 6.344 W -24m 1m 200m", "42 21 43.952 N 71 5 6.344 W -24.00m 1.00m 200.00m 10.00m", "\x00\x12\x24\x13\x89\x17\x06\x90\x70\xbf\x2d\xd8\x00\x98\x8d\x20"))
// local name
- (CASE_S(QType::SRV, "10 10 5060 sip.rec.test.", "\x00\x0a\x00\x0a\x13\xc4\x03sip\x03rec\x04test\x00",false))
+ (CASE_S(QType::SRV, "10 10 5060 sip.rec.test.", "\x00\x0a\x00\x0a\x13\xc4\x03sip\x03rec\x04test\x00"))
// non-local name
- (CASE_S(QType::SRV, "10 10 5060 sip.example.com.", "\x00\x0a\x00\x0a\x13\xc4\x03sip\x07""example\x03""com\x00",false))
+ (CASE_S(QType::SRV, "10 10 5060 sip.example.com.", "\x00\x0a\x00\x0a\x13\xc4\x03sip\x07""example\x03""com\x00"))
// root name
- (CASE_S(QType::SRV, "10 10 5060 .", "\x00\x0a\x00\x0a\x13\xc4\x00",false))
+ (CASE_S(QType::SRV, "10 10 5060 .", "\x00\x0a\x00\x0a\x13\xc4\x00"))
- (CASE_S(QType::NAPTR, "100 10 \"\" \"\" \"/urn:cid:.+@([^\\\\.]+\\\\.)(.*)$/\\\\2/i\" .", "\x00\x64\x00\x0a\x00\x00\x20/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\x00",false))
- (CASE_S(QType::NAPTR, "100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.rec.test.", "\x00\x64\x00\x32\x01s\x10http+I2L+I2C+I2R\x00\x05_http\x04_tcp\x03rec\x04test\x00",false))
- (CASE_S(QType::KX, "10 mail.rec.test.", "\x00\x0a\x04mail\x03rec\x04test\x00",false))
+ (CASE_S(QType::NAPTR, "100 10 \"\" \"\" \"/urn:cid:.+@([^\\\\.]+\\\\.)(.*)$/\\\\2/i\" .", "\x00\x64\x00\x0a\x00\x00\x20/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\x00"))
+ (CASE_S(QType::NAPTR, "100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.rec.test.", "\x00\x64\x00\x32\x01s\x10http+I2L+I2C+I2R\x00\x05_http\x04_tcp\x03rec\x04test\x00"))
+ (CASE_S(QType::KX, "10 mail.rec.test.", "\x00\x0a\x04mail\x03rec\x04test\x00"))
// X.509 as per PKIX
- (CASE_S(QType::CERT, "1 0 0 MIIB9DCCAV2gAwIBAgIJAKxUfFVXhw7HMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNVBAMMCHJlYy50ZXN0MB4XDTEzMDUxMjE5NDgwOVoXDTEzMDYxMTE5NDgwOVowEzERMA8GA1UEAwwIcmVjLnRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANKCu5aN/ewOXRPfzAo27XMXhYFCThCjfInTAUIEkzs6jBFZ/eyyIa/kFoiD0tAKwfFfykYU+9XgXeLjetD7rYt3SN3bzzCznoBGbGHHM0Fecrn0LV+tC/NfBB61Yx7e0AMUxmxIeLNRQW5ca5CW8qcIiiQ4fl0BScUjc5+E9QLHAgMBAAGjUDBOMB0GA1UdDgQWBBRzcVu/2bwrgkES+FhYbxZqr7mUgjAfBgNVHSMEGDAWgBRzcVu/2bwrgkES+FhYbxZqr7mUgjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFVQ8dZBOasOhsWzA/xpAV0WdsqVkxBxrkGIRlbHHBFqOBOOz2MFSzUNx4mDy0qDKI28gcWmWaVsxoQ9VFLD6YRJuUoM8MDNcZDJbKpfDumjvvfnUAK+SiM2c4Ur3xpf0wanCA60/q2bOtFiB0tfAH6RVuIgMC3qjHAIaKEld+fE", "\x00\x01\x00\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_L(QType::DS, "20642 8 2 04443ABE7E94C3985196BEAE5D548C727B044DDA5151E60D7CD76A9F D931D00E", "20642 8 2 04443abe7e94c3985196beae5d548c727b044dda5151e60d7cd76a9fd931d00e", "\x50\xa2\x08\x02\x04\x44\x3a\xbe\x7e\x94\xc3\x98\x51\x96\xbe\xae\x5d\x54\x8c\x72\x7b\x04\x4d\xda\x51\x51\xe6\x0d\x7c\xd7\x6a\x9f\xd9\x31\xd0\x0e",false))
- (CASE_S(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88",false))
-// as per RFC4025
- (CASE_L(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc253 8d88", "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88",false))
+ (CASE_S(QType::CERT, "1 0 0 MIIB9DCCAV2gAwIBAgIJAKxUfFVXhw7HMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNVBAMMCHJlYy50ZXN0MB4XDTEzMDUxMjE5NDgwOVoXDTEzMDYxMTE5NDgwOVowEzERMA8GA1UEAwwIcmVjLnRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANKCu5aN/ewOXRPfzAo27XMXhYFCThCjfInTAUIEkzs6jBFZ/eyyIa/kFoiD0tAKwfFfykYU+9XgXeLjetD7rYt3SN3bzzCznoBGbGHHM0Fecrn0LV+tC/NfBB61Yx7e0AMUxmxIeLNRQW5ca5CW8qcIiiQ4fl0BScUjc5+E9QLHAgMBAAGjUDBOMB0GA1UdDgQWBBRzcVu/2bwrgkES+FhYbxZqr7mUgjAfBgNVHSMEGDAWgBRzcVu/2bwrgkES+FhYbxZqr7mUgjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFVQ8dZBOasOhsWzA/xpAV0WdsqVkxBxrkGIRlbHHBFqOBOOz2MFSzUNx4mDy0qDKI28gcWmWaVsxoQ9VFLD6YRJuUoM8MDNcZDJbKpfDumjvvfnUAK+SiM2c4Ur3xpf0wanCA60/q2bOtFiB0tfAH6RVuIgMC3qjHAIaKEld+fE", "\x00\x01\x00\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_L(QType::DS, "20642 8 2 04443ABE7E94C3985196BEAE5D548C727B044DDA5151E60D7CD76A9F D931D00E", "20642 8 2 04443abe7e94c3985196beae5d548c727b044dda5151e60d7cd76a9fd931d00e", "\x50\xa2\x08\x02\x04\x44\x3a\xbe\x7e\x94\xc3\x98\x51\x96\xbe\xae\x5d\x54\x8c\x72\x7b\x04\x4d\xda\x51\x51\xe6\x0d\x7c\xd7\x6a\x9f\xd9\x31\xd0\x0e"))
+ (CASE_S(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88"))
+ (CASE_L(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc253 8d88", "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88"))
// as per RFC4025
- (CASE_S(QType::IPSECKEY, "255 0 0", "\xff\x00\x00",false))
- (CASE_S(QType::IPSECKEY, "255 0 1 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\xff\x00\x01\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52",false))
- (CASE_S(QType::IPSECKEY, "255 1 0 127.0.0.1", "\xff\x01\x00\x7f\x00\x00\x01", false))
- (CASE_S(QType::IPSECKEY, "255 2 0 fe80::250:56ff:fe9b:114", "\xff\x02\x00\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14", false))
- (CASE_S(QType::IPSECKEY, "10 1 1 127.0.0.1 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x0a\x01\x01\x7f\x00\x00\x01\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52", false))
- (CASE_S(QType::IPSECKEY, "10 2 1 fe80::250:56ff:fe9b:114 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x0a\x02\x01\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52", false))
- (CASE_S(QType::IPSECKEY, "10 3 1 gw.rec.test. V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x0a\x03\x01\x02gw\x03rec\x04test\x00\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52",false))
-
- (CASE_S(QType::RRSIG, "SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=", "\x00\x06\x08\x03\x00\x00\x01\x2c\x51\x9d\x5c\x00\x51\x8a\xe7\x00\xd3\xc8\x03\x72\x65\x63\x04\x74\x65\x73\x74\x00\x79\xc5\x8a\x0f\xf3\xac\x74\x08\x97\xa5\xb3\x3f\xb2\x03\xd3\xf3\x62\x95\x0f\xf5\xa2\x42\x79\xea\x73\x1a\x09\x82\x21\xf7\x8b\x11\xda\xf8\xb3\x80\x71\x85\x3b\x14\x6e\xd5\xe0\x14\x51\x27\x12\xeb\x89\x8d\x5e\xd2\xb0\x76\x80\x0b\x37\x90\x27\xa5\xf4\x3e\x1b\xcd\xf2\xba\xc0\x0a\x86\xe0\x38\xbf\x78\x75\x20\x1e\x9b\x51\x95\xa4\x76\xc9\xc1\x0b\x27\x8b\x75\xa6\x3c\xeb\x9e\xe1\xa2\x23\x30\x35\xc0\xcd\xa2\xcd\x19\x9e\x13\xe5\xe8\xcb\x5d\xfa\xed\x0b\xb4\xa4\xec\xf9\x0c\xbc\xd3\x7d\x02\x13\x58\x5b\x01\xda\x25\x33\x37\x86",false))
+ (CASE_S(QType::IPSECKEY, "255 0 0", "\xff\x00\x00"))
+ (CASE_S(QType::IPSECKEY, "255 0 1 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\xff\x00\x01\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52"))
+ (CASE_S(QType::IPSECKEY, "255 1 0 127.0.0.1", "\xff\x01\x00\x7f\x00\x00\x01"))
+ (CASE_S(QType::IPSECKEY, "255 2 0 fe80::250:56ff:fe9b:114", "\xff\x02\x00\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14"))
+ (CASE_S(QType::IPSECKEY, "10 1 1 127.0.0.1 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x0a\x01\x01\x7f\x00\x00\x01\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52"))
+ (CASE_S(QType::IPSECKEY, "10 2 1 fe80::250:56ff:fe9b:114 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x0a\x02\x01\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52"))
+ (CASE_S(QType::IPSECKEY, "10 3 1 gw.rec.test. V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==", "\x0a\x03\x01\x02gw\x03rec\x04test\x00\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52"))
+
+ (CASE_S(QType::RRSIG, "SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=", "\x00\x06\x08\x03\x00\x00\x01\x2c\x51\x9d\x5c\x00\x51\x8a\xe7\x00\xd3\xc8\x03\x72\x65\x63\x04\x74\x65\x73\x74\x00\x79\xc5\x8a\x0f\xf3\xac\x74\x08\x97\xa5\xb3\x3f\xb2\x03\xd3\xf3\x62\x95\x0f\xf5\xa2\x42\x79\xea\x73\x1a\x09\x82\x21\xf7\x8b\x11\xda\xf8\xb3\x80\x71\x85\x3b\x14\x6e\xd5\xe0\x14\x51\x27\x12\xeb\x89\x8d\x5e\xd2\xb0\x76\x80\x0b\x37\x90\x27\xa5\xf4\x3e\x1b\xcd\xf2\xba\xc0\x0a\x86\xe0\x38\xbf\x78\x75\x20\x1e\x9b\x51\x95\xa4\x76\xc9\xc1\x0b\x27\x8b\x75\xa6\x3c\xeb\x9e\xe1\xa2\x23\x30\x35\xc0\xcd\xa2\xcd\x19\x9e\x13\xe5\xe8\xcb\x5d\xfa\xed\x0b\xb4\xa4\xec\xf9\x0c\xbc\xd3\x7d\x02\x13\x58\x5b\x01\xda\x25\x33\x37\x86"))
// timestamps can be given as (32-bit wrapped) epoch too:
- (CASE_L(QType::RRSIG, "SOA 8 3 300 1369267200 1368057600 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=", "SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=", "\x00\x06\x08\x03\x00\x00\x01\x2c\x51\x9d\x5c\x00\x51\x8a\xe7\x00\xd3\xc8\x03\x72\x65\x63\x04\x74\x65\x73\x74\x00\x79\xc5\x8a\x0f\xf3\xac\x74\x08\x97\xa5\xb3\x3f\xb2\x03\xd3\xf3\x62\x95\x0f\xf5\xa2\x42\x79\xea\x73\x1a\x09\x82\x21\xf7\x8b\x11\xda\xf8\xb3\x80\x71\x85\x3b\x14\x6e\xd5\xe0\x14\x51\x27\x12\xeb\x89\x8d\x5e\xd2\xb0\x76\x80\x0b\x37\x90\x27\xa5\xf4\x3e\x1b\xcd\xf2\xba\xc0\x0a\x86\xe0\x38\xbf\x78\x75\x20\x1e\x9b\x51\x95\xa4\x76\xc9\xc1\x0b\x27\x8b\x75\xa6\x3c\xeb\x9e\xe1\xa2\x23\x30\x35\xc0\xcd\xa2\xcd\x19\x9e\x13\xe5\xe8\xcb\x5d\xfa\xed\x0b\xb4\xa4\xec\xf9\x0c\xbc\xd3\x7d\x02\x13\x58\x5b\x01\xda\x25\x33\x37\x86",false))
- (CASE_S(QType::NSEC, "a.rec.test. A NS SOA MX AAAA RRSIG NSEC DNSKEY", "\x01""a\x03rec\x04test\x00\x00\x07\x62\x01\x00\x08\x00\x03\x80",false))
- (CASE_S(QType::DNSKEY, "257 3 5 AwEAAZVtlHc8O4TVmlGx/PGJTc7hbVjMR7RywxLuAm1dqgyHvgNRD7chYLsALOdZKW6VRvusbyhoOPilnh8XpucBDqjGD6lIemsURz7drZEqcLupVA0TPxXABZ6auJ3jumqIhSOcLj9rpSwI4xuWt0yu6LR9tL2q8+A0yEZxcAaKS+Wq0fExJ93NxgXl1/fY+JcYQvonjd31GxXXef9uf0exXyzowh5h8+IIBETU+ZiYVB5BqiwkICZL/OX57idm99ycA2/tIen66F8u2ueTvgPcecnoqHvW0MtLQKzeNmqdGNthHhV5di0SZdMZQeo/izs68uN2WzqQDZy9Ec2JwBTbxWE=", "\x01\x01\x03\x05\x03\x01\x00\x01\x95\x6d\x94\x77\x3c\x3b\x84\xd5\x9a\x51\xb1\xfc\xf1\x89\x4d\xce\xe1\x6d\x58\xcc\x47\xb4\x72\xc3\x12\xee\x02\x6d\x5d\xaa\x0c\x87\xbe\x03\x51\x0f\xb7\x21\x60\xbb\x00\x2c\xe7\x59\x29\x6e\x95\x46\xfb\xac\x6f\x28\x68\x38\xf8\xa5\x9e\x1f\x17\xa6\xe7\x01\x0e\xa8\xc6\x0f\xa9\x48\x7a\x6b\x14\x47\x3e\xdd\xad\x91\x2a\x70\xbb\xa9\x54\x0d\x13\x3f\x15\xc0\x05\x9e\x9a\xb8\x9d\xe3\xba\x6a\x88\x85\x23\x9c\x2e\x3f\x6b\xa5\x2c\x08\xe3\x1b\x96\xb7\x4c\xae\xe8\xb4\x7d\xb4\xbd\xaa\xf3\xe0\x34\xc8\x46\x71\x70\x06\x8a\x4b\xe5\xaa\xd1\xf1\x31\x27\xdd\xcd\xc6\x05\xe5\xd7\xf7\xd8\xf8\x97\x18\x42\xfa\x27\x8d\xdd\xf5\x1b\x15\xd7\x79\xff\x6e\x7f\x47\xb1\x5f\x2c\xe8\xc2\x1e\x61\xf3\xe2\x08\x04\x44\xd4\xf9\x98\x98\x54\x1e\x41\xaa\x2c\x24\x20\x26\x4b\xfc\xe5\xf9\xee\x27\x66\xf7\xdc\x9c\x03\x6f\xed\x21\xe9\xfa\xe8\x5f\x2e\xda\xe7\x93\xbe\x03\xdc\x79\xc9\xe8\xa8\x7b\xd6\xd0\xcb\x4b\x40\xac\xde\x36\x6a\x9d\x18\xdb\x61\x1e\x15\x79\x76\x2d\x12\x65\xd3\x19\x41\xea\x3f\x8b\x3b\x3a\xf2\xe3\x76\x5b\x3a\x90\x0d\x9c\xbd\x11\xcd\x89\xc0\x14\xdb\xc5\x61",false))
-
- (CASE_S(QType::DHCID, "AAAB92JtyyO73zqENgu9LVua+0PZoeCcKapTw4asbYmx5F4=", "\x00\x00\x01\xf7\x62\x6d\xcb\x23\xbb\xdf\x3a\x84\x36\x0b\xbd\x2d\x5b\x9a\xfb\x43\xd9\xa1\xe0\x9c\x29\xaa\x53\xc3\x86\xac\x6d\x89\xb1\xe4\x5e",false))
- (CASE_S(QType::DHCID, "AAEB92JtyyO73zqENgu9LVua+0PZoeCcKapTw4asbYmx5F4=", "\x00\x01\x01\xf7\x62\x6d\xcb\x23\xbb\xdf\x3a\x84\x36\x0b\xbd\x2d\x5b\x9a\xfb\x43\xd9\xa1\xe0\x9c\x29\xaa\x53\xc3\x86\xac\x6d\x89\xb1\xe4\x5e",false))
- (CASE_S(QType::DHCID, "AAIB92JtyyO73zqENgu9LVua+0PZoeCcKapTw4asbYmx5F4=", "\x00\x02\x01\xf7\x62\x6d\xcb\x23\xbb\xdf\x3a\x84\x36\x0b\xbd\x2d\x5b\x9a\xfb\x43\xd9\xa1\xe0\x9c\x29\xaa\x53\xc3\x86\xac\x6d\x89\xb1\xe4\x5e",false))
-
- (CASE_S(QType::NSEC3, "1 1 1 f00b RPF1JGFCCNFA7STPTIJ9FPFNM40A4FLL NS SOA RRSIG DNSKEY NSEC3PARAM", "\x01\x01\x00\x01\x02\xf0\x0b\x14\xde\x5e\x19\xc1\xec\x65\xde\xa3\xf3\xb9\xec\xa6\x97\xe5\xf7\xb1\x00\xa2\x3e\xb5\x00\x07\x22\x00\x00\x00\x00\x02\x90",false))
- (CASE_S(QType::NSEC3PARAM, "1 0 1 f00b", "\x01\x00\x00\x01\x02\xf0\x0b",false))
-
- (CASE_S(QType::TLSA, "0 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x00\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::TLSA, "0 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x00\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
- (CASE_S(QType::TLSA, "1 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x01\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::TLSA, "1 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x01\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
- (CASE_S(QType::TLSA, "1 0 1 6acea2f68b03d9efe97a967e137aca6ac3a89490d532d87806d9e9c257668453", "\x01\x00\x01\x6a\xce\xa2\xf6\x8b\x03\xd9\xef\xe9\x7a\x96\x7e\x13\x7a\xca\x6a\xc3\xa8\x94\x90\xd5\x32\xd8\x78\x06\xd9\xe9\xc2\x57\x66\x84\x53",false))
- (CASE_S(QType::TLSA, "1 0 2 e6dce237992803488d11d828b7728deddd4577de73d7d078338c8a45880beddff98e076a28bf8e3068da8e73667b802a721c95d7323b038c60200a430cb6fbd4", "\x01\x00\x02\xe6\xdc\xe2\x37\x99\x28\x03\x48\x8d\x11\xd8\x28\xb7\x72\x8d\xed\xdd\x45\x77\xde\x73\xd7\xd0\x78\x33\x8c\x8a\x45\x88\x0b\xed\xdf\xf9\x8e\x07\x6a\x28\xbf\x8e\x30\x68\xda\x8e\x73\x66\x7b\x80\x2a\x72\x1c\x95\xd7\x32\x3b\x03\x8c\x60\x20\x0a\x43\x0c\xb6\xfb\xd4",false))
- (CASE_S(QType::TLSA, "2 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x02\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::TLSA, "2 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x02\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
-
- (CASE_S(QType::TLSA, "3 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x03\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::TLSA, "3 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x03\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
-
- (CASE_S(QType::SMIMEA, "0 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x00\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::SMIMEA, "0 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x00\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
- (CASE_S(QType::SMIMEA, "1 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x01\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::SMIMEA, "1 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x01\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
- (CASE_S(QType::SMIMEA, "1 0 1 6acea2f68b03d9efe97a967e137aca6ac3a89490d532d87806d9e9c257668453", "\x01\x00\x01\x6a\xce\xa2\xf6\x8b\x03\xd9\xef\xe9\x7a\x96\x7e\x13\x7a\xca\x6a\xc3\xa8\x94\x90\xd5\x32\xd8\x78\x06\xd9\xe9\xc2\x57\x66\x84\x53",false))
- (CASE_S(QType::SMIMEA, "1 0 2 e6dce237992803488d11d828b7728deddd4577de73d7d078338c8a45880beddff98e076a28bf8e3068da8e73667b802a721c95d7323b038c60200a430cb6fbd4", "\x01\x00\x02\xe6\xdc\xe2\x37\x99\x28\x03\x48\x8d\x11\xd8\x28\xb7\x72\x8d\xed\xdd\x45\x77\xde\x73\xd7\xd0\x78\x33\x8c\x8a\x45\x88\x0b\xed\xdf\xf9\x8e\x07\x6a\x28\xbf\x8e\x30\x68\xda\x8e\x73\x66\x7b\x80\x2a\x72\x1c\x95\xd7\x32\x3b\x03\x8c\x60\x20\x0a\x43\x0c\xb6\xfb\xd4",false))
- (CASE_S(QType::SMIMEA, "2 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x02\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::SMIMEA, "2 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x02\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
- (CASE_S(QType::SMIMEA, "3 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x03\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4",false))
- (CASE_S(QType::SMIMEA, "3 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x03\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01",false))
-
- (CASE_S(QType::OPENPGPKEY, "mQINBFUIXh0BEADNPlL6NpWEaR2KJx6p19scIVpsBIo7UqzCIzeFbRJaGDhn/HlQgcwAalcVNmWUX0ZQsrdn9CEfLWuFu9ON2o1TslYiwn+oSAlH2raFm2eyJTp/iM7IUUCte5jmf3d+L9rjVI7JjmMnbVo6SVY2KDDD72dULcg7IqYcCAN4CT+tPZP5y4cYf+DxRlpxhxvqqiGyAi6lAcJ24/8fJ4hsG0lS1vU12LWeWTHa5aRMM+x9kmv3GYdXG+FxFqZw52kZEnAscpC2ymbX+1YFCr8sjGYGde/D+5cLvuu4PGNZ4fkSeS+0yXve/s6u1mX6RkkF6SOGWuJfBJOGdWzYwber9kqgqpHTjpr8HOybzVroBijtTlB/tommIUd4BTk9Jv4fv2gA4UkC13UM9KBF1NnzUnKC+Js49O3mj0HZDoCrkWMnZyDsEmhMyQPU6YRFHWmB6OTKeD/Znk+b1uz+HIBgrbNuiG/A0c00Vnj7lR4p94oOuypI00XusLsJwPsjI4EgFGKdoRtM0spJhi+3gf88Vq0NENBaFVHLBGWVFaVrffurGcDZYUAdnvm8jSPCgBPfFxpZutexNkLjyaaXjDtga5/n5gSd/3RpWCvp9u3W5jcTNDZF4TORnOXUWHcot/+XmyH8/+cn8ydt0prOLGQ+FtdI+AWyMCXHen6aaZ1jeSLZqwARAQABtFpwZG5zIHJlZ3Jlc3Npb24gdGVzdGluZyBrZXkgKG9ubHkgZm9yIHRlc3RpbmcgdGhlIG9wZW5wZ3BrZXkgcnIpIDxyZWdyZXNzaW9uQHBvd2VyZG5zLm9yZz6JAjcEEwEIACEFAlUIXh0CGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQPr/KqoyK2Z7A6Q//YOBu8nwt+fguSo/vyCln0PqnTiBm4RvE2gPDUnsKuoXoP5F56XHBXKl9kEgmycht/nc7c7NRHzUhacM2RQau6CgNZE8KLaqDTKlEuc/ANtrnGGYG8gMId4TlzU5taLEA8yrHIHnwnMuDDpx1a0ETkbYCrj7CynqdhXCABqFjgRL7Qb37UnLPE7YdFt7fRGwZVLnb3GVZLKHurZ0TANvLdRVDST3f0lCcYMppPbHAvi2MIU71FPGkms++tj4gTltq0VRvrMNm1e5v4+hHZ++QN4sm4+DJGlo7l850gnMXc7c7GkRGtg8gV0h5k5jX5icdgxyvENTuBQ+QprkYTRh9uYzpoTQ+NYRZlgaJxxaDIv1K/kb3oPtnAEKJBC02IZbB0EiS3R5pxYXhUNoWV7ez2A4hX1L+tfvlgCAbbQ/cBLvqXgpgsf9x4ygSi52vQBy3twZyrtLsogxacxADfPcyleHtju/+lSku+Z6+W6OojA0kY2HlaMyQATJLIXd+6NE/tYy70RsU9Oq0OyVTjxh21SPLsExeSwSfciVSLn7IuKGIOV82MEHFhpo1Uhv+G52J8T1fI730sS4Tl5DekLaCz1pg/FmI/EQeAsYqm98uDAaFQcs6gDse8VYGmp2XYsoCW72as8ElKmMIbQ/xD7qxDORLmjCtVoyKH19+s6Pp61a5Ag0EVQheHQEQAMN6pcLJUhw9bfO5kqhLv4prt0AqVBUok6U8tIaEc9vDaasBcFHXgPsoOG97DXB6BdvsHuK/5uMVH5PNe58MLp08iCoIt0C0CbN3+D9Qbeg37AyKyFanB/CXq1tPKVCJc6BMNkO/BswnUsTTmlcd4GovpaJUOOZzblGUQBbhzRohhmOGfdsScGeeYME/yNFqzt1ZArV4va1hOLOUpNFv9TOy0ZVi/yDi+sYA9fCSZU9alWI/cbBct5I+3bh1l26umlZsYQm8uqnSgiQWpRm6UJO6xQbmUN9GzCYyKCmpzZRduqqjjtiF10W1yzioTfTtq2cvU6PdINYY8w2UuOjRd9gChtvGuduOIwqlRTYSaXX1dDoFe1vWqZzRm+pIDumO9eX5jMmzFXLDG2pD2l97zoSjVFf/pYoBasgX43e3V/aEk9PUgXbYFm2QxFMcIYSO9GEDMoE+QxoMXf1UjLxMCK5gD5iHL3Ff2zyXLzlTZE+fHPMLcAkzcp2u6pJ9xpAGekqqeqnISXZ2o8yXsqv8NVvl1zaSiSqU+kak9mIg/2+WC9W1qO2PeSLW2tiis980QnmyDOBg2oL01ITh/u+GTodEGwfRYJoNAJgUjcUMpWl0LuoG8lG6wukhA4QYFWpf2QPVgTR63VbpFgwCnUcSEPqHB0BRCsDHsd0k+/YSuPolABEBAAGJAh8EGAEIAAkFAlUIXh0CGwwACgkQPr/KqoyK2Z6zPA/+PkJTzP8kQw4GW0x2ZxXfOmkRVYpSEoHehf6y9YFN00+T8pb71RGItvuX5v6oPKPClOnIVg2WVHOq6Q3HsXEzl7oIbOtPE98WXHiVXud/djc54uHz9WjSPfy/idP7SMslo29BHR/K9nQkiGtayD57wdxgbLXObE3fA0gl4AsWl1EZzNcWVL4SIrvnBGpYIUGBcsTIiP3p09bu4Qf6HjJRXZlBuizigIgeO39l/G6tb6GA1cnbq4y6aCtQeXHLrnvak1jRqznlJWUqS1mQgOPF1MuOduHAvQbfMBQXAEfgOTzuH9PuKoGm7MePwTrU5GsOpNgS4LbvIRODJxYD+vIwA5BniijgfN9aj9KQVMURrd4Np7i0EVmj8P9FtNgYsEaDt7laGpNB9+9Y9heb6kNEulF7KI7y8CKikgvFGHHCyX2BCCbQBqi6wbEGq16qkTJmesYu9ig4v4xD/Q/cLJFziJLjEcWsL7hq7q2o6e7NL6hf5aTH0/bdeMXMqRzDCAFQ5Z+x0QUCgVonxzj+CuTD/LeOs/QHu/9emvm9EOMYY/X9vidLf58PT/AMqMiYbNWty6qY6k2LMw74Yd4+hO+Tjrk8MrqbCUs9h6ih9IOCo68JTWQQbgWSk2TAyd3U4OqTyBnHWr0HhHDRTOxyDbZUtXbk/r4Q4gTcAt+qjpswPyk=", "\x99\x02\x0d\x04\x55\x08\x5e\x1d\x01\x10\x00\xcd\x3e\x52\xfa\x36\x95\x84\x69\x1d\x8a\x27\x1e\xa9\xd7\xdb\x1c\x21\x5a\x6c\x04\x8a\x3b\x52\xac\xc2\x23\x37\x85\x6d\x12\x5a\x18\x38\x67\xfc\x79\x50\x81\xcc\x00\x6a\x57\x15\x36\x65\x94\x5f\x46\x50\xb2\xb7\x67\xf4\x21\x1f\x2d\x6b\x85\xbb\xd3\x8d\xda\x8d\x53\xb2\x56\x22\xc2\x7f\xa8\x48\x09\x47\xda\xb6\x85\x9b\x67\xb2\x25\x3a\x7f\x88\xce\xc8\x51\x40\xad\x7b\x98\xe6\x7f\x77\x7e\x2f\xda\xe3\x54\x8e\xc9\x8e\x63\x27\x6d\x5a\x3a\x49\x56\x36\x28\x30\xc3\xef\x67\x54\x2d\xc8\x3b\x22\xa6\x1c\x08\x03\x78\x09\x3f\xad\x3d\x93\xf9\xcb\x87\x18\x7f\xe0\xf1\x46\x5a\x71\x87\x1b\xea\xaa\x21\xb2\x02\x2e\xa5\x01\xc2\x76\xe3\xff\x1f\x27\x88\x6c\x1b\x49\x52\xd6\xf5\x35\xd8\xb5\x9e\x59\x31\xda\xe5\xa4\x4c\x33\xec\x7d\x92\x6b\xf7\x19\x87\x57\x1b\xe1\x71\x16\xa6\x70\xe7\x69\x19\x12\x70\x2c\x72\x90\xb6\xca\x66\xd7\xfb\x56\x05\x0a\xbf\x2c\x8c\x66\x06\x75\xef\xc3\xfb\x97\x0b\xbe\xeb\xb8\x3c\x63\x59\xe1\xf9\x12\x79\x2f\xb4\xc9\x7b\xde\xfe\xce\xae\xd6\x65\xfa\x46\x49\x05\xe9\x23\x86\x5a\xe2\x5f\x04\x93\x86\x75\x6c\xd8\xc1\xb7\xab\xf6\x4a\xa0\xaa\x91\xd3\x8e\x9a\xfc\x1c\xec\x9b\xcd\x5a\xe8\x06\x28\xed\x4e\x50\x7f\xb6\x89\xa6\x21\x47\x78\x05\x39\x3d\x26\xfe\x1f\xbf\x68\x00\xe1\x49\x02\xd7\x75\x0c\xf4\xa0\x45\xd4\xd9\xf3\x52\x72\x82\xf8\x9b\x38\xf4\xed\xe6\x8f\x41\xd9\x0e\x80\xab\x91\x63\x27\x67\x20\xec\x12\x68\x4c\xc9\x03\xd4\xe9\x84\x45\x1d\x69\x81\xe8\xe4\xca\x78\x3f\xd9\x9e\x4f\x9b\xd6\xec\xfe\x1c\x80\x60\xad\xb3\x6e\x88\x6f\xc0\xd1\xcd\x34\x56\x78\xfb\x95\x1e\x29\xf7\x8a\x0e\xbb\x2a\x48\xd3\x45\xee\xb0\xbb\x09\xc0\xfb\x23\x23\x81\x20\x14\x62\x9d\xa1\x1b\x4c\xd2\xca\x49\x86\x2f\xb7\x81\xff\x3c\x56\xad\x0d\x10\xd0\x5a\x15\x51\xcb\x04\x65\x95\x15\xa5\x6b\x7d\xfb\xab\x19\xc0\xd9\x61\x40\x1d\x9e\xf9\xbc\x8d\x23\xc2\x80\x13\xdf\x17\x1a\x59\xba\xd7\xb1\x36\x42\xe3\xc9\xa6\x97\x8c\x3b\x60\x6b\x9f\xe7\xe6\x04\x9d\xff\x74\x69\x58\x2b\xe9\xf6\xed\xd6\xe6\x37\x13\x34\x36\x45\xe1\x33\x91\x9c\xe5\xd4\x58\x77\x28\xb7\xff\x97\x9b\x21\xfc\xff\xe7\x27\xf3\x27\x6d\xd2\x9a\xce\x2c\x64\x3e\x16\xd7\x48\xf8\x05\xb2\x30\x25\xc7\x7a\x7e\x9a\x69\x9d\x63\x79\x22\xd9\xab\x00\x11\x01\x00\x01\xb4\x5a\x70\x64\x6e\x73\x20\x72\x65\x67\x72\x65\x73\x73\x69\x6f\x6e\x20\x74\x65\x73\x74\x69\x6e\x67\x20\x6b\x65\x79\x20\x28\x6f\x6e\x6c\x79\x20\x66\x6f\x72\x20\x74\x65\x73\x74\x69\x6e\x67\x20\x74\x68\x65\x20\x6f\x70\x65\x6e\x70\x67\x70\x6b\x65\x79\x20\x72\x72\x29\x20\x3c\x72\x65\x67\x72\x65\x73\x73\x69\x6f\x6e\x40\x70\x6f\x77\x65\x72\x64\x6e\x73\x2e\x6f\x72\x67\x3e\x89\x02\x37\x04\x13\x01\x08\x00\x21\x05\x02\x55\x08\x5e\x1d\x02\x1b\x03\x05\x0b\x09\x08\x07\x02\x06\x15\x08\x09\x0a\x0b\x02\x04\x16\x02\x03\x01\x02\x1e\x01\x02\x17\x80\x00\x0a\x09\x10\x3e\xbf\xca\xaa\x8c\x8a\xd9\x9e\xc0\xe9\x0f\xff\x60\xe0\x6e\xf2\x7c\x2d\xf9\xf8\x2e\x4a\x8f\xef\xc8\x29\x67\xd0\xfa\xa7\x4e\x20\x66\xe1\x1b\xc4\xda\x03\xc3\x52\x7b\x0a\xba\x85\xe8\x3f\x91\x79\xe9\x71\xc1\x5c\xa9\x7d\x90\x48\x26\xc9\xc8\x6d\xfe\x77\x3b\x73\xb3\x51\x1f\x35\x21\x69\xc3\x36\x45\x06\xae\xe8\x28\x0d\x64\x4f\x0a\x2d\xaa\x83\x4c\xa9\x44\xb9\xcf\xc0\x36\xda\xe7\x18\x66\x06\xf2\x03\x08\x77\x84\xe5\xcd\x4e\x6d\x68\xb1\x00\xf3\x2a\xc7\x20\x79\xf0\x9c\xcb\x83\x0e\x9c\x75\x6b\x41\x13\x91\xb6\x02\xae\x3e\xc2\xca\x7a\x9d\x85\x70\x80\x06\xa1\x63\x81\x12\xfb\x41\xbd\xfb\x52\x72\xcf\x13\xb6\x1d\x16\xde\xdf\x44\x6c\x19\x54\xb9\xdb\xdc\x65\x59\x2c\xa1\xee\xad\x9d\x13\x00\xdb\xcb\x75\x15\x43\x49\x3d\xdf\xd2\x50\x9c\x60\xca\x69\x3d\xb1\xc0\xbe\x2d\x8c\x21\x4e\xf5\x14\xf1\xa4\x9a\xcf\xbe\xb6\x3e\x20\x4e\x5b\x6a\xd1\x54\x6f\xac\xc3\x66\xd5\xee\x6f\xe3\xe8\x47\x67\xef\x90\x37\x8b\x26\xe3\xe0\xc9\x1a\x5a\x3b\x97\xce\x74\x82\x73\x17\x73\xb7\x3b\x1a\x44\x46\xb6\x0f\x20\x57\x48\x79\x93\x98\xd7\xe6\x27\x1d\x83\x1c\xaf\x10\xd4\xee\x05\x0f\x90\xa6\xb9\x18\x4d\x18\x7d\xb9\x8c\xe9\xa1\x34\x3e\x35\x84\x59\x96\x06\x89\xc7\x16\x83\x22\xfd\x4a\xfe\x46\xf7\xa0\xfb\x67\x00\x42\x89\x04\x2d\x36\x21\x96\xc1\xd0\x48\x92\xdd\x1e\x69\xc5\x85\xe1\x50\xda\x16\x57\xb7\xb3\xd8\x0e\x21\x5f\x52\xfe\xb5\xfb\xe5\x80\x20\x1b\x6d\x0f\xdc\x04\xbb\xea\x5e\x0a\x60\xb1\xff\x71\xe3\x28\x12\x8b\x9d\xaf\x40\x1c\xb7\xb7\x06\x72\xae\xd2\xec\xa2\x0c\x5a\x73\x10\x03\x7c\xf7\x32\x95\xe1\xed\x8e\xef\xfe\x95\x29\x2e\xf9\x9e\xbe\x5b\xa3\xa8\x8c\x0d\x24\x63\x61\xe5\x68\xcc\x90\x01\x32\x4b\x21\x77\x7e\xe8\xd1\x3f\xb5\x8c\xbb\xd1\x1b\x14\xf4\xea\xb4\x3b\x25\x53\x8f\x18\x76\xd5\x23\xcb\xb0\x4c\x5e\x4b\x04\x9f\x72\x25\x52\x2e\x7e\xc8\xb8\xa1\x88\x39\x5f\x36\x30\x41\xc5\x86\x9a\x35\x52\x1b\xfe\x1b\x9d\x89\xf1\x3d\x5f\x23\xbd\xf4\xb1\x2e\x13\x97\x90\xde\x90\xb6\x82\xcf\x5a\x60\xfc\x59\x88\xfc\x44\x1e\x02\xc6\x2a\x9b\xdf\x2e\x0c\x06\x85\x41\xcb\x3a\x80\x3b\x1e\xf1\x56\x06\x9a\x9d\x97\x62\xca\x02\x5b\xbd\x9a\xb3\xc1\x25\x2a\x63\x08\x6d\x0f\xf1\x0f\xba\xb1\x0c\xe4\x4b\x9a\x30\xad\x56\x8c\x8a\x1f\x5f\x7e\xb3\xa3\xe9\xeb\x56\xb9\x02\x0d\x04\x55\x08\x5e\x1d\x01\x10\x00\xc3\x7a\xa5\xc2\xc9\x52\x1c\x3d\x6d\xf3\xb9\x92\xa8\x4b\xbf\x8a\x6b\xb7\x40\x2a\x54\x15\x28\x93\xa5\x3c\xb4\x86\x84\x73\xdb\xc3\x69\xab\x01\x70\x51\xd7\x80\xfb\x28\x38\x6f\x7b\x0d\x70\x7a\x05\xdb\xec\x1e\xe2\xbf\xe6\xe3\x15\x1f\x93\xcd\x7b\x9f\x0c\x2e\x9d\x3c\x88\x2a\x08\xb7\x40\xb4\x09\xb3\x77\xf8\x3f\x50\x6d\xe8\x37\xec\x0c\x8a\xc8\x56\xa7\x07\xf0\x97\xab\x5b\x4f\x29\x50\x89\x73\xa0\x4c\x36\x43\xbf\x06\xcc\x27\x52\xc4\xd3\x9a\x57\x1d\xe0\x6a\x2f\xa5\xa2\x54\x38\xe6\x73\x6e\x51\x94\x40\x16\xe1\xcd\x1a\x21\x86\x63\x86\x7d\xdb\x12\x70\x67\x9e\x60\xc1\x3f\xc8\xd1\x6a\xce\xdd\x59\x02\xb5\x78\xbd\xad\x61\x38\xb3\x94\xa4\xd1\x6f\xf5\x33\xb2\xd1\x95\x62\xff\x20\xe2\xfa\xc6\x00\xf5\xf0\x92\x65\x4f\x5a\x95\x62\x3f\x71\xb0\x5c\xb7\x92\x3e\xdd\xb8\x75\x97\x6e\xae\x9a\x56\x6c\x61\x09\xbc\xba\xa9\xd2\x82\x24\x16\xa5\x19\xba\x50\x93\xba\xc5\x06\xe6\x50\xdf\x46\xcc\x26\x32\x28\x29\xa9\xcd\x94\x5d\xba\xaa\xa3\x8e\xd8\x85\xd7\x45\xb5\xcb\x38\xa8\x4d\xf4\xed\xab\x67\x2f\x53\xa3\xdd\x20\xd6\x18\xf3\x0d\x94\xb8\xe8\xd1\x77\xd8\x02\x86\xdb\xc6\xb9\xdb\x8e\x23\x0a\xa5\x45\x36\x12\x69\x75\xf5\x74\x3a\x05\x7b\x5b\xd6\xa9\x9c\xd1\x9b\xea\x48\x0e\xe9\x8e\xf5\xe5\xf9\x8c\xc9\xb3\x15\x72\xc3\x1b\x6a\x43\xda\x5f\x7b\xce\x84\xa3\x54\x57\xff\xa5\x8a\x01\x6a\xc8\x17\xe3\x77\xb7\x57\xf6\x84\x93\xd3\xd4\x81\x76\xd8\x16\x6d\x90\xc4\x53\x1c\x21\x84\x8e\xf4\x61\x03\x32\x81\x3e\x43\x1a\x0c\x5d\xfd\x54\x8c\xbc\x4c\x08\xae\x60\x0f\x98\x87\x2f\x71\x5f\xdb\x3c\x97\x2f\x39\x53\x64\x4f\x9f\x1c\xf3\x0b\x70\x09\x33\x72\x9d\xae\xea\x92\x7d\xc6\x90\x06\x7a\x4a\xaa\x7a\xa9\xc8\x49\x76\x76\xa3\xcc\x97\xb2\xab\xfc\x35\x5b\xe5\xd7\x36\x92\x89\x2a\x94\xfa\x46\xa4\xf6\x62\x20\xff\x6f\x96\x0b\xd5\xb5\xa8\xed\x8f\x79\x22\xd6\xda\xd8\xa2\xb3\xdf\x34\x42\x79\xb2\x0c\xe0\x60\xda\x82\xf4\xd4\x84\xe1\xfe\xef\x86\x4e\x87\x44\x1b\x07\xd1\x60\x9a\x0d\x00\x98\x14\x8d\xc5\x0c\xa5\x69\x74\x2e\xea\x06\xf2\x51\xba\xc2\xe9\x21\x03\x84\x18\x15\x6a\x5f\xd9\x03\xd5\x81\x34\x7a\xdd\x56\xe9\x16\x0c\x02\x9d\x47\x12\x10\xfa\x87\x07\x40\x51\x0a\xc0\xc7\xb1\xdd\x24\xfb\xf6\x12\xb8\xfa\x25\x00\x11\x01\x00\x01\x89\x02\x1f\x04\x18\x01\x08\x00\x09\x05\x02\x55\x08\x5e\x1d\x02\x1b\x0c\x00\x0a\x09\x10\x3e\xbf\xca\xaa\x8c\x8a\xd9\x9e\xb3\x3c\x0f\xfe\x3e\x42\x53\xcc\xff\x24\x43\x0e\x06\x5b\x4c\x76\x67\x15\xdf\x3a\x69\x11\x55\x8a\x52\x12\x81\xde\x85\xfe\xb2\xf5\x81\x4d\xd3\x4f\x93\xf2\x96\xfb\xd5\x11\x88\xb6\xfb\x97\xe6\xfe\xa8\x3c\xa3\xc2\x94\xe9\xc8\x56\x0d\x96\x54\x73\xaa\xe9\x0d\xc7\xb1\x71\x33\x97\xba\x08\x6c\xeb\x4f\x13\xdf\x16\x5c\x78\x95\x5e\xe7\x7f\x76\x37\x39\xe2\xe1\xf3\xf5\x68\xd2\x3d\xfc\xbf\x89\xd3\xfb\x48\xcb\x25\xa3\x6f\x41\x1d\x1f\xca\xf6\x74\x24\x88\x6b\x5a\xc8\x3e\x7b\xc1\xdc\x60\x6c\xb5\xce\x6c\x4d\xdf\x03\x48\x25\xe0\x0b\x16\x97\x51\x19\xcc\xd7\x16\x54\xbe\x12\x22\xbb\xe7\x04\x6a\x58\x21\x41\x81\x72\xc4\xc8\x88\xfd\xe9\xd3\xd6\xee\xe1\x07\xfa\x1e\x32\x51\x5d\x99\x41\xba\x2c\xe2\x80\x88\x1e\x3b\x7f\x65\xfc\x6e\xad\x6f\xa1\x80\xd5\xc9\xdb\xab\x8c\xba\x68\x2b\x50\x79\x71\xcb\xae\x7b\xda\x93\x58\xd1\xab\x39\xe5\x25\x65\x2a\x4b\x59\x90\x80\xe3\xc5\xd4\xcb\x8e\x76\xe1\xc0\xbd\x06\xdf\x30\x14\x17\x00\x47\xe0\x39\x3c\xee\x1f\xd3\xee\x2a\x81\xa6\xec\xc7\x8f\xc1\x3a\xd4\xe4\x6b\x0e\xa4\xd8\x12\xe0\xb6\xef\x21\x13\x83\x27\x16\x03\xfa\xf2\x30\x03\x90\x67\x8a\x28\xe0\x7c\xdf\x5a\x8f\xd2\x90\x54\xc5\x11\xad\xde\x0d\xa7\xb8\xb4\x11\x59\xa3\xf0\xff\x45\xb4\xd8\x18\xb0\x46\x83\xb7\xb9\x5a\x1a\x93\x41\xf7\xef\x58\xf6\x17\x9b\xea\x43\x44\xba\x51\x7b\x28\x8e\xf2\xf0\x22\xa2\x92\x0b\xc5\x18\x71\xc2\xc9\x7d\x81\x08\x26\xd0\x06\xa8\xba\xc1\xb1\x06\xab\x5e\xaa\x91\x32\x66\x7a\xc6\x2e\xf6\x28\x38\xbf\x8c\x43\xfd\x0f\xdc\x2c\x91\x73\x88\x92\xe3\x11\xc5\xac\x2f\xb8\x6a\xee\xad\xa8\xe9\xee\xcd\x2f\xa8\x5f\xe5\xa4\xc7\xd3\xf6\xdd\x78\xc5\xcc\xa9\x1c\xc3\x08\x01\x50\xe5\x9f\xb1\xd1\x05\x02\x81\x5a\x27\xc7\x38\xfe\x0a\xe4\xc3\xfc\xb7\x8e\xb3\xf4\x07\xbb\xff\x5e\x9a\xf9\xbd\x10\xe3\x18\x63\xf5\xfd\xbe\x27\x4b\x7f\x9f\x0f\x4f\xf0\x0c\xa8\xc8\x98\x6c\xd5\xad\xcb\xaa\x98\xea\x4d\x8b\x33\x0e\xf8\x61\xde\x3e\x84\xef\x93\x8e\xb9\x3c\x32\xba\x9b\x09\x4b\x3d\x87\xa8\xa1\xf4\x83\x82\xa3\xaf\x09\x4d\x64\x10\x6e\x05\x92\x93\x64\xc0\xc9\xdd\xd4\xe0\xea\x93\xc8\x19\xc7\x5a\xbd\x07\x84\x70\xd1\x4c\xec\x72\x0d\xb6\x54\xb5\x76\xe4\xfe\xbe\x10\xe2\x04\xdc\x02\xdf\xaa\x8e\x9b\x30\x3f\x29",false))
-
- (CASE_S(QType::SPF, "\"v=spf1 a:mail.rec.test ~all\"", "\x1bv=spf1 a:mail.rec.test ~all",false))
- (CASE_S(QType::EUI48, "00-11-22-33-44-55", "\x00\x11\x22\x33\x44\x55",false))
- (CASE_S(QType::EUI64, "00-11-22-33-44-55-66-77", "\x00\x11\x22\x33\x44\x55\x66\x77",false))
- (CASE_S(QType::TSIG, "HMAC-MD5.SIG-ALG.REG.INT. 1368386956 60 16 TkbpD66/Mtgo8GUEFZIwhg== 12345 0 0", "\x08HMAC-MD5\x07SIG-ALG\x03REG\x03INT\x00\x00\x00\x51\x8f\xed\x8c\x00\x3c\x00\x10\x4e\x46\xe9\x0f\xae\xbf\x32\xd8\x28\xf0\x65\x04\x15\x92\x30\x86\x30\x39\x00\x00\x00\x00",false))
- (CASE_S(QType::TSIG, "HMAC-MD5.SIG-ALG.REG.INT. 1368386956 60 16 TkbpD66/Mtgo8GUEFZIwhg== 12345 18 16 TkbpD66/Mtgo8GUEFZIwhg==", "\x08HMAC-MD5\x07SIG-ALG\x03REG\x03INT\x00\x00\x00\x51\x8f\xed\x8c\x00\x3c\x00\x10\x4e\x46\xe9\x0f\xae\xbf\x32\xd8\x28\xf0\x65\x04\x15\x92\x30\x86\x30\x39\x00\x12\x00\x10\x4e\x46\xe9\x0f\xae\xbf\x32\xd8\x28\xf0\x65\x04\x15\x92\x30\x86",false))
- (CASE_S(QType::TKEY, "gss-tsig. 12345 12345 3 21 4 dGVzdA== 4 dGVzdA==", "\x08gss-tsig\x00\x00\x00\x30\x39\x00\x00\x30\x39\x00\x03\x00\x15\x00\x04test\x00\x04test", false))
-/* (CASE_S(QType::ADDR, "zone format", "line format",false)) */
- (CASE_S(QType::DLV, "20642 8 2 04443abe7e94c3985196beae5d548c727b044dda5151e60d7cd76a9fd931d00e", "\x50\xa2\x08\x02\x04\x44\x3a\xbe\x7e\x94\xc3\x98\x51\x96\xbe\xae\x5d\x54\x8c\x72\x7b\x04\x4d\xda\x51\x51\xe6\x0d\x7c\xd7\x6a\x9f\xd9\x31\xd0\x0e",false))
- (CASE_S(QType::URI, "10000 1 \"ftp://ftp1.example.com/public\"", "\x27\x10\x00\x01\x66\x74\x70\x3a\x2f\x2f\x66\x74\x70\x31\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x2f\x70\x75\x62\x6c\x69\x63", false))
- (CASE_S(QType::URI, "10 1 \"ftp://ftp1.example.com/public/with/a/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/long/url\"", "\x00\x0a\x00\x01\x66\x74\x70\x3a\x2f\x2f\x66\x74\x70\x31\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x2f\x70\x75\x62\x6c\x69\x63\x2f\x77\x69\x74\x68\x2f\x61\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x6c\x6f\x6e\x67\x2f\x75\x72\x6c", false))
- (CASE_S(QType::CAA, "0 issue \"example.net\"", "\x00\x05\x69\x73\x73\x75\x65\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6e\x65\x74",false))
- (CASE_S((QType::typeenum)65226,"\\# 3 414243","\x41\x42\x43",false))
+ (CASE_L(QType::RRSIG, "SOA 8 3 300 1369267200 1368057600 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=", "SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=", "\x00\x06\x08\x03\x00\x00\x01\x2c\x51\x9d\x5c\x00\x51\x8a\xe7\x00\xd3\xc8\x03\x72\x65\x63\x04\x74\x65\x73\x74\x00\x79\xc5\x8a\x0f\xf3\xac\x74\x08\x97\xa5\xb3\x3f\xb2\x03\xd3\xf3\x62\x95\x0f\xf5\xa2\x42\x79\xea\x73\x1a\x09\x82\x21\xf7\x8b\x11\xda\xf8\xb3\x80\x71\x85\x3b\x14\x6e\xd5\xe0\x14\x51\x27\x12\xeb\x89\x8d\x5e\xd2\xb0\x76\x80\x0b\x37\x90\x27\xa5\xf4\x3e\x1b\xcd\xf2\xba\xc0\x0a\x86\xe0\x38\xbf\x78\x75\x20\x1e\x9b\x51\x95\xa4\x76\xc9\xc1\x0b\x27\x8b\x75\xa6\x3c\xeb\x9e\xe1\xa2\x23\x30\x35\xc0\xcd\xa2\xcd\x19\x9e\x13\xe5\xe8\xcb\x5d\xfa\xed\x0b\xb4\xa4\xec\xf9\x0c\xbc\xd3\x7d\x02\x13\x58\x5b\x01\xda\x25\x33\x37\x86"))
+ (CASE_S(QType::NSEC, "a.rec.test. A NS SOA MX AAAA RRSIG NSEC DNSKEY", "\x01""a\x03rec\x04test\x00\x00\x07\x62\x01\x00\x08\x00\x03\x80"))
+ (CASE_S(QType::DNSKEY, "257 3 5 AwEAAZVtlHc8O4TVmlGx/PGJTc7hbVjMR7RywxLuAm1dqgyHvgNRD7chYLsALOdZKW6VRvusbyhoOPilnh8XpucBDqjGD6lIemsURz7drZEqcLupVA0TPxXABZ6auJ3jumqIhSOcLj9rpSwI4xuWt0yu6LR9tL2q8+A0yEZxcAaKS+Wq0fExJ93NxgXl1/fY+JcYQvonjd31GxXXef9uf0exXyzowh5h8+IIBETU+ZiYVB5BqiwkICZL/OX57idm99ycA2/tIen66F8u2ueTvgPcecnoqHvW0MtLQKzeNmqdGNthHhV5di0SZdMZQeo/izs68uN2WzqQDZy9Ec2JwBTbxWE=", "\x01\x01\x03\x05\x03\x01\x00\x01\x95\x6d\x94\x77\x3c\x3b\x84\xd5\x9a\x51\xb1\xfc\xf1\x89\x4d\xce\xe1\x6d\x58\xcc\x47\xb4\x72\xc3\x12\xee\x02\x6d\x5d\xaa\x0c\x87\xbe\x03\x51\x0f\xb7\x21\x60\xbb\x00\x2c\xe7\x59\x29\x6e\x95\x46\xfb\xac\x6f\x28\x68\x38\xf8\xa5\x9e\x1f\x17\xa6\xe7\x01\x0e\xa8\xc6\x0f\xa9\x48\x7a\x6b\x14\x47\x3e\xdd\xad\x91\x2a\x70\xbb\xa9\x54\x0d\x13\x3f\x15\xc0\x05\x9e\x9a\xb8\x9d\xe3\xba\x6a\x88\x85\x23\x9c\x2e\x3f\x6b\xa5\x2c\x08\xe3\x1b\x96\xb7\x4c\xae\xe8\xb4\x7d\xb4\xbd\xaa\xf3\xe0\x34\xc8\x46\x71\x70\x06\x8a\x4b\xe5\xaa\xd1\xf1\x31\x27\xdd\xcd\xc6\x05\xe5\xd7\xf7\xd8\xf8\x97\x18\x42\xfa\x27\x8d\xdd\xf5\x1b\x15\xd7\x79\xff\x6e\x7f\x47\xb1\x5f\x2c\xe8\xc2\x1e\x61\xf3\xe2\x08\x04\x44\xd4\xf9\x98\x98\x54\x1e\x41\xaa\x2c\x24\x20\x26\x4b\xfc\xe5\xf9\xee\x27\x66\xf7\xdc\x9c\x03\x6f\xed\x21\xe9\xfa\xe8\x5f\x2e\xda\xe7\x93\xbe\x03\xdc\x79\xc9\xe8\xa8\x7b\xd6\xd0\xcb\x4b\x40\xac\xde\x36\x6a\x9d\x18\xdb\x61\x1e\x15\x79\x76\x2d\x12\x65\xd3\x19\x41\xea\x3f\x8b\x3b\x3a\xf2\xe3\x76\x5b\x3a\x90\x0d\x9c\xbd\x11\xcd\x89\xc0\x14\xdb\xc5\x61"))
+
+ (CASE_S(QType::DHCID, "AAAB92JtyyO73zqENgu9LVua+0PZoeCcKapTw4asbYmx5F4=", "\x00\x00\x01\xf7\x62\x6d\xcb\x23\xbb\xdf\x3a\x84\x36\x0b\xbd\x2d\x5b\x9a\xfb\x43\xd9\xa1\xe0\x9c\x29\xaa\x53\xc3\x86\xac\x6d\x89\xb1\xe4\x5e"))
+ (CASE_S(QType::DHCID, "AAEB92JtyyO73zqENgu9LVua+0PZoeCcKapTw4asbYmx5F4=", "\x00\x01\x01\xf7\x62\x6d\xcb\x23\xbb\xdf\x3a\x84\x36\x0b\xbd\x2d\x5b\x9a\xfb\x43\xd9\xa1\xe0\x9c\x29\xaa\x53\xc3\x86\xac\x6d\x89\xb1\xe4\x5e"))
+ (CASE_S(QType::DHCID, "AAIB92JtyyO73zqENgu9LVua+0PZoeCcKapTw4asbYmx5F4=", "\x00\x02\x01\xf7\x62\x6d\xcb\x23\xbb\xdf\x3a\x84\x36\x0b\xbd\x2d\x5b\x9a\xfb\x43\xd9\xa1\xe0\x9c\x29\xaa\x53\xc3\x86\xac\x6d\x89\xb1\xe4\x5e"))
+
+ (CASE_S(QType::NSEC3, "1 1 1 f00b RPF1JGFCCNFA7STPTIJ9FPFNM40A4FLL NS SOA RRSIG DNSKEY NSEC3PARAM", "\x01\x01\x00\x01\x02\xf0\x0b\x14\xde\x5e\x19\xc1\xec\x65\xde\xa3\xf3\xb9\xec\xa6\x97\xe5\xf7\xb1\x00\xa2\x3e\xb5\x00\x07\x22\x00\x00\x00\x00\x02\x90"))
+ (CASE_S(QType::NSEC3PARAM, "1 0 1 f00b", "\x01\x00\x00\x01\x02\xf0\x0b"))
+
+ (CASE_S(QType::TLSA, "0 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x00\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::TLSA, "0 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x00\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+ (CASE_S(QType::TLSA, "1 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x01\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::TLSA, "1 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x01\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+ (CASE_S(QType::TLSA, "1 0 1 6acea2f68b03d9efe97a967e137aca6ac3a89490d532d87806d9e9c257668453", "\x01\x00\x01\x6a\xce\xa2\xf6\x8b\x03\xd9\xef\xe9\x7a\x96\x7e\x13\x7a\xca\x6a\xc3\xa8\x94\x90\xd5\x32\xd8\x78\x06\xd9\xe9\xc2\x57\x66\x84\x53"))
+ (CASE_S(QType::TLSA, "1 0 2 e6dce237992803488d11d828b7728deddd4577de73d7d078338c8a45880beddff98e076a28bf8e3068da8e73667b802a721c95d7323b038c60200a430cb6fbd4", "\x01\x00\x02\xe6\xdc\xe2\x37\x99\x28\x03\x48\x8d\x11\xd8\x28\xb7\x72\x8d\xed\xdd\x45\x77\xde\x73\xd7\xd0\x78\x33\x8c\x8a\x45\x88\x0b\xed\xdf\xf9\x8e\x07\x6a\x28\xbf\x8e\x30\x68\xda\x8e\x73\x66\x7b\x80\x2a\x72\x1c\x95\xd7\x32\x3b\x03\x8c\x60\x20\x0a\x43\x0c\xb6\xfb\xd4"))
+ (CASE_S(QType::TLSA, "2 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x02\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::TLSA, "2 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x02\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+
+ (CASE_S(QType::TLSA, "3 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x03\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::TLSA, "3 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x03\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+
+ (CASE_S(QType::SMIMEA, "0 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x00\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::SMIMEA, "0 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x00\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+ (CASE_S(QType::SMIMEA, "1 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x01\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::SMIMEA, "1 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x01\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+ (CASE_S(QType::SMIMEA, "1 0 1 6acea2f68b03d9efe97a967e137aca6ac3a89490d532d87806d9e9c257668453", "\x01\x00\x01\x6a\xce\xa2\xf6\x8b\x03\xd9\xef\xe9\x7a\x96\x7e\x13\x7a\xca\x6a\xc3\xa8\x94\x90\xd5\x32\xd8\x78\x06\xd9\xe9\xc2\x57\x66\x84\x53"))
+ (CASE_S(QType::SMIMEA, "1 0 2 e6dce237992803488d11d828b7728deddd4577de73d7d078338c8a45880beddff98e076a28bf8e3068da8e73667b802a721c95d7323b038c60200a430cb6fbd4", "\x01\x00\x02\xe6\xdc\xe2\x37\x99\x28\x03\x48\x8d\x11\xd8\x28\xb7\x72\x8d\xed\xdd\x45\x77\xde\x73\xd7\xd0\x78\x33\x8c\x8a\x45\x88\x0b\xed\xdf\xf9\x8e\x07\x6a\x28\xbf\x8e\x30\x68\xda\x8e\x73\x66\x7b\x80\x2a\x72\x1c\x95\xd7\x32\x3b\x03\x8c\x60\x20\x0a\x43\x0c\xb6\xfb\xd4"))
+ (CASE_S(QType::SMIMEA, "2 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x02\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::SMIMEA, "2 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x02\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+ (CASE_S(QType::SMIMEA, "3 0 0 308201f43082015da003020102020900ac547c5557870ec7300d06092a864886f70d010105050030133111300f06035504030c087265632e74657374301e170d3133303531323139343830395a170d3133303631313139343830395a30133111300f06035504030c087265632e7465737430819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001a350304e301d0603551d0e0416041473715bbfd9bc2b824112f858586f166aafb99482301f0603551d2304183016801473715bbfd9bc2b824112f858586f166aafb99482300c0603551d13040530030101ff300d06092a864886f70d0101050500038181005550f1d64139ab0e86c5b303fc69015d1676ca95931071ae41884656c71c116a38138ecf63054b350dc78983cb4a83288dbc81c5a659a56cc6843d5452c3e98449b94a0cf0c0cd7190c96caa5f0ee9a3bef7e75002be4a233673852bdf1a5fd306a7080eb4fead9b3ad162074b5f007e9156e220302dea8c700868a12577e7c4", "\x03\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::SMIMEA, "3 1 0 30819f300d06092a864886f70d010101050003818d0030818902818100d282bb968dfdec0e5d13dfcc0a36ed73178581424e10a37c89d3014204933b3a8c1159fdecb221afe4168883d2d00ac1f15fca4614fbd5e05de2e37ad0fbad8b7748dddbcf30b39e80466c61c733415e72b9f42d5fad0bf35f041eb5631eded00314c66c4878b351416e5c6b9096f2a7088a24387e5d0149c523739f84f502c70203010001", "\x03\x01\x00\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01"))
+
+ (CASE_S(QType::OPENPGPKEY, "mQINBFUIXh0BEADNPlL6NpWEaR2KJx6p19scIVpsBIo7UqzCIzeFbRJaGDhn/HlQgcwAalcVNmWUX0ZQsrdn9CEfLWuFu9ON2o1TslYiwn+oSAlH2raFm2eyJTp/iM7IUUCte5jmf3d+L9rjVI7JjmMnbVo6SVY2KDDD72dULcg7IqYcCAN4CT+tPZP5y4cYf+DxRlpxhxvqqiGyAi6lAcJ24/8fJ4hsG0lS1vU12LWeWTHa5aRMM+x9kmv3GYdXG+FxFqZw52kZEnAscpC2ymbX+1YFCr8sjGYGde/D+5cLvuu4PGNZ4fkSeS+0yXve/s6u1mX6RkkF6SOGWuJfBJOGdWzYwber9kqgqpHTjpr8HOybzVroBijtTlB/tommIUd4BTk9Jv4fv2gA4UkC13UM9KBF1NnzUnKC+Js49O3mj0HZDoCrkWMnZyDsEmhMyQPU6YRFHWmB6OTKeD/Znk+b1uz+HIBgrbNuiG/A0c00Vnj7lR4p94oOuypI00XusLsJwPsjI4EgFGKdoRtM0spJhi+3gf88Vq0NENBaFVHLBGWVFaVrffurGcDZYUAdnvm8jSPCgBPfFxpZutexNkLjyaaXjDtga5/n5gSd/3RpWCvp9u3W5jcTNDZF4TORnOXUWHcot/+XmyH8/+cn8ydt0prOLGQ+FtdI+AWyMCXHen6aaZ1jeSLZqwARAQABtFpwZG5zIHJlZ3Jlc3Npb24gdGVzdGluZyBrZXkgKG9ubHkgZm9yIHRlc3RpbmcgdGhlIG9wZW5wZ3BrZXkgcnIpIDxyZWdyZXNzaW9uQHBvd2VyZG5zLm9yZz6JAjcEEwEIACEFAlUIXh0CGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQPr/KqoyK2Z7A6Q//YOBu8nwt+fguSo/vyCln0PqnTiBm4RvE2gPDUnsKuoXoP5F56XHBXKl9kEgmycht/nc7c7NRHzUhacM2RQau6CgNZE8KLaqDTKlEuc/ANtrnGGYG8gMId4TlzU5taLEA8yrHIHnwnMuDDpx1a0ETkbYCrj7CynqdhXCABqFjgRL7Qb37UnLPE7YdFt7fRGwZVLnb3GVZLKHurZ0TANvLdRVDST3f0lCcYMppPbHAvi2MIU71FPGkms++tj4gTltq0VRvrMNm1e5v4+hHZ++QN4sm4+DJGlo7l850gnMXc7c7GkRGtg8gV0h5k5jX5icdgxyvENTuBQ+QprkYTRh9uYzpoTQ+NYRZlgaJxxaDIv1K/kb3oPtnAEKJBC02IZbB0EiS3R5pxYXhUNoWV7ez2A4hX1L+tfvlgCAbbQ/cBLvqXgpgsf9x4ygSi52vQBy3twZyrtLsogxacxADfPcyleHtju/+lSku+Z6+W6OojA0kY2HlaMyQATJLIXd+6NE/tYy70RsU9Oq0OyVTjxh21SPLsExeSwSfciVSLn7IuKGIOV82MEHFhpo1Uhv+G52J8T1fI730sS4Tl5DekLaCz1pg/FmI/EQeAsYqm98uDAaFQcs6gDse8VYGmp2XYsoCW72as8ElKmMIbQ/xD7qxDORLmjCtVoyKH19+s6Pp61a5Ag0EVQheHQEQAMN6pcLJUhw9bfO5kqhLv4prt0AqVBUok6U8tIaEc9vDaasBcFHXgPsoOG97DXB6BdvsHuK/5uMVH5PNe58MLp08iCoIt0C0CbN3+D9Qbeg37AyKyFanB/CXq1tPKVCJc6BMNkO/BswnUsTTmlcd4GovpaJUOOZzblGUQBbhzRohhmOGfdsScGeeYME/yNFqzt1ZArV4va1hOLOUpNFv9TOy0ZVi/yDi+sYA9fCSZU9alWI/cbBct5I+3bh1l26umlZsYQm8uqnSgiQWpRm6UJO6xQbmUN9GzCYyKCmpzZRduqqjjtiF10W1yzioTfTtq2cvU6PdINYY8w2UuOjRd9gChtvGuduOIwqlRTYSaXX1dDoFe1vWqZzRm+pIDumO9eX5jMmzFXLDG2pD2l97zoSjVFf/pYoBasgX43e3V/aEk9PUgXbYFm2QxFMcIYSO9GEDMoE+QxoMXf1UjLxMCK5gD5iHL3Ff2zyXLzlTZE+fHPMLcAkzcp2u6pJ9xpAGekqqeqnISXZ2o8yXsqv8NVvl1zaSiSqU+kak9mIg/2+WC9W1qO2PeSLW2tiis980QnmyDOBg2oL01ITh/u+GTodEGwfRYJoNAJgUjcUMpWl0LuoG8lG6wukhA4QYFWpf2QPVgTR63VbpFgwCnUcSEPqHB0BRCsDHsd0k+/YSuPolABEBAAGJAh8EGAEIAAkFAlUIXh0CGwwACgkQPr/KqoyK2Z6zPA/+PkJTzP8kQw4GW0x2ZxXfOmkRVYpSEoHehf6y9YFN00+T8pb71RGItvuX5v6oPKPClOnIVg2WVHOq6Q3HsXEzl7oIbOtPE98WXHiVXud/djc54uHz9WjSPfy/idP7SMslo29BHR/K9nQkiGtayD57wdxgbLXObE3fA0gl4AsWl1EZzNcWVL4SIrvnBGpYIUGBcsTIiP3p09bu4Qf6HjJRXZlBuizigIgeO39l/G6tb6GA1cnbq4y6aCtQeXHLrnvak1jRqznlJWUqS1mQgOPF1MuOduHAvQbfMBQXAEfgOTzuH9PuKoGm7MePwTrU5GsOpNgS4LbvIRODJxYD+vIwA5BniijgfN9aj9KQVMURrd4Np7i0EVmj8P9FtNgYsEaDt7laGpNB9+9Y9heb6kNEulF7KI7y8CKikgvFGHHCyX2BCCbQBqi6wbEGq16qkTJmesYu9ig4v4xD/Q/cLJFziJLjEcWsL7hq7q2o6e7NL6hf5aTH0/bdeMXMqRzDCAFQ5Z+x0QUCgVonxzj+CuTD/LeOs/QHu/9emvm9EOMYY/X9vidLf58PT/AMqMiYbNWty6qY6k2LMw74Yd4+hO+Tjrk8MrqbCUs9h6ih9IOCo68JTWQQbgWSk2TAyd3U4OqTyBnHWr0HhHDRTOxyDbZUtXbk/r4Q4gTcAt+qjpswPyk=", "\x99\x02\x0d\x04\x55\x08\x5e\x1d\x01\x10\x00\xcd\x3e\x52\xfa\x36\x95\x84\x69\x1d\x8a\x27\x1e\xa9\xd7\xdb\x1c\x21\x5a\x6c\x04\x8a\x3b\x52\xac\xc2\x23\x37\x85\x6d\x12\x5a\x18\x38\x67\xfc\x79\x50\x81\xcc\x00\x6a\x57\x15\x36\x65\x94\x5f\x46\x50\xb2\xb7\x67\xf4\x21\x1f\x2d\x6b\x85\xbb\xd3\x8d\xda\x8d\x53\xb2\x56\x22\xc2\x7f\xa8\x48\x09\x47\xda\xb6\x85\x9b\x67\xb2\x25\x3a\x7f\x88\xce\xc8\x51\x40\xad\x7b\x98\xe6\x7f\x77\x7e\x2f\xda\xe3\x54\x8e\xc9\x8e\x63\x27\x6d\x5a\x3a\x49\x56\x36\x28\x30\xc3\xef\x67\x54\x2d\xc8\x3b\x22\xa6\x1c\x08\x03\x78\x09\x3f\xad\x3d\x93\xf9\xcb\x87\x18\x7f\xe0\xf1\x46\x5a\x71\x87\x1b\xea\xaa\x21\xb2\x02\x2e\xa5\x01\xc2\x76\xe3\xff\x1f\x27\x88\x6c\x1b\x49\x52\xd6\xf5\x35\xd8\xb5\x9e\x59\x31\xda\xe5\xa4\x4c\x33\xec\x7d\x92\x6b\xf7\x19\x87\x57\x1b\xe1\x71\x16\xa6\x70\xe7\x69\x19\x12\x70\x2c\x72\x90\xb6\xca\x66\xd7\xfb\x56\x05\x0a\xbf\x2c\x8c\x66\x06\x75\xef\xc3\xfb\x97\x0b\xbe\xeb\xb8\x3c\x63\x59\xe1\xf9\x12\x79\x2f\xb4\xc9\x7b\xde\xfe\xce\xae\xd6\x65\xfa\x46\x49\x05\xe9\x23\x86\x5a\xe2\x5f\x04\x93\x86\x75\x6c\xd8\xc1\xb7\xab\xf6\x4a\xa0\xaa\x91\xd3\x8e\x9a\xfc\x1c\xec\x9b\xcd\x5a\xe8\x06\x28\xed\x4e\x50\x7f\xb6\x89\xa6\x21\x47\x78\x05\x39\x3d\x26\xfe\x1f\xbf\x68\x00\xe1\x49\x02\xd7\x75\x0c\xf4\xa0\x45\xd4\xd9\xf3\x52\x72\x82\xf8\x9b\x38\xf4\xed\xe6\x8f\x41\xd9\x0e\x80\xab\x91\x63\x27\x67\x20\xec\x12\x68\x4c\xc9\x03\xd4\xe9\x84\x45\x1d\x69\x81\xe8\xe4\xca\x78\x3f\xd9\x9e\x4f\x9b\xd6\xec\xfe\x1c\x80\x60\xad\xb3\x6e\x88\x6f\xc0\xd1\xcd\x34\x56\x78\xfb\x95\x1e\x29\xf7\x8a\x0e\xbb\x2a\x48\xd3\x45\xee\xb0\xbb\x09\xc0\xfb\x23\x23\x81\x20\x14\x62\x9d\xa1\x1b\x4c\xd2\xca\x49\x86\x2f\xb7\x81\xff\x3c\x56\xad\x0d\x10\xd0\x5a\x15\x51\xcb\x04\x65\x95\x15\xa5\x6b\x7d\xfb\xab\x19\xc0\xd9\x61\x40\x1d\x9e\xf9\xbc\x8d\x23\xc2\x80\x13\xdf\x17\x1a\x59\xba\xd7\xb1\x36\x42\xe3\xc9\xa6\x97\x8c\x3b\x60\x6b\x9f\xe7\xe6\x04\x9d\xff\x74\x69\x58\x2b\xe9\xf6\xed\xd6\xe6\x37\x13\x34\x36\x45\xe1\x33\x91\x9c\xe5\xd4\x58\x77\x28\xb7\xff\x97\x9b\x21\xfc\xff\xe7\x27\xf3\x27\x6d\xd2\x9a\xce\x2c\x64\x3e\x16\xd7\x48\xf8\x05\xb2\x30\x25\xc7\x7a\x7e\x9a\x69\x9d\x63\x79\x22\xd9\xab\x00\x11\x01\x00\x01\xb4\x5a\x70\x64\x6e\x73\x20\x72\x65\x67\x72\x65\x73\x73\x69\x6f\x6e\x20\x74\x65\x73\x74\x69\x6e\x67\x20\x6b\x65\x79\x20\x28\x6f\x6e\x6c\x79\x20\x66\x6f\x72\x20\x74\x65\x73\x74\x69\x6e\x67\x20\x74\x68\x65\x20\x6f\x70\x65\x6e\x70\x67\x70\x6b\x65\x79\x20\x72\x72\x29\x20\x3c\x72\x65\x67\x72\x65\x73\x73\x69\x6f\x6e\x40\x70\x6f\x77\x65\x72\x64\x6e\x73\x2e\x6f\x72\x67\x3e\x89\x02\x37\x04\x13\x01\x08\x00\x21\x05\x02\x55\x08\x5e\x1d\x02\x1b\x03\x05\x0b\x09\x08\x07\x02\x06\x15\x08\x09\x0a\x0b\x02\x04\x16\x02\x03\x01\x02\x1e\x01\x02\x17\x80\x00\x0a\x09\x10\x3e\xbf\xca\xaa\x8c\x8a\xd9\x9e\xc0\xe9\x0f\xff\x60\xe0\x6e\xf2\x7c\x2d\xf9\xf8\x2e\x4a\x8f\xef\xc8\x29\x67\xd0\xfa\xa7\x4e\x20\x66\xe1\x1b\xc4\xda\x03\xc3\x52\x7b\x0a\xba\x85\xe8\x3f\x91\x79\xe9\x71\xc1\x5c\xa9\x7d\x90\x48\x26\xc9\xc8\x6d\xfe\x77\x3b\x73\xb3\x51\x1f\x35\x21\x69\xc3\x36\x45\x06\xae\xe8\x28\x0d\x64\x4f\x0a\x2d\xaa\x83\x4c\xa9\x44\xb9\xcf\xc0\x36\xda\xe7\x18\x66\x06\xf2\x03\x08\x77\x84\xe5\xcd\x4e\x6d\x68\xb1\x00\xf3\x2a\xc7\x20\x79\xf0\x9c\xcb\x83\x0e\x9c\x75\x6b\x41\x13\x91\xb6\x02\xae\x3e\xc2\xca\x7a\x9d\x85\x70\x80\x06\xa1\x63\x81\x12\xfb\x41\xbd\xfb\x52\x72\xcf\x13\xb6\x1d\x16\xde\xdf\x44\x6c\x19\x54\xb9\xdb\xdc\x65\x59\x2c\xa1\xee\xad\x9d\x13\x00\xdb\xcb\x75\x15\x43\x49\x3d\xdf\xd2\x50\x9c\x60\xca\x69\x3d\xb1\xc0\xbe\x2d\x8c\x21\x4e\xf5\x14\xf1\xa4\x9a\xcf\xbe\xb6\x3e\x20\x4e\x5b\x6a\xd1\x54\x6f\xac\xc3\x66\xd5\xee\x6f\xe3\xe8\x47\x67\xef\x90\x37\x8b\x26\xe3\xe0\xc9\x1a\x5a\x3b\x97\xce\x74\x82\x73\x17\x73\xb7\x3b\x1a\x44\x46\xb6\x0f\x20\x57\x48\x79\x93\x98\xd7\xe6\x27\x1d\x83\x1c\xaf\x10\xd4\xee\x05\x0f\x90\xa6\xb9\x18\x4d\x18\x7d\xb9\x8c\xe9\xa1\x34\x3e\x35\x84\x59\x96\x06\x89\xc7\x16\x83\x22\xfd\x4a\xfe\x46\xf7\xa0\xfb\x67\x00\x42\x89\x04\x2d\x36\x21\x96\xc1\xd0\x48\x92\xdd\x1e\x69\xc5\x85\xe1\x50\xda\x16\x57\xb7\xb3\xd8\x0e\x21\x5f\x52\xfe\xb5\xfb\xe5\x80\x20\x1b\x6d\x0f\xdc\x04\xbb\xea\x5e\x0a\x60\xb1\xff\x71\xe3\x28\x12\x8b\x9d\xaf\x40\x1c\xb7\xb7\x06\x72\xae\xd2\xec\xa2\x0c\x5a\x73\x10\x03\x7c\xf7\x32\x95\xe1\xed\x8e\xef\xfe\x95\x29\x2e\xf9\x9e\xbe\x5b\xa3\xa8\x8c\x0d\x24\x63\x61\xe5\x68\xcc\x90\x01\x32\x4b\x21\x77\x7e\xe8\xd1\x3f\xb5\x8c\xbb\xd1\x1b\x14\xf4\xea\xb4\x3b\x25\x53\x8f\x18\x76\xd5\x23\xcb\xb0\x4c\x5e\x4b\x04\x9f\x72\x25\x52\x2e\x7e\xc8\xb8\xa1\x88\x39\x5f\x36\x30\x41\xc5\x86\x9a\x35\x52\x1b\xfe\x1b\x9d\x89\xf1\x3d\x5f\x23\xbd\xf4\xb1\x2e\x13\x97\x90\xde\x90\xb6\x82\xcf\x5a\x60\xfc\x59\x88\xfc\x44\x1e\x02\xc6\x2a\x9b\xdf\x2e\x0c\x06\x85\x41\xcb\x3a\x80\x3b\x1e\xf1\x56\x06\x9a\x9d\x97\x62\xca\x02\x5b\xbd\x9a\xb3\xc1\x25\x2a\x63\x08\x6d\x0f\xf1\x0f\xba\xb1\x0c\xe4\x4b\x9a\x30\xad\x56\x8c\x8a\x1f\x5f\x7e\xb3\xa3\xe9\xeb\x56\xb9\x02\x0d\x04\x55\x08\x5e\x1d\x01\x10\x00\xc3\x7a\xa5\xc2\xc9\x52\x1c\x3d\x6d\xf3\xb9\x92\xa8\x4b\xbf\x8a\x6b\xb7\x40\x2a\x54\x15\x28\x93\xa5\x3c\xb4\x86\x84\x73\xdb\xc3\x69\xab\x01\x70\x51\xd7\x80\xfb\x28\x38\x6f\x7b\x0d\x70\x7a\x05\xdb\xec\x1e\xe2\xbf\xe6\xe3\x15\x1f\x93\xcd\x7b\x9f\x0c\x2e\x9d\x3c\x88\x2a\x08\xb7\x40\xb4\x09\xb3\x77\xf8\x3f\x50\x6d\xe8\x37\xec\x0c\x8a\xc8\x56\xa7\x07\xf0\x97\xab\x5b\x4f\x29\x50\x89\x73\xa0\x4c\x36\x43\xbf\x06\xcc\x27\x52\xc4\xd3\x9a\x57\x1d\xe0\x6a\x2f\xa5\xa2\x54\x38\xe6\x73\x6e\x51\x94\x40\x16\xe1\xcd\x1a\x21\x86\x63\x86\x7d\xdb\x12\x70\x67\x9e\x60\xc1\x3f\xc8\xd1\x6a\xce\xdd\x59\x02\xb5\x78\xbd\xad\x61\x38\xb3\x94\xa4\xd1\x6f\xf5\x33\xb2\xd1\x95\x62\xff\x20\xe2\xfa\xc6\x00\xf5\xf0\x92\x65\x4f\x5a\x95\x62\x3f\x71\xb0\x5c\xb7\x92\x3e\xdd\xb8\x75\x97\x6e\xae\x9a\x56\x6c\x61\x09\xbc\xba\xa9\xd2\x82\x24\x16\xa5\x19\xba\x50\x93\xba\xc5\x06\xe6\x50\xdf\x46\xcc\x26\x32\x28\x29\xa9\xcd\x94\x5d\xba\xaa\xa3\x8e\xd8\x85\xd7\x45\xb5\xcb\x38\xa8\x4d\xf4\xed\xab\x67\x2f\x53\xa3\xdd\x20\xd6\x18\xf3\x0d\x94\xb8\xe8\xd1\x77\xd8\x02\x86\xdb\xc6\xb9\xdb\x8e\x23\x0a\xa5\x45\x36\x12\x69\x75\xf5\x74\x3a\x05\x7b\x5b\xd6\xa9\x9c\xd1\x9b\xea\x48\x0e\xe9\x8e\xf5\xe5\xf9\x8c\xc9\xb3\x15\x72\xc3\x1b\x6a\x43\xda\x5f\x7b\xce\x84\xa3\x54\x57\xff\xa5\x8a\x01\x6a\xc8\x17\xe3\x77\xb7\x57\xf6\x84\x93\xd3\xd4\x81\x76\xd8\x16\x6d\x90\xc4\x53\x1c\x21\x84\x8e\xf4\x61\x03\x32\x81\x3e\x43\x1a\x0c\x5d\xfd\x54\x8c\xbc\x4c\x08\xae\x60\x0f\x98\x87\x2f\x71\x5f\xdb\x3c\x97\x2f\x39\x53\x64\x4f\x9f\x1c\xf3\x0b\x70\x09\x33\x72\x9d\xae\xea\x92\x7d\xc6\x90\x06\x7a\x4a\xaa\x7a\xa9\xc8\x49\x76\x76\xa3\xcc\x97\xb2\xab\xfc\x35\x5b\xe5\xd7\x36\x92\x89\x2a\x94\xfa\x46\xa4\xf6\x62\x20\xff\x6f\x96\x0b\xd5\xb5\xa8\xed\x8f\x79\x22\xd6\xda\xd8\xa2\xb3\xdf\x34\x42\x79\xb2\x0c\xe0\x60\xda\x82\xf4\xd4\x84\xe1\xfe\xef\x86\x4e\x87\x44\x1b\x07\xd1\x60\x9a\x0d\x00\x98\x14\x8d\xc5\x0c\xa5\x69\x74\x2e\xea\x06\xf2\x51\xba\xc2\xe9\x21\x03\x84\x18\x15\x6a\x5f\xd9\x03\xd5\x81\x34\x7a\xdd\x56\xe9\x16\x0c\x02\x9d\x47\x12\x10\xfa\x87\x07\x40\x51\x0a\xc0\xc7\xb1\xdd\x24\xfb\xf6\x12\xb8\xfa\x25\x00\x11\x01\x00\x01\x89\x02\x1f\x04\x18\x01\x08\x00\x09\x05\x02\x55\x08\x5e\x1d\x02\x1b\x0c\x00\x0a\x09\x10\x3e\xbf\xca\xaa\x8c\x8a\xd9\x9e\xb3\x3c\x0f\xfe\x3e\x42\x53\xcc\xff\x24\x43\x0e\x06\x5b\x4c\x76\x67\x15\xdf\x3a\x69\x11\x55\x8a\x52\x12\x81\xde\x85\xfe\xb2\xf5\x81\x4d\xd3\x4f\x93\xf2\x96\xfb\xd5\x11\x88\xb6\xfb\x97\xe6\xfe\xa8\x3c\xa3\xc2\x94\xe9\xc8\x56\x0d\x96\x54\x73\xaa\xe9\x0d\xc7\xb1\x71\x33\x97\xba\x08\x6c\xeb\x4f\x13\xdf\x16\x5c\x78\x95\x5e\xe7\x7f\x76\x37\x39\xe2\xe1\xf3\xf5\x68\xd2\x3d\xfc\xbf\x89\xd3\xfb\x48\xcb\x25\xa3\x6f\x41\x1d\x1f\xca\xf6\x74\x24\x88\x6b\x5a\xc8\x3e\x7b\xc1\xdc\x60\x6c\xb5\xce\x6c\x4d\xdf\x03\x48\x25\xe0\x0b\x16\x97\x51\x19\xcc\xd7\x16\x54\xbe\x12\x22\xbb\xe7\x04\x6a\x58\x21\x41\x81\x72\xc4\xc8\x88\xfd\xe9\xd3\xd6\xee\xe1\x07\xfa\x1e\x32\x51\x5d\x99\x41\xba\x2c\xe2\x80\x88\x1e\x3b\x7f\x65\xfc\x6e\xad\x6f\xa1\x80\xd5\xc9\xdb\xab\x8c\xba\x68\x2b\x50\x79\x71\xcb\xae\x7b\xda\x93\x58\xd1\xab\x39\xe5\x25\x65\x2a\x4b\x59\x90\x80\xe3\xc5\xd4\xcb\x8e\x76\xe1\xc0\xbd\x06\xdf\x30\x14\x17\x00\x47\xe0\x39\x3c\xee\x1f\xd3\xee\x2a\x81\xa6\xec\xc7\x8f\xc1\x3a\xd4\xe4\x6b\x0e\xa4\xd8\x12\xe0\xb6\xef\x21\x13\x83\x27\x16\x03\xfa\xf2\x30\x03\x90\x67\x8a\x28\xe0\x7c\xdf\x5a\x8f\xd2\x90\x54\xc5\x11\xad\xde\x0d\xa7\xb8\xb4\x11\x59\xa3\xf0\xff\x45\xb4\xd8\x18\xb0\x46\x83\xb7\xb9\x5a\x1a\x93\x41\xf7\xef\x58\xf6\x17\x9b\xea\x43\x44\xba\x51\x7b\x28\x8e\xf2\xf0\x22\xa2\x92\x0b\xc5\x18\x71\xc2\xc9\x7d\x81\x08\x26\xd0\x06\xa8\xba\xc1\xb1\x06\xab\x5e\xaa\x91\x32\x66\x7a\xc6\x2e\xf6\x28\x38\xbf\x8c\x43\xfd\x0f\xdc\x2c\x91\x73\x88\x92\xe3\x11\xc5\xac\x2f\xb8\x6a\xee\xad\xa8\xe9\xee\xcd\x2f\xa8\x5f\xe5\xa4\xc7\xd3\xf6\xdd\x78\xc5\xcc\xa9\x1c\xc3\x08\x01\x50\xe5\x9f\xb1\xd1\x05\x02\x81\x5a\x27\xc7\x38\xfe\x0a\xe4\xc3\xfc\xb7\x8e\xb3\xf4\x07\xbb\xff\x5e\x9a\xf9\xbd\x10\xe3\x18\x63\xf5\xfd\xbe\x27\x4b\x7f\x9f\x0f\x4f\xf0\x0c\xa8\xc8\x98\x6c\xd5\xad\xcb\xaa\x98\xea\x4d\x8b\x33\x0e\xf8\x61\xde\x3e\x84\xef\x93\x8e\xb9\x3c\x32\xba\x9b\x09\x4b\x3d\x87\xa8\xa1\xf4\x83\x82\xa3\xaf\x09\x4d\x64\x10\x6e\x05\x92\x93\x64\xc0\xc9\xdd\xd4\xe0\xea\x93\xc8\x19\xc7\x5a\xbd\x07\x84\x70\xd1\x4c\xec\x72\x0d\xb6\x54\xb5\x76\xe4\xfe\xbe\x10\xe2\x04\xdc\x02\xdf\xaa\x8e\x9b\x30\x3f\x29"))
+
+ (CASE_S(QType::SPF, "\"v=spf1 a:mail.rec.test ~all\"", "\x1bv=spf1 a:mail.rec.test ~all"))
+ (CASE_S(QType::EUI48, "00-11-22-33-44-55", "\x00\x11\x22\x33\x44\x55"))
+ (CASE_S(QType::EUI64, "00-11-22-33-44-55-66-77", "\x00\x11\x22\x33\x44\x55\x66\x77"))
+ (CASE_S(QType::TKEY, "gss-tsig. 12345 12345 3 21 4 dGVzdA== 4 dGVzdA==", "\x08gss-tsig\x00\x00\x00\x30\x39\x00\x00\x30\x39\x00\x03\x00\x15\x00\x04test\x00\x04test"))
+ (CASE_S(QType::TSIG, "HMAC-MD5.SIG-ALG.REG.INT. 1368386956 60 16 TkbpD66/Mtgo8GUEFZIwhg== 12345 0 0", "\x08HMAC-MD5\x07SIG-ALG\x03REG\x03INT\x00\x00\x00\x51\x8f\xed\x8c\x00\x3c\x00\x10\x4e\x46\xe9\x0f\xae\xbf\x32\xd8\x28\xf0\x65\x04\x15\x92\x30\x86\x30\x39\x00\x00\x00\x00"))
+ (CASE_S(QType::TSIG, "HMAC-MD5.SIG-ALG.REG.INT. 1368386956 60 16 TkbpD66/Mtgo8GUEFZIwhg== 12345 18 16 TkbpD66/Mtgo8GUEFZIwhg==", "\x08HMAC-MD5\x07SIG-ALG\x03REG\x03INT\x00\x00\x00\x51\x8f\xed\x8c\x00\x3c\x00\x10\x4e\x46\xe9\x0f\xae\xbf\x32\xd8\x28\xf0\x65\x04\x15\x92\x30\x86\x30\x39\x00\x12\x00\x10\x4e\x46\xe9\x0f\xae\xbf\x32\xd8\x28\xf0\x65\x04\x15\x92\x30\x86"))
+ (CASE_S(QType::URI, "10000 1 \"ftp://ftp1.example.com/public\"", "\x27\x10\x00\x01\x66\x74\x70\x3a\x2f\x2f\x66\x74\x70\x31\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x2f\x70\x75\x62\x6c\x69\x63"))
+ (CASE_S(QType::URI, "10 1 \"ftp://ftp1.example.com/public/with/a/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/long/url\"", "\x00\x0a\x00\x01\x66\x74\x70\x3a\x2f\x2f\x66\x74\x70\x31\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x2f\x70\x75\x62\x6c\x69\x63\x2f\x77\x69\x74\x68\x2f\x61\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x76\x65\x72\x79\x2f\x6c\x6f\x6e\x67\x2f\x75\x72\x6c"))
+ (CASE_S(QType::CAA, "0 issue \"example.net\"", "\x00\x05\x69\x73\x73\x75\x65\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6e\x65\x74"))
+ (CASE_S(QType::DLV, "20642 8 2 04443abe7e94c3985196beae5d548c727b044dda5151e60d7cd76a9fd931d00e", "\x50\xa2\x08\x02\x04\x44\x3a\xbe\x7e\x94\xc3\x98\x51\x96\xbe\xae\x5d\x54\x8c\x72\x7b\x04\x4d\xda\x51\x51\xe6\x0d\x7c\xd7\x6a\x9f\xd9\x31\xd0\x0e"))
+ (CASE_S((QType::typeenum)65226,"\\# 3 414243","\x41\x42\x43"))
+
+/* (CASE_S(QType::NAME, "zone format", "line format")) */
+/* (CASE_L(QType::NAME, "zone format", "canonic zone format", "line format")) */
;
int n=0;
int lq=-1;
for(const cases_t::value_type& val : cases) {
- QType q(val.get<0>());
+ const QType q(val.get<0>());
+ const std::string& inval = val.get<1>();
+ const std::string& zoneval = val.get<2>();
+ const std::string& lineval = val.get<3>();
+ const broken_marker broken = val.get<4>();
+
if (lq != q.getCode()) n = 0;
+ BOOST_CHECK_MESSAGE(q.getCode() >= lq, "record types not sorted correctly: " << q.getCode() << " < " << lq);
lq = q.getCode();
n++;
BOOST_TEST_CHECKPOINT("Checking record type " << q.getName() << " test #" << n);
BOOST_TEST_MESSAGE("Checking record type " << q.getName() << " test #" << n);
try {
std::string recData;
- auto rec = DNSRecordContent::mastermake(q.getCode(), 1, val.get<1>());
- BOOST_CHECK_MESSAGE(rec != NULL, "mastermake( " << q.getCode() << ", 1, " << val.get<1>() << ") returned NULL");
+ auto rec = DNSRecordContent::mastermake(q.getCode(), 1, inval);
+ BOOST_CHECK_MESSAGE(rec != NULL, "mastermake( " << q.getCode() << ", 1, " << inval << ") returned NULL");
if (rec == NULL) continue;
// now verify the record (note that this will be same as *zone* value (except for certain QTypes)
case QType::SOA:
case QType::TXT:
// check *input* value instead
- REC_CHECK_EQUAL(rec->getZoneRepresentation(), val.get<1>());
+ REC_CHECK_EQUAL(rec->getZoneRepresentation(), inval);
break;
default:
- REC_CHECK_EQUAL(rec->getZoneRepresentation(), val.get<2>());
+ REC_CHECK_EQUAL(rec->getZoneRepresentation(), zoneval);
}
recData = rec->serialize(DNSName("rec.test"));
BOOST_CHECK_MESSAGE(rec2 != NULL, "unserialize(rec.test, " << q.getCode() << ", recData) returned NULL");
if (rec2 == NULL) continue;
// now verify the zone representation (here it can be different!)
- REC_CHECK_EQUAL(rec2->getZoneRepresentation(), val.get<2>());
+ REC_CHECK_EQUAL(rec2->getZoneRepresentation(), zoneval);
// and last, check the wire format (using hex format for error readability)
- string cmpData = makeHexDump(val.get<3>());
+ string cmpData = makeHexDump(lineval);
recData = makeHexDump(recData);
REC_CHECK_EQUAL(recData, cmpData);
} catch (std::runtime_error &err) {
// these *MUST NOT* parse properly!
BOOST_AUTO_TEST_CASE(test_record_types_bad_values) {
+ enum class case_type_t { zone, wire };
+
// qtype, value, zone/wire format, broken
- typedef boost::tuple<const QType::typeenum, const std::string, case_type_enum_t, bool> case_t;
+ typedef boost::tuple<const QType::typeenum, const std::string, case_type_t, broken_marker> case_t;
typedef std::list<case_t> cases_t;
- cases_t cases = boost::assign::list_of
- (case_t(QType::A, "932.521.256.42", zone, false)) // hollywood IP
- (case_t(QType::A, "932.521", zone, false)) // truncated hollywood IP
- (case_t(QType::A, "10.0", zone, false)) // truncated IP
- (case_t(QType::A, "10.0.0.1.", zone, false)) // trailing dot
- (case_t(QType::A, "10.0.0.", zone, false)) // trailing dot
- (case_t(QType::A, ".0.0.1", zone, false)) // empty octet
- (case_t(QType::A, "10..0.1", zone, false)) // empty octet
- (case_t(QType::A, "\xca\xec\x00", wire, false)) // truncated wire value
- (case_t(QType::A, "127.0.0.1 evil data", zone, false)) // trailing garbage
- (case_t(QType::AAAA, "23:00", zone, false)) // time when this test was written
- (case_t(QType::AAAA, "23:00::15::43", zone, false)) // double compression
- (case_t(QType::AAAA, "2a23:00::15::", zone, false)) // ditto
- (case_t(QType::AAAA, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff", wire, false)) // truncated wire value
+#define ZONE_CASE(type, input) case_t(type, BINARY(input), case_type_t::zone, broken_marker::WORKING)
+#define WIRE_CASE(type, input) case_t(type, BINARY(input), case_type_t::wire, broken_marker::WORKING)
+#define BROKEN_ZONE_CASE(type, input) case_t(type, BINARY(input), case_type_t::zone, broken_marker::BROKEN)
+#define BROKEN_WIRE_CASE(type, input) case_t(type, BINARY(input), case_type_t::wire, broken_marker::BROKEN)
+
+ const cases_t cases = boost::assign::list_of
+ (ZONE_CASE(QType::A, "932.521.256.42")) // hollywood IP
+ (ZONE_CASE(QType::A, "932.521")) // truncated hollywood IP
+ (ZONE_CASE(QType::A, "10.0")) // truncated IP
+ (ZONE_CASE(QType::A, "10.0.0.1.")) // trailing dot
+ (ZONE_CASE(QType::A, "10.0.0.")) // trailing dot
+ (ZONE_CASE(QType::A, ".0.0.1")) // empty octet
+ (ZONE_CASE(QType::A, "10..0.1")) // empty octet
+ (WIRE_CASE(QType::A, "\xca\xec\x00")) // truncated wire value
+ (ZONE_CASE(QType::A, "127.0.0.1 evil data")) // trailing garbage
+ (ZONE_CASE(QType::AAAA, "23:00")) // time when this test was written
+ (ZONE_CASE(QType::AAAA, "23:00::15::43")) // double compression
+ (ZONE_CASE(QType::AAAA, "2a23:00::15::")) // ditto
+ (WIRE_CASE(QType::AAAA, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff")) // truncated wire value
// empty label, must be broken
- (case_t(QType::CNAME, "name..example.com.", zone, false))
+ (ZONE_CASE(QType::CNAME, "name..example.com."))
// overly large label (64), must be broken
- (case_t(QType::CNAME, "1234567890123456789012345678901234567890123456789012345678901234.example.com.", zone, false))
+ (ZONE_CASE(QType::CNAME, "1234567890123456789012345678901234567890123456789012345678901234.example.com."))
// local overly large name (256), must be broken
- (case_t(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123.rec.test.", zone, false))
+ (ZONE_CASE(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123.rec.test."))
// non-local overly large name (256), must be broken
- (case_t(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012.", zone, false))
- (case_t(QType::SOA, "ns.rec.test hostmaster.test.rec 20130512010 3600 3600 604800 120", zone, false)) // too long serial
+ (ZONE_CASE(QType::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012."))
+ (ZONE_CASE(QType::SOA, "ns.rec.test hostmaster.test.rec 20130512010 3600 3600 604800 120")) // too long serial
;
int n=0;
int lq=-1;
for(const cases_t::value_type& val : cases) {
- QType q(val.get<0>());
+ const QType q(val.get<0>());
+ const std::string& input = val.get<1>();
+ const case_type_t case_type = val.get<2>();
+ const broken_marker broken = val.get<3>();
+
if (lq != q.getCode()) n = 0;
lq = q.getCode();
n++;
BOOST_TEST_CHECKPOINT("Checking bad value for record type " << q.getName() << " test #" << n);
BOOST_TEST_MESSAGE("Checking bad value for record type " << q.getName() << " test #" << n);
-
+
vector<uint8_t> packet;
DNSPacketWriter pw(packet, DNSName("unit.test"), q.getCode());
- if (val.get<2>() == wire) {
+ if (case_type == case_type_t::wire) {
BOOST_WARN_MESSAGE(false, "wire checks not supported");
continue;
}
- if (val.get<3>()) {
+ if (broken_marker::BROKEN == broken) {
bool success=true;
- BOOST_WARN_EXCEPTION( { auto drc = DNSRecordContent::mastermake(q.getCode(), 1, val.get<1>()); pw.startRecord(DNSName("unit.test"), q.getCode()); drc->toPacket(pw); success=false; }, std::exception, test_dnsrecords_cc_predicate );
- if (success) REC_FAIL_XSUCCESS2(q.getName() << " test #" << n << " has unexpectedly passed"); // a bad record was detected when it was supposed not to be detected
+ BOOST_WARN_EXCEPTION(
+ {
+ auto drc = DNSRecordContent::mastermake(q.getCode(), 1, input);
+ pw.startRecord(DNSName("unit.test"), q.getCode());
+ drc->toPacket(pw);
+ success=false;
+ },
+ std::exception, test_dnsrecords_cc_predicate
+ );
+ if (success) REC_FAIL_XSUCCESS(q.getName() << " test #" << n << " has unexpectedly passed"); // a bad record was detected when it was supposed not to be detected
} else {
- BOOST_CHECK_EXCEPTION( { auto drc = DNSRecordContent::mastermake(q.getCode(), 1, val.get<1>()); pw.startRecord(DNSName("unit.test"), q.getCode()); drc->toPacket(pw); }, std::exception, test_dnsrecords_cc_predicate );
+ BOOST_CHECK_EXCEPTION(
+ {
+ auto drc = DNSRecordContent::mastermake(q.getCode(), 1, input);
+ pw.startRecord(DNSName("unit.test"), q.getCode());
+ drc->toPacket(pw);
+ },
+ std::exception, test_dnsrecords_cc_predicate
+ );
}
};
}
// this should contain NSID now
BOOST_CHECK_EQUAL(eo.d_packetsize, 1280);
-
+
// it should contain NSID option with value 'powerdns', and nothing else
BOOST_CHECK_EQUAL(eo.d_options.size(), 1);
BOOST_CHECK_EQUAL(eo.d_options[0].first, 3); // nsid
pw.xfrIP(htonl(0x7f000001));
opts.push_back(pair<uint16_t,string>(3, "powerdns"));
pw.addOpt(1280, 0, 0, opts);
- pw.getHeader()->id = htons(0xf001);
+ pw.getHeader()->id = htons(0xf001);
pw.getHeader()->rd = 1;
pw.commit();
Socket sock(AF_INET, SOCK_DGRAM);
ComboAddress dest(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
seedRandom("/dev/urandom");
- cerr<<"Keyname: '"<<keyname.toString()<<"', algo: '"<<trc.d_algoName.toString()<<"', key: '"<<Base64Encode(key)<<"'\n";
+ cerr<<"Keyname: '"<<keyname<<"', algo: '"<<trc.d_algoName<<"', key: '"<<Base64Encode(key)<<"'\n";
TSIGTriplet tt;
tt.name=keyname;
tt.algo=DNSName("hmac-md5");
if (checkTSIG) {
if (theirMac.empty()) {
- throw std::runtime_error("No TSIG on AXFR response from "+d_remote.toStringWithPort()+" , should be signed with TSIG key '"+d_tt.name.toString()+"'");
+ throw std::runtime_error("No TSIG on AXFR response from "+d_remote.toStringWithPort()+" , should be signed with TSIG key '"+d_tt.name.toLogString()+"'");
}
try {
}
// not found in neg. or pos. cache, look it up
- return getSOAUncached(domain, sd);
+ return getSOAUncached(domain, sd, false);
}
-bool UeberBackend::getSOAUncached(const DNSName &domain, SOAData &sd)
+bool UeberBackend::getSOAUncached(const DNSName &domain, SOAData &sd, bool unmodifiedSerial)
{
d_question.qtype=QType::SOA;
d_question.qname=domain;
d_question.zoneId=-1;
for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
- if((*i)->getSOA(domain, sd)) {
- if( d_cache_ttl ) {
+ if((*i)->getSOA(domain, sd, unmodifiedSerial)) {
+ if(d_cache_ttl && !unmodifiedSerial) {
DNSZoneRecord rr;
rr.dr.d_name = sd.qname;
rr.dr.d_type = QType::SOA;
/** Determines if we are authoritative for a zone, and at what level */
bool getAuth(const DNSName &target, const QType &qtype, SOAData* sd, bool cachedOk=true);
bool getSOA(const DNSName &domain, SOAData &sd);
- bool getSOAUncached(const DNSName &domain, SOAData &sd); // same, but ignores cache
+ /** Load SOA info from backends, ignoring the cache. Callers that will write to the backend should use this
+ * function, possibly setting unmodifiedSerial=true when editing the SOA Serial. */
+ bool getSOAUncached(const DNSName &domain, SOAData &sd, bool unmodifiedSerial=false);
bool get(DNSZoneRecord &r);
void getAllDomains(vector<DomainInfo> *domains, bool include_disabled=false);
return result;
}
+static bool isNSECAncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSECRecordContent> nsec)
+{
+ return nsec->d_set.count(QType::NS) &&
+ !nsec->d_set.count(QType::SOA) &&
+ signer.countLabels() < owner.countLabels();
+}
+
+static bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSEC3RecordContent> nsec3)
+{
+ return nsec3->d_set.count(QType::NS) &&
+ !nsec3->d_set.count(QType::SOA) &&
+ signer.countLabels() < owner.countLabels();
+}
+
static bool provesNoDataWildCard(const DNSName& qname, const uint16_t qtype, const cspmap_t& validrrsets)
{
LOG("Trying to prove that there is no data in wildcard for "<<qname<<"/"<<QType(qtype).getName()<<endl);
that (original) owner name other than DS RRs, and all RRs below that
owner name regardless of type.
*/
- if (nsec->d_set.count(QType::NS) && !nsec->d_set.count(QType::SOA) &&
- signer.countLabels() < owner.countLabels()) {
- LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec->d_set.count(QType::NS))<<", SOA is "<<std::to_string(nsec->d_set.count(QType::SOA))<<", signer is "<<signer.toString()<<", owner name is "<<owner.toString()<<endl);
+ if (qtype != QType::DS && (qname == owner || qname.isPartOf(owner)) && isNSECAncestorDelegation(signer, owner, nsec)) {
+ LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec->d_set.count(QType::NS))<<", SOA is "<<std::to_string(nsec->d_set.count(QType::SOA))<<", signer is "<<signer<<", owner name is "<<owner<<endl);
/* this is an "ancestor delegation" NSEC RR */
- if (qname == owner && qtype != QType::DS) {
- LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
- continue;
- }
+ LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
+ return NODATA;
}
/* check if the type is denied */
if(qname == owner) {
if (nsec->d_set.count(qtype)) {
LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<endl);
- continue;
+ return NODATA;
}
LOG("Denies existence of type "<<QType(qtype).getName()<<endl);
that (original) owner name other than DS RRs, and all RRs below that
owner name regardless of type.
*/
- if (nsec3->d_set.count(QType::NS) && !nsec3->d_set.count(QType::SOA) &&
- signer.countLabels() < v.first.first.countLabels()) {
- LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec3->d_set.count(QType::NS))<<", SOA is "<<std::to_string(nsec3->d_set.count(QType::SOA))<<", signer is "<<signer.toString()<<", owner name is "<<v.first.first.toString()<<endl);
+ if (qtype != QType::DS && beginHash == h && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
+ LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec3->d_set.count(QType::NS))<<", SOA is "<<std::to_string(nsec3->d_set.count(QType::SOA))<<", signer is "<<signer<<", owner name is "<<v.first.first<<endl);
/* this is an "ancestor delegation" NSEC3 RR */
- if (beginHash == h && qtype != QType::DS) {
- LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
- continue;
- }
+ LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
+ return NODATA;
}
// If the name exists, check if the qtype is denied
if(beginHash == h) {
if (nsec3->d_set.count(qtype)) {
LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
- continue;
+ return NODATA;
}
LOG("Denies existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
if(!nsec3)
continue;
+ const DNSName signer = getSigner(v.second.signatures);
+ if (!v.first.first.isPartOf(signer)) {
+ LOG("Owner "<<v.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
+ continue;
+ }
+
string h = getHashFromNSEC3(closestEncloser, nsec3);
if (h.empty()) {
return INSECURE;
LOG("Comparing "<<toBase32Hex(h)<<" ("<<closestEncloser<<") against "<<toBase32Hex(beginHash)<<endl);
if(beginHash == h) {
+ if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
+ LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
+ continue;
+ }
+
LOG("Closest encloser for "<<qname<<" is "<<closestEncloser<<endl);
found = true;
break;
{
vector<DNSName> ret;
if(!begin.isPartOf(end))
- throw PDNSException(end.toLogString() + "is not part of " + begin.toString());
+ throw PDNSException(end.toLogString() + "is not part of " + begin.toLogString());
DNSName qname(end);
vector<string> labelsToAdd = begin.makeRelative(end).getRawLabels();
lowestNTA = negAnchor.first;
if(!lowestNTA.empty()) {
- LOG("Found a Negative Trust Anchor for "<<lowestNTA.toStringRootDot()<<", which was added with reason '"<<negAnchors.at(lowestNTA)<<"', ");
+ LOG("Found a Negative Trust Anchor for "<<lowestNTA<<", which was added with reason '"<<negAnchors.at(lowestNTA)<<"', ");
/* RFC 7646 section 2.1 tells us that we SHOULD still validate if there
* is a Trust Anchor below the Negative Trust Anchor for the name we
LOG("marking answer Insecure"<<endl);
return NTA; // Not Insecure, this way validateRecords() can shortcut
}
- LOG("but a Trust Anchor for "<<lowestTA.toStringRootDot()<<" is configured, continuing validation."<<endl);
+ LOG("but a Trust Anchor for "<<lowestTA<<" is configured, continuing validation."<<endl);
}
}
if (rr.qtype.getCode() == QType::SOA && rr.qname==zonename) {
have_soa = true;
increaseSOARecord(rr, soa_edit_api_kind, soa_edit_kind);
- // fixup dots after serializeSOAData/increaseSOARecord
- rr.content = makeBackendRecordContent(rr.qtype, rr.content);
}
if (rr.qtype.getCode() == QType::NS && rr.qname==zonename) {
have_zone_ns = true;
if (!have_soa && zonekind != DomainInfo::Slave) {
// synthesize a SOA record so the zone "really" exists
- string soa = (boost::format("%s %s %lu")
+ string soa = (boost::format("%s %s %ul")
% ::arg()["default-soa-name"]
% (::arg().isEmpty("default-soa-mail") ? (DNSName("hostmaster.") + zonename).toString() : ::arg()["default-soa-mail"])
% document["serial"].int_value()
).str();
SOAData sd;
fillSOAData(soa, sd); // fills out default values for us
- autorr.qtype = "SOA";
- autorr.content = serializeSOAData(sd);
+ autorr.qtype = QType::SOA;
+ autorr.content = makeSOAContent(sd)->getZoneRepresentation(true);
increaseSOARecord(autorr, soa_edit_api_kind, soa_edit_kind);
- // fixup dots after serializeSOAData/increaseSOARecord
- autorr.content = makeBackendRecordContent(autorr.qtype, autorr.content);
new_records.push_back(autorr);
}
} catch (...) {
throw ApiException("Unable to parse DNS Name for NS '" + nameserver + "'");
}
- autorr.qtype = "NS";
+ autorr.qtype = QType::NS;
new_records.push_back(autorr);
if (have_zone_ns) {
throw ApiException("Nameservers list MUST NOT be mixed with zone-level NS in rrsets");
sd.db->getDomainMetadataOne(sd.qname, "SOA-EDIT-API", soa_edit_api_kind);
sd.db->getDomainMetadataOne(sd.qname, "SOA-EDIT", soa_edit_kind);
if (!soa_edit_api_kind.empty()) {
- soarr.qname = sd.qname;
- soarr.content = serializeSOAData(sd);
- soarr.qtype = "SOA";
- soarr.domain_id = sd.domain_id;
- soarr.auth = 1;
- soarr.ttl = sd.ttl;
- increaseSOARecord(soarr, soa_edit_api_kind, soa_edit_kind);
- // fixup dots after serializeSOAData/increaseSOARecord
- soarr.content = makeBackendRecordContent(soarr.qtype, soarr.content);
- soa_changed = true;
+ soa_changed = makeIncreasedSOARecord(sd, soa_edit_api_kind, soa_edit_kind, soarr);
}
sd.db->startTransaction(sd.qname);
rr.domain_id = di.id;
if (rr.qtype.getCode() == QType::SOA && rr.qname==zonename) {
soa_edit_done = increaseSOARecord(rr, soa_edit_api_kind, soa_edit_kind);
- rr.content = makeBackendRecordContent(rr.qtype, rr.content);
}
}
checkDuplicateRecords(new_records);
// edit SOA (if needed)
if (!soa_edit_api_kind.empty() && !soa_edit_done) {
SOAData sd;
- if (!B.getSOA(zonename, sd))
+ if (!B.getSOAUncached(zonename, sd, true))
throw ApiException("No SOA found for domain '"+zonename.toString()+"'");
DNSResourceRecord rr;
- rr.qname = zonename;
- rr.content = serializeSOAData(sd);
- rr.qtype = "SOA";
- rr.domain_id = di.id;
- rr.auth = 1;
- rr.ttl = sd.ttl;
- increaseSOARecord(rr, soa_edit_api_kind, soa_edit_kind);
- // fixup dots after serializeSOAData/increaseSOARecord
- rr.content = makeBackendRecordContent(rr.qtype, rr.content);
-
- if (!di.backend->replaceRRSet(di.id, rr.qname, rr.qtype, vector<DNSResourceRecord>(1, rr))) {
- throw ApiException("Hosting backend does not support editing records.");
+ if (makeIncreasedSOARecord(sd, soa_edit_api_kind, soa_edit_kind, rr)) {
+ if (!di.backend->replaceRRSet(di.id, rr.qname, rr.qtype, vector<DNSResourceRecord>(1, rr))) {
+ throw ApiException("Hosting backend does not support editing records.");
+ }
}
// return old and new serials in headers
{
auto iter = SyncRes::t_sstorage.domainmap->find(zonename);
if (iter == SyncRes::t_sstorage.domainmap->end())
- throw ApiException("Could not find domain '"+zonename.toString()+"'");
+ throw ApiException("Could not find domain '"+zonename.toLogString()+"'");
const SyncRes::AuthDomain& zone = iter->second;
SyncRes::domainmap_t::const_iterator iter = SyncRes::t_sstorage.domainmap->find(zonename);
if (iter == SyncRes::t_sstorage.domainmap->end())
- throw ApiException("Could not find domain '"+zonename.toString()+"'");
+ throw ApiException("Could not find domain '"+zonename.toLogString()+"'");
if(req->method == "PUT" && !::arg().mustDo("api-readonly")) {
Json document = req->json();
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "xpf.hh"
+
+std::string generateXPFPayload(bool tcp, const ComboAddress& source, const ComboAddress& destination)
+{
+ if (source.sin4.sin_family != destination.sin4.sin_family) {
+ throw std::runtime_error("The XPF destination and source addresses must be of the same family");
+ }
+
+ std::string ret;
+ const uint8_t version = source.isIPv4() ? 4 : 6;
+ const uint8_t protocol = tcp ? 6 : 17;
+ const size_t addrSize = source.isIPv4() ? sizeof(source.sin4.sin_addr.s_addr) : sizeof(source.sin6.sin6_addr.s6_addr);
+ const uint16_t sourcePort = source.sin4.sin_port;
+ const uint16_t destinationPort = destination.sin4.sin_port;
+
+ ret.reserve(sizeof(version) + sizeof(protocol) + (addrSize * 2) + sizeof(sourcePort) + sizeof(destinationPort));
+
+ ret.append(reinterpret_cast<const char*>(&version), sizeof(version));
+ ret.append(reinterpret_cast<const char*>(&protocol), sizeof(protocol));
+
+ // We already established source and destination sin_family equivalence
+ if (source.isIPv4()) {
+ assert(addrSize == sizeof(source.sin4.sin_addr.s_addr));
+ ret.append(reinterpret_cast<const char*>(&source.sin4.sin_addr.s_addr), addrSize);
+ assert(addrSize == sizeof(destination.sin4.sin_addr.s_addr));
+ ret.append(reinterpret_cast<const char*>(&destination.sin4.sin_addr.s_addr), addrSize);
+ }
+ else {
+ assert(addrSize == sizeof(source.sin6.sin6_addr.s6_addr));
+ ret.append(reinterpret_cast<const char*>(&source.sin6.sin6_addr.s6_addr), addrSize);
+ assert(addrSize == sizeof(destination.sin6.sin6_addr.s6_addr));
+ ret.append(reinterpret_cast<const char*>(&destination.sin6.sin6_addr.s6_addr), addrSize);
+ }
+
+ ret.append(reinterpret_cast<const char*>(&sourcePort), sizeof(sourcePort));
+ ret.append(reinterpret_cast<const char*>(&destinationPort), sizeof(destinationPort));
+
+ return ret;
+}
+
+bool parseXPFPayload(const char* payload, size_t len, ComboAddress& source, ComboAddress* destination)
+{
+ static const size_t addr4Size = sizeof(source.sin4.sin_addr.s_addr);
+ static const size_t addr6Size = sizeof(source.sin6.sin6_addr.s6_addr);
+ uint8_t version;
+ uint8_t protocol;
+ uint16_t sourcePort;
+ uint16_t destinationPort;
+
+ if (len != (sizeof(version) + sizeof(protocol) + (addr4Size * 2) + sizeof(sourcePort) + sizeof(destinationPort)) &&
+ len != (sizeof(version) + sizeof(protocol) + (addr6Size * 2) + sizeof(sourcePort) + sizeof(destinationPort))) {
+ return false;
+ }
+
+ size_t pos = 0;
+
+ memcpy(&version, payload + pos, sizeof(version));
+ pos += sizeof(version);
+
+ if (version != 4 && version != 6) {
+ return false;
+ }
+
+ memcpy(&protocol, payload + pos, sizeof(protocol));
+ pos += sizeof(protocol);
+
+ if (protocol != 6 && protocol != 17) {
+ return false;
+ }
+
+ const size_t addrSize = version == 4 ? sizeof(source.sin4.sin_addr.s_addr) : sizeof(source.sin6.sin6_addr.s6_addr);
+ if (len - pos != ((addrSize * 2) + sizeof(sourcePort) + sizeof(destinationPort))) {
+ return false;
+ }
+
+ source = makeComboAddressFromRaw(version, payload + pos, addrSize);
+ pos += addrSize;
+ if (destination != nullptr) {
+ *destination = makeComboAddressFromRaw(version, payload + pos, addrSize);
+ }
+ pos += addrSize;
+
+ memcpy(&sourcePort, payload + pos, sizeof(sourcePort));
+ pos += sizeof(sourcePort);
+ source.sin4.sin_port = sourcePort;
+
+ memcpy(&destinationPort, payload + pos, sizeof(destinationPort));
+ pos += sizeof(destinationPort);
+ if (destination != nullptr) {
+ destination->sin4.sin_port = destinationPort;
+ }
+
+ return true;
+}
--- /dev/null
+/*
+ * 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 <iputils.hh>
+
+std::string generateXPFPayload(bool tcp, const ComboAddress& source, const ComboAddress& destination);
+bool parseXPFPayload(const char* payload, size_t len, ComboAddress& source, ComboAddress* destination);
+
try {
recparts[1] = toCanonic(d_zonename, recparts[1]).toStringRootDot();
} catch (std::exception &e) {
- throw PDNSException("Error in record '" + rr.qname.toString() + " " + rr.qtype.getName() + "': " + e.what());
+ throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.getName() + "': " + e.what());
}
}
rr.content=recparts[0]+" "+recparts[1];
try {
recparts[3] = toCanonic(d_zonename, recparts[3]).toStringRootDot();
} catch (std::exception &e) {
- throw PDNSException("Error in record '" + rr.qname.toString() + " " + rr.qtype.getName() + "': " + e.what());
+ throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.getName() + "': " + e.what());
}
}
rr.content=recparts[0]+" "+recparts[1]+" "+recparts[2]+" "+recparts[3];
try {
rr.content = toCanonic(d_zonename, rr.content).toStringRootDot();
} catch (std::exception &e) {
- throw PDNSException("Error in record '" + rr.qname.toString() + " " + rr.qtype.getName() + "': " + e.what());
+ throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.getName() + "': " + e.what());
}
break;
case QType::AFSDB:
try {
recparts[1]=toCanonic(d_zonename, recparts[1]).toStringRootDot();
} catch (std::exception &e) {
- throw PDNSException("Error in record '" + rr.qname.toString() + " " + rr.qtype.getName() + "': " + e.what());
+ throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.getName() + "': " + e.what());
}
} else {
- throw PDNSException("AFSDB record for "+rr.qname.toString()+" invalid");
+ throw PDNSException("AFSDB record for "+rr.qname.toLogString()+" invalid");
}
rr.content.clear();
for(string::size_type n = 0; n < recparts.size(); ++n) {
case QType::SOA:
stringtok(recparts, rr.content);
if(recparts.size() > 7)
- throw PDNSException("SOA record contents for "+rr.qname.toString()+" contains too many parts");
+ throw PDNSException("SOA record contents for "+rr.qname.toLogString()+" contains too many parts");
if(recparts.size() > 1) {
try {
recparts[0]=toCanonic(d_zonename, recparts[0]).toStringRootDot();
recparts[1]=toCanonic(d_zonename, recparts[1]).toStringRootDot();
} catch (std::exception &e) {
- throw PDNSException("Error in record '" + rr.qname.toString() + " " + rr.qtype.getName() + "': " + e.what());
+ throw PDNSException("Error in record '" + rr.qname.toLogString() + " " + rr.qtype.getName() + "': " + e.what());
}
}
rr.content.clear();
print data
self.assertEquals(data['soa_edit_api'], 'DEFAULT')
+ def test_create_zone_with_soa_edit(self):
+ name, payload, data = self.create_zone(soa_edit='INCEPTION-INCREMENT', soa_edit_api='SOA-EDIT-INCREASE')
+ print data
+ self.assertEquals(data['soa_edit'], 'INCEPTION-INCREMENT')
+ self.assertEquals(data['soa_edit_api'], 'SOA-EDIT-INCREASE')
+ soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2]
+ # These particular settings lead to the first serial set to YYYYMMDD01.
+ self.assertEquals(soa_serial[-2:], '01')
+ rrset = {
+ 'changetype': 'replace',
+ 'name': name,
+ 'type': 'A',
+ 'ttl': 3600,
+ 'records': [
+ {
+ "content": "127.0.0.1",
+ "disabled": False
+ }
+ ]
+ }
+ payload = {'rrsets': [rrset]}
+ self.session.patch(
+ self.url("/api/v1/servers/localhost/zones/" + data['id']),
+ data=json.dumps(payload),
+ headers={'content-type': 'application/json'})
+ r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id']))
+ data = r.json()
+ soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2]
+ self.assertEquals(soa_serial[-2:], '02')
+
def test_create_zone_with_records(self):
name = unique_zone_name()
rrset = {
--- /dev/null
+[req]
+default_bits = 2048
+encrypt_key = no
+x509_extensions = custom_extensions
+prompt = no
+distinguished_name = distinguished_name
+
+[v3_ca]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = critical, CA:true
+
+[distinguished_name]
+CN = DNSDist TLS regression tests CA
+OU = PowerDNS.com BV
+countryName = NL
+
+[custom_extensions]
+basicConstraints = CA:true
+keyUsage = cRLSign, keyCertSign
--- /dev/null
+[req]
+default_bits = 2048
+encrypt_key = no
+prompt = no
+distinguished_name = server_distinguished_name
+
+[server_distinguished_name]
+CN = tls.tests.dnsdist.org
+OU = PowerDNS.com BV
+countryName = NL
+
import Queue
import os
import socket
+import ssl
import struct
import subprocess
import sys
return sock
@classmethod
- def sendTCPQueryOverConnection(cls, sock, query, rawQuery=False):
+ def openTLSConnection(cls, port, serverName, caCert=None, timeout=None):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ if timeout:
+ sock.settimeout(timeout)
+
+ # 2.7.9+
+ if hasattr(ssl, 'create_default_context'):
+ sslctx = ssl.create_default_context(cafile=caCert)
+ sslsock = sslctx.wrap_socket(sock, server_hostname=serverName)
+ else:
+ sslsock = ssl.wrap_socket(sock, ca_certs=caCert, cert_reqs=ssl.CERT_REQUIRED)
+
+ sslsock.connect(("127.0.0.1", port))
+ return sslsock
+
+ @classmethod
+ def sendTCPQueryOverConnection(cls, sock, query, rawQuery=False, response=None, timeout=2.0):
if not rawQuery:
wire = query.to_wire()
else:
wire = query
+ if response:
+ cls._toResponderQueue.put(response, True, timeout)
+
sock.send(struct.pack("!H", len(wire)))
sock.send(wire)
@classmethod
- def recvTCPResponseOverConnection(cls, sock):
+ def recvTCPResponseOverConnection(cls, sock, useQueue=False, timeout=2.0):
message = None
data = sock.recv(2)
if data:
data = sock.recv(datalen)
if data:
message = dns.message.from_wire(data)
- return message
+
+ if useQueue and not cls._fromResponderQueue.empty():
+ receivedQuery = cls._fromResponderQueue.get(True, timeout)
+ return (receivedQuery, message)
+ else:
+ return message
@classmethod
def sendTCPQuery(cls, query, response, useQueue=True, timeout=2.0, rawQuery=False):
if [ "${PDNS_DEBUG}" = "YES" ]; then
set -x
fi
+
+# Generate a new CA
+openssl req -new -x509 -days 1 -extensions v3_ca -keyout ca.key -out ca.pem -nodes -config configCA.conf
+# Generate a new server certificate request
+openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -config configServer.conf
+# Sign the server cert
+openssl x509 -req -days 1 -CA ca.pem -CAkey ca.key -CAcreateserial -in server.csr -out server.pem
+# Generate a chain
+cat server.pem ca.pem >> server.chain
+
nosetests --with-xunit $@
+
+rm ca.key ca.pem ca.srl server.csr server.key server.pem server.chain
self.assertEquals(content['daemon_type'], 'dnsdist')
- for key in ['version', 'acl', 'local', 'rules', 'response-rules', 'cache-hit-response-rules', 'servers', 'frontends', 'pools']:
+ rule_groups = ['response-rules', 'cache-hit-response-rules', 'self-answered-response-rules']
+ for key in ['version', 'acl', 'local', 'rules', 'servers', 'frontends', 'pools'] + rule_groups:
self.assertIn(key, content)
for rule in content['rules']:
- for key in ['id', 'matches', 'rule', 'action']:
+ for key in ['id', 'matches', 'rule', 'action', 'uuid']:
self.assertIn(key, rule)
for key in ['id', 'matches']:
self.assertTrue(rule[key] >= 0)
- for rule in content['response-rules']:
- for key in ['id', 'matches', 'rule', 'action']:
- self.assertIn(key, rule)
- for key in ['id', 'matches']:
- self.assertTrue(rule[key] >= 0)
-
- for rule in content['cache-hit-response-rules']:
- for key in ['id', 'matches', 'rule', 'action']:
- self.assertIn(key, rule)
- for key in ['id', 'matches']:
- self.assertTrue(rule[key] >= 0)
+ for rule_group in rule_groups:
+ for rule in content[rule_group]:
+ for key in ['id', 'matches', 'rule', 'action', 'uuid']:
+ self.assertIn(key, rule)
+ for key in ['id', 'matches']:
+ self.assertTrue(rule[key] >= 0)
for server in content['servers']:
for key in ['id', 'latency', 'name', 'weight', 'outstanding', 'qpsLimit',
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEquals(receivedResponse, response)
+
+class TestAdvancedLuaTempFailureTTL(DNSDistTest):
+
+ _config_template = """
+ function testAction(dq)
+ if dq.tempFailureTTL ~= nil then
+ return DNSAction.Spoof, "initially.not.nil.but." + dq.tempFailureTTL + ".tests.powerdns.com."
+ end
+ dq.tempFailureTTL = 30
+ if dq.tempFailureTTL ~= 30 then
+ return DNSAction.Spoof, "after.set.not.expected.value.but." + dq.tempFailureTTL + ".tests.powerdns.com."
+ end
+ dq.tempFailureTTL = nil
+ if dq.tempFailureTTL ~= nil then
+ return DNSAction.Spoof, "after.unset.not.nil.but." + dq.tempFailureTTL + ".tests.powerdns.com."
+ end
+ return DNSAction.None, ""
+ end
+ addAction(AllRule(), LuaAction(testAction))
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testTempFailureTTLBinding(self):
+ """
+ Exercise dq.tempFailureTTL Lua binding
+ """
+ name = 'tempfailurettlbinding.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
self.assertEquals(receivedResponse, differentCaseResponse)
+class TestTempFailureCacheTTLAction(DNSDistTest):
+
+ _config_template = """
+ pc = newPacketCache(100, 86400, 1)
+ getPool(""):setCache(pc)
+ addAction("servfail.cache.tests.powerdns.com.", TempFailureCacheTTLAction(1))
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testTempFailureCacheTTLAction(self):
+ """
+ Cache: When a TempFailure TTL is set, it should be honored
+
+ dnsdist is configured to cache packets, plus a specific qname is
+ set up with a lower TempFailure Cache TTL. we are sending one request
+ (cache miss) and verify that the cache is hit for the following query,
+ but the TTL then expires before the larger "good" packetcache TTL.
+ """
+ name = 'servfail.cache.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'AAAA', 'IN')
+ response = dns.message.make_response(query)
+ response.set_rcode(dns.rcode.SERVFAIL)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ # next query should hit the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertFalse(receivedQuery)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, response)
+
+ # now we wait a bit for the Failure-Cache TTL to expire
+ time.sleep(2)
+
+ # next query should NOT hit the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, response)
+
+
class TestCachingWithExistingEDNS(DNSDistTest):
_config_template = """
import clientsubnetoption
import cookiesoption
from dnsdisttests import DNSDistTest
+from datetime import datetime, timedelta
class TestEdnsClientSubnetNoOverride(DNSDistTest):
"""
self.assertEquals(response, receivedResponse)
self.assertTrue((end - begin) < timedelta(0, 1))
+class TestResponseRuleERCode(DNSDistTest):
+
+ _config_template = """
+ newServer{address="127.0.0.1:%s"}
+ addResponseAction(ERCodeRule(dnsdist.BADVERS), DelayResponseAction(1000))
+ """
+
+ def testBADVERSDelayed(self):
+ """
+ Responses: Delayed on BADVERS
+
+ Send an A query to "delayed.responses.tests.powerdns.com.",
+ check that the response delay is longer than 1000 ms
+ for a BADVERS response over UDP, shorter for BADKEY and NoError.
+ """
+ name = 'delayed.responses.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ response.use_edns(edns=True)
+
+ # BADVERS over UDP
+ # BADVERS == 16, so rcode==0, ercode==1
+ response.set_rcode(dns.rcode.BADVERS)
+ begin = datetime.now()
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ end = datetime.now()
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ self.assertTrue((end - begin) > timedelta(0, 1))
+
+ # BADKEY (17, an ERCode) over UDP
+ response.set_rcode(17)
+ begin = datetime.now()
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ end = datetime.now()
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ self.assertTrue((end - begin) < timedelta(0, 1))
+
+ # NoError (non-ERcode, basic RCode bits match BADVERS) over UDP
+ response.set_rcode(dns.rcode.NOERROR)
+ begin = datetime.now()
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ end = datetime.now()
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ self.assertTrue((end - begin) < timedelta(0, 1))
+
class TestResponseRuleQNameDropped(DNSDistTest):
_config_template = """
--- /dev/null
+#!/usr/bin/env python
+import dns
+from dnsdisttests import DNSDistTest
+
+class TestSelfAnsweredResponses(DNSDistTest):
+
+ _config_template = """
+ -- this is a silly test config, please do not do this in production.
+ addAction(makeRule("udp.selfanswered.tests.powerdns.com."), SpoofAction("192.0.2.1"))
+ addSelfAnsweredResponseAction(AndRule({makeRule("udp.selfanswered.tests.powerdns.com."), NotRule(MaxQPSRule(1))}), DropResponseAction())
+ addAction(makeRule("tcp.selfanswered.tests.powerdns.com."), SpoofAction("192.0.2.1"))
+ addSelfAnsweredResponseAction(AndRule({makeRule("tcp.selfanswered.tests.powerdns.com."), NotRule(MaxQPSRule(1))}), DropResponseAction())
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testSelfAnsweredUDP(self):
+ """
+ CacheHitResponse: Drop when served from the cache
+ """
+ ttl = 60
+ name = 'udp.selfanswered.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+ response.flags |= dns.flags.RA
+
+ # self-answered, but no SelfAnswered rule matches.
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, response)
+
+ # self-answered, AND SelfAnswered rule matches. Should not see a reply.
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertIsNone(receivedResponse)
+
+ def testSelfAnsweredTCP(self):
+ """
+ testSelfAnsweredTCP: Drop after exceeding QPS
+ """
+ ttl = 60
+ name = 'tcp.selfanswered.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+ response.flags |= dns.flags.RA
+
+ # self-answered, but no SelfAnswered rule matches.
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, response)
+
+ # self-answered, AND SelfAnswered rule matches. Should not see a reply.
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertIsNone(receivedResponse)
--- /dev/null
+#!/usr/bin/env python
+import dns
+from dnsdisttests import DNSDistTest
+
+class TestTLS(DNSDistTest):
+
+ _serverKey = 'server.key'
+ _serverCert = 'server.chain'
+ _serverName = 'tls.tests.dnsdist.org'
+ _caCert = 'ca.pem'
+ _tlsServerPort = 8453
+ _config_template = """
+ newServer{address="127.0.0.1:%s"}
+ addTLSLocal("127.0.0.1:%s", "%s", "%s")
+ """
+ _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
+
+ def testTLSSimple(self):
+ """
+ TLS: Single query
+ """
+ name = 'single.tls.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+
+ conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
+
+ self.sendTCPQueryOverConnection(conn, query, response=response)
+ (receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(conn, useQueue=True)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
--- /dev/null
+#!/usr/bin/env python
+
+import dns
+from dnsdisttests import DNSDistTest
+
+class XPFTest(DNSDistTest):
+ """
+ dnsdist is configured to add XPF to the query
+ """
+
+ _xpfCode = 65422
+ _config_template = """
+ newServer{address="127.0.0.1:%d", addXPF=%d}
+ """
+ _config_params = ['_testServerPort', '_xpfCode']
+
+ def checkMessageHasXPF(self, msg, expectedValue):
+ self.assertGreaterEqual(len(msg.additional), 1)
+
+ found = False
+ for add in msg.additional:
+ if add.rdtype == self._xpfCode:
+ found = True
+ self.assertEquals(add.rdclass, dns.rdataclass.IN)
+ self.assertEquals(add.ttl, 0)
+ xpfData = add.to_rdataset()[0].to_text()
+ # skip the ports
+ self.assertEquals(xpfData[:26], expectedValue[:26])
+
+ self.assertTrue(found)
+
+ def testXPF(self):
+ """
+ XPF
+ """
+ name = 'xpf.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+
+ expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ # 0x04 is IPv4, 0x11 (17) is UDP then 127.0.0.1 as source and destination
+ # and finally the ports, zeroed because we have no way to know them beforehand
+ xpfData = "\# 14 04117f0000017f00000100000000"
+ rdata = dns.rdata.from_text(dns.rdataclass.IN, self._xpfCode, xpfData)
+ rrset = dns.rrset.from_rdata(name, 60, rdata)
+ expectedQuery.additional.append(rrset)
+
+ response = dns.message.make_response(expectedQuery)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ receivedResponse.id = response.id
+
+ self.assertEquals(receivedQuery, expectedQuery)
+ self.checkMessageHasXPF(receivedQuery, xpfData)
+ self.assertEquals(response, receivedResponse)
+
+ expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ # 0x04 is IPv4, 0x06 (6) is TCP then 127.0.0.1 as source and destination
+ # and finally the ports, zeroed because we have no way to know them beforehand
+ xpfData = "\# 14 04067f0000017f00000100000000"
+ rdata = dns.rdata.from_text(dns.rdataclass.IN, self._xpfCode, xpfData)
+ rrset = dns.rrset.from_rdata(name, 60, rdata)
+ expectedQuery.additional.append(rrset)
+
+ response = dns.message.make_response(expectedQuery)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ receivedResponse.id = response.id
+
+ self.assertEquals(receivedQuery, expectedQuery)
+ self.checkMessageHasXPF(receivedQuery, xpfData)
+ self.assertEquals(response, receivedResponse)
for ans in msg.answer:
ret += "%s\n" % ans.to_text()
if ans.match(rrset.name, rrset.rdclass, rrset.rdtype, 0, None):
- self.assertEqual(ans, rrset)
+ self.assertEqual(ans, rrset, "'%s' != '%s'" % (ans.to_text(), rrset.to_text()))
found = True
if not found:
export PDNSUTIL=${PDNSUTIL:-${PWD}/../pdns/pdnsutil}
export PDNSRECURSOR=${PDNSRECURSOR:-${PWD}/../pdns/recursordist/pdns_recursor}
export RECCONTROL=${RECCONTROL:-${PWD}/../pdns/recursordist/rec_control}
-export LIBFAKETIME=${LIBFAKETIME:-/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1} # ubuntu default
+
+LIBFAKETIME_DEFAULT=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 # ubuntu default
+LIBAUTHBIND_DEFAULT=/usr/lib/authbind/libauthbind.so.1
+if [ $(uname -s) = "Darwin" ]; then
+ # macOS is not /really/ supported here; it works for some tests, and then you might need sudo.
+ LIBFAKETIME_DEFAULT=/usr/local/lib/faketime/libfaketime.1.dylib
+ LIBAUTHBIND_DEFAULT=""
+fi
+
+export LIBFAKETIME=${LIBFAKETIME:-$LIBFAKETIME_DEFAULT}
+export LIBAUTHBIND=${LIBAUTHBIND:-$LIBAUTHBIND_DEFAULT}
export PREFIX=127.0.0
+for bin in "$PDNS" "$PDNSUTIL" "$PDNSRECURSOR" "$RECCONTROL" "$LIBFAKETIME" "$LIBAUTHBIND"; do
+ if [ -n "$bin" -a ! -e "$bin" ]; then
+ echo "E: Required binary $bin not found. Please install the binary and/or edit ./vars."
+ exit 1
+ fi
+done
set -e
if [ "${PDNS_DEBUG}" = "YES" ]; then
set -x
fi
-LD_PRELOAD="/usr/lib/authbind/libauthbind.so.1 ${LIBFAKETIME}" nosetests -I test_WellKnown.py --with-xunit $@
+LD_PRELOAD="${LIBAUTHBIND} ${LIBFAKETIME}" nosetests -I test_WellKnown.py --with-xunit $@
daemon=no
trace=yes
dont-query=
+ecs-add-for=0.0.0.0/0
local-address=127.0.0.1
packetcache-ttl=0
packetcache-servfail-ttl=0