void pdns_ffi_param_set_rcode(pdns_ffi_param_t* ref, int rcode) __attribute__ ((visibility ("default")));
void pdns_ffi_param_set_follow_cname_records(pdns_ffi_param_t* ref, bool follow) __attribute__ ((visibility ("default")));
+ void pdns_ffi_param_set_extended_error_code(pdns_ffi_param_t* ref, uint16_t code) __attribute__ ((visibility ("default")));
+ void pdns_ffi_param_set_extended_error_extra(pdns_ffi_param_t* ref, size_t len, const char* extra) __attribute__ ((visibility ("default")));
+
/* returns true if the record was correctly added, false if something went wrong.
Passing a NULL pointer to 'name' will result in the qname being used for the record owner name. */
bool pdns_ffi_param_add_record(pdns_ffi_param_t *ref, const char* name, uint16_t type, uint32_t ttl, const char* content, size_t contentSize, pdns_record_place_t place) __attribute__ ((visibility ("default")));
d_lw->registerMember("followupPrefix", &DNSQuestion::followupPrefix);
d_lw->registerMember("followupName", &DNSQuestion::followupName);
d_lw->registerMember("data", &DNSQuestion::data);
+ d_lw->registerMember<uint16_t (DNSQuestion::*)>("extendedErrorCode", [](const DNSQuestion& dq) -> uint16_t {
+ if (dq.extendedErrorCode && *dq.extendedErrorCode) {
+ return *(*dq.extendedErrorCode);
+ }
+ return 0;
+ },
+ [](DNSQuestion& dq, uint16_t newCode) {
+ if (dq.extendedErrorCode) {
+ *dq.extendedErrorCode = newCode;
+ }
+ });
+ d_lw->registerMember<std::string (DNSQuestion::*)>("extendedErrorExtra", [](const DNSQuestion& dq) -> std::string {
+ if (dq.extendedErrorExtra) {
+ return *dq.extendedErrorExtra;
+ }
+ return "";
+ },
+ [](DNSQuestion& dq, const std::string& newExtra) {
+ if (dq.extendedErrorExtra) {
+ *dq.extendedErrorExtra = newExtra;
+ }
+ });
d_lw->registerMember("udpQuery", &DNSQuestion::udpQuery);
d_lw->registerMember("udpAnswer", &DNSQuestion::udpAnswer);
d_lw->registerMember("udpQueryDest", &DNSQuestion::udpQueryDest);
d_lw->registerMember("udpCallback", &DNSQuestion::udpCallback);
d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy);
+
d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyName",
[](const DNSFilterEngine::Policy& pol) -> std::string {
return pol.getName();
d_lw->registerFunction("getProxyProtocolValues", &DNSQuestion::getProxyProtocolValues);
d_lw->registerFunction("getEDNSFlags", &DNSQuestion::getEDNSFlags);
d_lw->registerFunction("getEDNSFlag", &DNSQuestion::getEDNSFlag);
+
d_lw->registerMember("name", &DNSRecord::d_name);
d_lw->registerMember("type", &DNSRecord::d_type);
d_lw->registerMember("ttl", &DNSRecord::d_ttl);
struct pdns_ffi_param
{
public:
- pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::unordered_set<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector<ProxyProtocolValue>& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, std::string& routingTag_, boost::optional<int>& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), proxyProtocolValues(proxyProtocolValues_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), routingTag(routingTag_), rcode(rcode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_)
+ pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::unordered_set<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector<ProxyProtocolValue>& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, std::string& routingTag_, boost::optional<int>& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_, boost::optional<uint16_t>& extendedErrorCode_, std::string& extendedErrorExtra_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), proxyProtocolValues(proxyProtocolValues_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), routingTag(routingTag_), extendedErrorExtra(extendedErrorExtra_), rcode(rcode_), extendedErrorCode(extendedErrorCode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_)
{
}
std::string& deviceId;
std::string& deviceName;
std::string& routingTag;
+ std::string& extendedErrorExtra;
boost::optional<int>& rcode;
+ boost::optional<uint16_t>& extendedErrorCode;
uint32_t& ttlCap;
bool& variable;
bool& logQuery;
bool tcp;
};
-unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const
+unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords, boost::optional<uint16_t>& extendedErrorCode, std::string& extendedErrorExtra) const
{
if (d_gettag_ffi) {
- pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, tcp, logQuery, logResponse, followCNAMERecords);
+ pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, tcp, logQuery, logResponse, followCNAMERecords, extendedErrorCode, extendedErrorExtra);
auto ret = d_gettag_ffi(¶m);
if (ret) {
ref->followCNAMERecords = follow;
}
+void pdns_ffi_param_set_extended_error_code(pdns_ffi_param_t* ref, uint16_t code)
+{
+ ref->extendedErrorCode = code;
+}
+
+void pdns_ffi_param_set_extended_error_extra(pdns_ffi_param_t* ref, size_t len, const char* extra)
+{
+ ref->extendedErrorExtra = std::string(extra, len);
+}
+
bool pdns_ffi_param_add_record(pdns_ffi_param_t *ref, const char* name, uint16_t type, uint32_t ttl, const char* content, size_t contentSize, pdns_record_place_t place)
{
try {
std::unordered_set<std::string>* policyTags{nullptr};
const std::vector<ProxyProtocolValue>* proxyProtocolValues{nullptr};
std::unordered_map<std::string, bool>* discardedPolicies{nullptr};
+ std::string* extendedErrorExtra{nullptr};
+ boost::optional<uint16_t>* extendedErrorCode{nullptr};
std::string requestorId;
std::string deviceId;
std::string deviceName;
};
unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const;
- unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const;
+ unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords, boost::optional<uint16_t>& extendedErrorCode, std::string& extendedErrorExtra) const;
void maintenance() const;
bool prerpz(DNSQuestion& dq, int& ret) const;
LuaContext::LuaObject d_data;
EDNSSubnetOpts d_ednssubnet;
shared_ptr<TCPConnection> d_tcpConnection;
+ boost::optional<uint16_t> d_extendedErrorCode{boost::none};
+ string d_extendedErrorExtra;
boost::optional<int> d_rcode{boost::none};
int d_socket{-1};
unsigned int d_tag{0};
dq.deviceName = dc->d_deviceName;
#endif
dq.proxyProtocolValues = &dc->d_proxyProtocolValues;
+ dq.extendedErrorCode = &dc->d_extendedErrorCode;
+ dq.extendedErrorExtra = &dc->d_extendedErrorExtra;
if(ednsExtRCode != 0) {
goto sendit;
if (haveEDNS) {
auto state = sr.getValidationState();
- if (s_addExtendedDNSErrors && vStateIsBogus(state) && pw.size() < maxanswersize && (maxanswersize - pw.size()) > (2 + 2 + 2)) {
+ if (s_addExtendedDNSErrors && (dc->d_extendedErrorCode || vStateIsBogus(state))) {
EDNSExtendedError::code code;
+ std::string extra;
- switch (state) {
- case vState::BogusNoValidDNSKEY:
- code = EDNSExtendedError::code::DNSKEYMissing;
- break;
- case vState::BogusInvalidDenial:
- code = EDNSExtendedError::code::NSECMissing;
- break;
- case vState::BogusUnableToGetDSs:
- code = EDNSExtendedError::code::DNSSECBogus;
- break;
- case vState::BogusUnableToGetDNSKEYs:
- code = EDNSExtendedError::code::DNSKEYMissing;
- break;
- case vState::BogusSelfSignedDS:
- code = EDNSExtendedError::code::DNSSECBogus;
- break;
- case vState::BogusNoRRSIG:
- code = EDNSExtendedError::code::RRSIGsMissing;
- break;
- case vState::BogusNoValidRRSIG:
- code = EDNSExtendedError::code::DNSSECBogus;
- break;
- case vState::BogusMissingNegativeIndication:
- code = EDNSExtendedError::code::NSECMissing;
- break;
- case vState::BogusSignatureNotYetValid:
- code = EDNSExtendedError::code::SignatureNotYetValid;
- break;
- case vState::BogusSignatureExpired:
- code = EDNSExtendedError::code::SignatureExpired;
- break;
- case vState::BogusUnsupportedDNSKEYAlgo:
- code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
- break;
- case vState::BogusUnsupportedDSDigestType:
- code = EDNSExtendedError::code::UnsupportedDSDigestType;
- break;
- case vState::BogusNoZoneKeyBitSet:
- code = EDNSExtendedError::code::NoZoneKeyBitSet;
- break;
- case vState::BogusRevokedDNSKEY:
- code = EDNSExtendedError::code::DNSSECBogus;
- break;
- case vState::BogusInvalidDNSKEYProtocol:
- code = EDNSExtendedError::code::DNSSECBogus;
- break;
- default:
- throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
+ if (dc->d_extendedErrorCode) {
+ code = static_cast<EDNSExtendedError::code>(*dc->d_extendedErrorCode);
+ extra = std::move(dc->d_extendedErrorExtra);
+ }
+ else {
+ switch (state) {
+ case vState::BogusNoValidDNSKEY:
+ code = EDNSExtendedError::code::DNSKEYMissing;
+ break;
+ case vState::BogusInvalidDenial:
+ code = EDNSExtendedError::code::NSECMissing;
+ break;
+ case vState::BogusUnableToGetDSs:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusUnableToGetDNSKEYs:
+ code = EDNSExtendedError::code::DNSKEYMissing;
+ break;
+ case vState::BogusSelfSignedDS:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusNoRRSIG:
+ code = EDNSExtendedError::code::RRSIGsMissing;
+ break;
+ case vState::BogusNoValidRRSIG:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusMissingNegativeIndication:
+ code = EDNSExtendedError::code::NSECMissing;
+ break;
+ case vState::BogusSignatureNotYetValid:
+ code = EDNSExtendedError::code::SignatureNotYetValid;
+ break;
+ case vState::BogusSignatureExpired:
+ code = EDNSExtendedError::code::SignatureExpired;
+ break;
+ case vState::BogusUnsupportedDNSKEYAlgo:
+ code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
+ break;
+ case vState::BogusUnsupportedDSDigestType:
+ code = EDNSExtendedError::code::UnsupportedDSDigestType;
+ break;
+ case vState::BogusNoZoneKeyBitSet:
+ code = EDNSExtendedError::code::NoZoneKeyBitSet;
+ break;
+ case vState::BogusRevokedDNSKEY:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusInvalidDNSKEYProtocol:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ default:
+ throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
+ }
}
EDNSExtendedError eee;
eee.infoCode = static_cast<uint16_t>(code);
- returnedEdnsOptions.push_back(make_pair(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee)));
+ eee.extraText = std::move(extra);
+
+ if (pw.size() < maxanswersize && (maxanswersize - pw.size()) >= (2 + 2 + 2 + eee.extraText.size())) {
+ returnedEdnsOptions.push_back(make_pair(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee)));
+ }
}
/* we try to add the EDNS OPT RR even for truncated answers,
if(t_pdl) {
try {
if (t_pdl->d_gettag_ffi) {
- dc->d_tag = t_pdl->gettag_ffi(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_records, dc->d_data, ednsOptions, true, dc->d_proxyProtocolValues, requestorId, deviceId, deviceName, dc->d_routingTag, dc->d_rcode, dc->d_ttlCap, dc->d_variable, logQuery, dc->d_logResponse, dc->d_followCNAMERecords);
+ dc->d_tag = t_pdl->gettag_ffi(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_records, dc->d_data, ednsOptions, true, dc->d_proxyProtocolValues, requestorId, deviceId, deviceName, dc->d_routingTag, dc->d_rcode, dc->d_ttlCap, dc->d_variable, logQuery, dc->d_logResponse, dc->d_followCNAMERecords, dc->d_extendedErrorCode, dc->d_extendedErrorExtra);
}
else if (t_pdl->d_gettag) {
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, deviceName, dc->d_routingTag, dc->d_proxyProtocolValues);
bool ecsFound = false;
bool ecsParsed = false;
std::vector<DNSRecord> records;
+ std::string extendedErrorExtra;
boost::optional<int> rcode = boost::none;
+ boost::optional<uint16_t> extendedErrorCode{boost::none};
uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
bool variable = false;
bool followCNAMEs = false;
if(t_pdl) {
try {
if (t_pdl->d_gettag_ffi) {
- ctag = t_pdl->gettag_ffi(source, ednssubnet.source, destination, qname, qtype, &policyTags, records, data, ednsOptions, false, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, logQuery, logResponse, followCNAMEs);
+ ctag = t_pdl->gettag_ffi(source, ednssubnet.source, destination, qname, qtype, &policyTags, records, data, ednsOptions, false, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, logQuery, logResponse, followCNAMEs, extendedErrorCode, extendedErrorExtra);
}
else if (t_pdl->d_gettag) {
ctag = t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
#endif
dc->d_proxyProtocolValues = std::move(proxyProtocolValues);
dc->d_routingTag = std::move(routingTag);
+ dc->d_extendedErrorCode = extendedErrorCode;
+ dc->d_extendedErrorExtra = std::move(extendedErrorExtra);
MT->makeThread(startDoResolve, (void*) dc.release()); // deletes dc
return 0;
std::string extra;
/* the size of an EDNS option is limited to 2^16-1, and in this case the code already adds 2 bytes */
- extra.resize(65535);
+ extra.resize(65534);
BOOST_CHECK_THROW(checkExtendedErrorOptionValidity(EDNSExtendedError::code::Other, extra), std::runtime_error);
EDNSExtendedError parsed;
+++ /dev/null
-#!/usr/bin/env python2
-
-import dns
-import dns.edns
-import dns.flags
-import dns.message
-import dns.query
-
-class CookiesOption(dns.edns.Option):
- """Implementation of draft-ietf-dnsop-cookies-09.
- """
-
- def __init__(self, client, server):
- super(CookiesOption, self).__init__(10)
-
- if len(client) != 8:
- raise Exception('invalid client cookie length')
-
- if server is not None and len(server) != 0 and (len(server) < 8 or len(server) > 32):
- raise Exception('invalid server cookie length')
-
- self.client = client
- self.server = server
-
- def to_wire(self, file=None):
- """Create EDNS packet as defined in draft-ietf-dnsop-cookies-09."""
-
- if self.server and len(self.server) > 0:
- data = self.client + self.server
- else:
- data = self.client
-
- if file:
- file.write(data)
- else:
- return data
-
- def from_wire(cls, otype, wire, current, olen):
- """Read EDNS packet as defined in draft-ietf-dnsop-cookies-09.
-
- Returns:
- An instance of CookiesOption based on the EDNS packet
- """
-
- data = wire[current:current + olen]
- if len(data) != 8 and (len(data) < 16 or len(data) > 40):
- raise Exception('Invalid EDNS Cookies option')
-
- client = data[:8]
- if len(data) > 8:
- server = data[8:]
- else:
- server = None
-
- return cls(client, server)
-
- from_wire = classmethod(from_wire)
-
- # needed in 2.0.0
- @classmethod
- def from_wire_parser(cls, otype, parser):
- data = parser.get_remaining()
-
- if len(data) != 8 and (len(data) < 16 or len(data) > 40):
- raise Exception('Invalid EDNS Cookies option')
-
- client = data[:8]
- if len(data) > 8:
- server = data[8:]
- else:
- server = None
-
- return cls(client, server)
-
- def __repr__(self):
- return '%s(%s, %s)' % (
- self.__class__.__name__,
- self.client,
- self.server
- )
-
- def to_text(self):
- return self.__repr__()
-
- def __eq__(self, other):
- if not isinstance(other, CookiesOption):
- return False
- if self.client != other.client:
- return False
- if self.server != other.server:
- return False
- return True
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
-
-dns.edns._type_to_class[0x000A] = CookiesOption
--- /dev/null
+../regression-tests.dnsdist/cookiesoption.py
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python
+import struct
+
+import dns
+import dns.edns
+import dns.flags
+import dns.message
+import dns.query
+
+class ExtendedErrorOption(dns.edns.Option):
+ """Implementation of rfc8914
+ """
+
+ def __init__(self, code, extra):
+ super(ExtendedErrorOption, self).__init__(15)
+
+ self.code = code
+ self.extra = extra
+
+ def to_wire(self, file=None):
+ """Create EDNS packet."""
+
+ data = struct.pack('!H', self.code)
+ data = data + self.extra
+ if file:
+ file.write(data)
+ else:
+ return data
+
+ def from_wire(cls, otype, wire, current, olen):
+ """Read EDNS packet.
+
+ Returns:
+ An instance of ExtendedErrorOption based on the EDNS packet
+ """
+
+ if olen < 2:
+ raise Exception('Invalid EDNS Extended Error option')
+
+ (code,) = struct.unpack('!H', wire[current:current+2])
+ if olen > 2:
+ extra = wire[current + 2:current + olen]
+ else:
+ extra = b''
+
+ return cls(code, extra)
+
+ from_wire = classmethod(from_wire)
+
+ # needed in 2.0.0
+ @classmethod
+ def from_wire_parser(cls, otype, parser):
+ data = parser.get_remaining()
+
+ if len(data) < 2:
+ raise Exception('Invalid EDNS Extended Error option')
+
+ (code,) = struct.unpack('!H', data[0:2])
+ if len(data) > 2:
+ extra = data[2:]
+ else:
+ extra = b''
+
+ return cls(code, extra)
+
+ def __repr__(self):
+ return '%s(%d, %s)' % (
+ self.__class__.__name__,
+ self.code,
+ self.extra
+ )
+
+ def to_text(self):
+ return self.__repr__()
+
+ def __eq__(self, other):
+ if not isinstance(other, ExtendedErrorOption):
+ return False
+ if self.code != other.code:
+ return False
+ if self.extra != other.extra:
+ return False
+ return True
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
+dns.edns._type_to_class[0x000F] = ExtendedErrorOption
--- /dev/null
+import dns
+import os
+import extendederrors
+
+from recursortests import RecursorTest
+
+class ExtendedErrorsRecursorTest(RecursorTest):
+
+ _confdir = 'ExtendedErrors'
+ _config_template_default = """
+dnssec=validate
+daemon=no
+trace=yes
+packetcache-ttl=0
+packetcache-servfail-ttl=0
+max-cache-ttl=15
+threads=1
+loglevel=9
+disable-syslog=yes
+log-common-errors=yes
+"""
+ _config_template = """
+ extended-errors=yes
+ """
+ _lua_dns_script_file = """
+ function preresolve(dq)
+ if dq.qname == newDN('fromlua.extended.') then
+ dq.extendedErrorCode = 10
+ dq.extendedErrorExtra = "Extra text from Lua!"
+ return true
+ end
+ if dq.qname == newDN('toolarge.extended.') then
+ dq:addRecord(pdns.TXT, '%s', pdns.place.ANSWER)
+ dq.extendedErrorCode = 10
+ dq.extendedErrorExtra = "Extra text from Lua!"
+ return true
+ end
+ return false
+ end
+
+ local ffi = require("ffi")
+
+ ffi.cdef[[
+ typedef struct pdns_ffi_param pdns_ffi_param_t;
+
+ const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref) __attribute__ ((visibility ("default")));
+ void pdns_ffi_param_set_rcode(pdns_ffi_param_t* ref, int rcode) __attribute__ ((visibility ("default")));
+ void pdns_ffi_param_set_extended_error_code(pdns_ffi_param_t* ref, uint16_t code) __attribute__ ((visibility ("default")));
+ void pdns_ffi_param_set_extended_error_extra(pdns_ffi_param_t* ref, size_t len, const char* extra);
+ ]]
+
+ function gettag_ffi(obj)
+ local qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
+ if qname == 'fromluaffi.extended' then
+ ffi.C.pdns_ffi_param_set_rcode(obj, 0)
+ ffi.C.pdns_ffi_param_set_extended_error_code(obj, 10)
+ local extra = 'Extra text from Lua FFI!'
+ ffi.C.pdns_ffi_param_set_extended_error_extra(obj, #extra, extra)
+ end
+ return {}
+ end
+ """ % ('A'*427)
+
+ _roothints = None
+
+ @classmethod
+ def setUpClass(cls):
+
+ # we don't need all the auth stuff
+ cls.setUpSockets()
+ cls.startResponders()
+
+ confdir = os.path.join('configs', cls._confdir)
+ cls.createConfigDir(confdir)
+
+ cls.generateRecursorConfig(confdir)
+ cls.startRecursor(confdir, cls._recursorPort)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.tearDownRecursor()
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ super(ExtendedErrorsRecursorTest, cls).generateRecursorConfig(confdir)
+
+ def testNotIncepted(self):
+ qname = 'signotincepted.bad-dnssec.wb.sidnlabs.nl.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(8, b''))
+
+ def testExpired(self):
+ qname = 'sigexpired.bad-dnssec.wb.sidnlabs.nl.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(7, b''))
+
+ def testBogus(self):
+ qname = 'unknownalgorithm.bad-dnssec.wb.sidnlabs.nl.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(6, b''))
+
+ def testMissingRRSIG(self):
+ qname = 'brokendnssec.net.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b''))
+
+ def testFromLua(self):
+ qname = 'fromlua.extended.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b'Extra text from Lua!'))
+
+ def testFromLuaFFI(self):
+ qname = 'fromluaffi.extended.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b'Extra text from Lua FFI!'))
+
+ def testTooLarge(self):
+ qname = 'toolarge.extended.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True, payload=512)
+
+ # should not have the Extended Option since the packet is too large already
+ res = self.sendUDPQuery(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEquals(len(res.answer), 1)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 0)
+
+ res = self.sendTCPQuery(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEquals(len(res.answer), 1)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 1)
+ self.assertEqual(res.options[0].otype, 15)
+ self.assertEqual(res.options[0], extendederrors.ExtendedErrorOption(10, b'Extra text from Lua!'))
+
+class NoExtendedErrorsRecursorTest(RecursorTest):
+
+ _confdir = 'ExtendedErrorsDisabled'
+ _config_template_default = """
+dnssec=validate
+daemon=no
+trace=yes
+packetcache-ttl=0
+packetcache-servfail-ttl=0
+max-cache-ttl=15
+threads=1
+loglevel=9
+disable-syslog=yes
+log-common-errors=yes
+"""
+ _config_template = """
+ extended-errors=no
+ """
+ _roothints = None
+
+ @classmethod
+ def setUpClass(cls):
+
+ # we don't need all the auth stuff
+ cls.setUpSockets()
+ cls.startResponders()
+
+ confdir = os.path.join('configs', cls._confdir)
+ cls.createConfigDir(confdir)
+
+ cls.generateRecursorConfig(confdir)
+ cls.startRecursor(confdir, cls._recursorPort)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.tearDownRecursor()
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ super(NoExtendedErrorsRecursorTest, cls).generateRecursorConfig(confdir)
+
+ def testNotIncepted(self):
+ qname = 'signotincepted.bad-dnssec.wb.sidnlabs.nl.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query, timeout=5.0)
+ self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+ self.assertEqual(res.edns, 0)
+ self.assertEqual(len(res.options), 0)