From: Charles-Henri Bruyand Date: Fri, 18 Jun 2021 09:30:54 +0000 (+0200) Subject: also copy metas to responses + unit tests X-Git-Tag: dnsdist-1.7.0-alpha1~106^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3211bbafa31d3d63c1916262098c348695313beb;p=thirdparty%2Fpdns.git also copy metas to responses + unit tests --- diff --git a/pdns/lua-recursor4.hh b/pdns/lua-recursor4.hh index 1536b303b8..a1ff21e89d 100644 --- a/pdns/lua-recursor4.hh +++ b/pdns/lua-recursor4.hh @@ -63,6 +63,11 @@ public: RecursorLua4(); ~RecursorLua4(); // this is so unique_ptr works with an incomplete type + struct MetaValue + { + std::unordered_set stringVal; + std::unordered_set intVal; + }; struct DNSQuestion { DNSQuestion(const ComboAddress& rem, const ComboAddress& loc, const DNSName& query, uint16_t type, bool tcp, bool& variable_, bool& wantsRPZ_, bool& logResponse_, bool& addPaddingToResponse_) : @@ -93,6 +98,7 @@ public: bool& logResponse; bool& addPaddingToResponse; unsigned int tag{0}; + std::map meta; void addAnswer(uint16_t type, const std::string& content, boost::optional ttl, boost::optional name); void addRecord(uint16_t type, const std::string& content, DNSResourceRecord::Place place, boost::optional ttl, boost::optional name); @@ -136,11 +142,6 @@ public: std::unordered_set* policyTags{nullptr}; std::unordered_map* discardedPolicies{nullptr}; }; - struct MetaValue - { - std::unordered_set stringVal; - std::unordered_set intVal; - }; struct FFIParams { diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 4c499f1373..542a9754f3 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -1750,6 +1750,7 @@ static void startDoResolve(void *p) dq.proxyProtocolValues = &dc->d_proxyProtocolValues; dq.extendedErrorCode = &dc->d_extendedErrorCode; dq.extendedErrorExtra = &dc->d_extendedErrorExtra; + dq.meta = dc->d_meta; if(ednsExtRCode != 0) { goto sendit; @@ -1894,6 +1895,7 @@ static void startDoResolve(void *p) dq.validationState = sr.getValidationState(); appliedPolicy = sr.d_appliedPolicy; dc->d_policyTags = std::move(sr.d_policyTags); + if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) { dc->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode; dc->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra; @@ -2251,6 +2253,10 @@ static void startDoResolve(void *p) pbMessage.setDeviceName(dq.deviceName); pbMessage.setFromPort(dc->d_source.getPort()); pbMessage.setToPort(dc->d_destination.getPort()); + + for (const auto& m : dq.meta) { + pbMessage.setMeta(m.first, m.second.stringVal, m.second.intVal); + } #ifdef NOD_ENABLED if (g_nodEnabled) { if (nod) { @@ -2535,6 +2541,7 @@ static bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& d g_stats.ourtime(0); #endif } + return cacheHit; } @@ -3148,6 +3155,7 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr dc->d_extendedErrorCode = extendedErrorCode; dc->d_extendedErrorExtra = std::move(extendedErrorExtra); dc->d_responsePaddingDisabled = responsePaddingDisabled; + dc->d_meta = std::move(meta); MT->makeThread(startDoResolve, (void*) dc.release()); // deletes dc return 0; diff --git a/regression-tests.recursor-dnssec/test_Protobuf.py b/regression-tests.recursor-dnssec/test_Protobuf.py index c0dec8fe09..9a6e54fd66 100644 --- a/regression-tests.recursor-dnssec/test_Protobuf.py +++ b/regression-tests.recursor-dnssec/test_Protobuf.py @@ -228,6 +228,20 @@ class TestRecursorProtobuf(RecursorTest): for tag in msg.response.tags: self.assertTrue(tag in tags) + def checkProtobufMetas(self, msg, metas): + print(metas) + print('---') + print(msg.meta) + self.assertEqual(len(msg.meta), len(metas)) + for m in msg.meta: + self.assertTrue(m.HasField('key')) + self.assertTrue(m.HasField('value')) + self.assertTrue(m.key in metas) + for i in m.value.intVal : + self.assertTrue(i in metas[m.key]['intVal']) + for s in m.value.stringVal : + self.assertTrue(s in metas[m.key]['stringVal']) + def checkProtobufOutgoingQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', length=None): self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType) self.checkOutgoingProtobufBase(msg, protocol, query, initiator, length=length) @@ -279,6 +293,7 @@ class TestRecursorProtobuf(RecursorTest): @ 3600 IN SOA {soa} a 3600 IN A 192.0.2.42 tagged 3600 IN A 192.0.2.84 +meta 3600 IN A 192.0.2.85 query-selected 3600 IN A 192.0.2.84 answer-selected 3600 IN A 192.0.2.84 types 3600 IN A 192.0.2.84 @@ -991,3 +1006,61 @@ sub.test 3600 IN A 192.0.2.42 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15) self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42') self.checkNoRemainingMessage() + + +class ProtobufMetaFFITest(TestRecursorProtobuf): + """ + This test makes sure that we can correctly add extra meta fields (FFI version). + """ + _confdir = 'ProtobufMetaFFITest' + _config_template = """ +auth-zones=example=configs/%s/example.zone""" % _confdir + _lua_config_file = """ + protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } ) + """ % (protobufServersParameters[0].port, protobufServersParameters[1].port) + _lua_dns_script_file = """ + 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); + void pdns_ffi_param_add_meta_single_string_kv(pdns_ffi_param_t *ref, const char* key, const char* val); + void pdns_ffi_param_add_meta_single_int64_kv(pdns_ffi_param_t *ref, const char* key, int64_t val); + ]] + + function gettag_ffi(obj) + qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj)) + if qname == 'meta.example' then + ffi.C.pdns_ffi_param_add_meta_single_string_kv(obj, "meta-str", "keyword") + ffi.C.pdns_ffi_param_add_meta_single_int64_kv(obj, "meta-int", 42) + ffi.C.pdns_ffi_param_add_meta_single_string_kv(obj, "meta-str", "content") + ffi.C.pdns_ffi_param_add_meta_single_int64_kv(obj, "meta-int", 21) + end + return 0 + end + """ + def testMeta(self): + name = 'meta.example.' + expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.85') + query = dns.message.make_query(name, 'A', want_dnssec=True) + query.flags |= dns.flags.CD + res = self.sendUDPQuery(query) + self.assertRRsetInAnswer(res, expected) + + # check the protobuf messages corresponding to the UDP query and answer + msg = self.getFirstProtobufMessage() + self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name) + self.checkProtobufMetas(msg, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}}) + + # then the response + msg = self.getFirstProtobufMessage() + self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res) + self.assertEqual(len(msg.response.rrs), 1) + rr = msg.response.rrs[0] + # we have max-cache-ttl set to 15 + self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15) + self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.85') + self.checkProtobufMetas(msg, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}}) + + self.checkNoRemainingMessage()