From: Miod Vallat Date: Fri, 4 Apr 2025 05:39:56 +0000 (+0200) Subject: Split PacketHandler::doQuestion into per-opcode routines. X-Git-Tag: dnsdist-2.0.0-alpha2~69^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c95a1337fb7331f3172c4a7e698230a2dddf6b60;p=thirdparty%2Fpdns.git Split PacketHandler::doQuestion into per-opcode routines. --- diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index c8c8bb96c8..1415968aef 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -1363,23 +1363,8 @@ bool PacketHandler::tryWildcard(DNSPacket& p, std::unique_ptr& r, DNS // NOLINTNEXTLINE(readability-function-cognitive-complexity): TODO Clean this function up. std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) { - DNSZoneRecord rr; - - int retargetcount=0; - set authSet; - - vector rrset; - bool weDone=false, weRedirected=false, weHaveUnauth=false, doSigs=false; - DNSName haveAlias; - uint8_t aliasScopeMask; - - std::unique_ptr r{nullptr}; bool noCache=false; -#ifdef HAVE_LUA_RECORDS - bool doLua=g_doLuaRecord; -#endif - if(p.d.qr) { // QR bit from dns packet (thanks RA from N) if(d_logDNSDetails) g_log< PacketHandler::doQuestion(DNSPacket& p) if (p.hasEDNS()) { if(p.getEDNSVersion() > 0) { - r = p.replyPacket(); - + auto r = p.replyPacket(); // PacketWriter::addOpt will take care of setting this correctly in the packet r->setEDNSRcode(ERCode::BADVERS); return r; } if (p.hasEDNSCookie()) { if (!p.hasWellFormedEDNSCookie()) { - r = p.replyPacket(); + auto r = p.replyPacket(); r->setRcode(RCode::FormErr); return r; } if (!p.hasValidEDNSCookie() && !p.d_tcp) { - r = p.replyPacket(); + auto r = p.replyPacket(); r->setEDNSRcode(ERCode::BADCOOKIE); return r; } @@ -1423,7 +1407,7 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) string secret; TSIGRecordContent trc; if (!checkForCorrectTSIG(p, &tsigkeyname, &secret, &trc)) { - r=p.replyPacket(); // generate an empty reply packet + auto r=p.replyPacket(); // generate an empty reply packet if(d_logDNSDetails) g_log< PacketHandler::doQuestion(DNSPacket& p) noCache=true; } - r=p.replyPacket(); // generate an empty reply packet, possibly with TSIG details inside - if (p.qtype == QType::TKEY) { + auto r=p.replyPacket(); // generate an empty reply packet, possibly with TSIG details inside this->tkeyHandler(p, r); return r; } @@ -1464,422 +1447,474 @@ std::unique_ptr PacketHandler::doQuestion(DNSPacket& p) S.inc("corrupt-packets"); S.ringAccount("remotes-corrupt", p.getInnerRemote()); S.inc("servfail-packets"); + auto r=p.replyPacket(); // generate an empty reply packet r->setRcode(RCode::ServFail); return r; } - if(p.d.opcode) { // non-zero opcode (again thanks RA!) - if(p.d.opcode==Opcode::Update) { - S.inc("dnsupdate-queries"); - int res=processUpdate(p); - if (res == RCode::Refused) - S.inc("dnsupdate-refused"); - else if (res != RCode::ServFail) - S.inc("dnsupdate-answers"); - r->setRcode(res); - r->setOpcode(Opcode::Update); - return r; - } - else if(p.d.opcode==Opcode::Notify) { - S.inc("incoming-notifications"); - int res=processNotify(p); - if(res>=0) { - r->setRcode(res); - r->setOpcode(Opcode::Notify); - return r; - } - return nullptr; - } - g_log< (PacketHandler::*)(DNSPacket&, bool); + const static opcodeHandler opcodeHandlers[16] = { + &PacketHandler::opcodeQuery, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotify, + &PacketHandler::opcodeUpdate, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented, + &PacketHandler::opcodeNotImplemented + }; + + return (this->*(opcodeHandlers[p.d.opcode]))(p, noCache); + } + catch(const DBException &e) { + g_log<setRcode(RCode::ServFail); + S.inc("servfail-packets"); + S.ringAccount("servfail-queries", p.qdomain, p.qtype); + return r; + } + catch(const PDNSException &e) { + g_log<setRcode(RCode::ServFail); + S.inc("servfail-packets"); + S.ringAccount("servfail-queries", p.qdomain, p.qtype); + return r; + } +} - r->setRcode(RCode::NotImp); - return r; - } +std::unique_ptr PacketHandler::opcodeQuery(DNSPacket& p, bool noCache) +{ + std::unique_ptr r{nullptr}; + DNSZoneRecord rr; - // g_log< authSet; - if(p.qtype.getCode()==QType::IXFR) { - r->setRcode(RCode::Refused); - return r; - } + vector rrset; + bool weDone=false, weRedirected=false, weHaveUnauth=false, doSigs=false; + DNSName haveAlias; + uint8_t aliasScopeMask; - DNSName target=p.qdomain; +#ifdef HAVE_LUA_RECORDS + bool doLua=g_doLuaRecord; +#endif - // catch chaos qclass requests - if(p.qclass == QClass::CHAOS) { - if (doChaosRequest(p,r,target)) - goto sendit; - else - return r; - } + r=p.replyPacket(); // generate an empty reply packet, possibly with TSIG details inside - // we only know about qclass IN (and ANY), send Refused for everything else. - if(p.qclass != QClass::IN && p.qclass!=QClass::ANY) { - r->setRcode(RCode::Refused); - return r; - } + // g_log<d.tc = 1; - r->commitD(); + if(p.qtype.getCode()==QType::IXFR) { + r->setRcode(RCode::Refused); + return r; + } + + DNSName target=p.qdomain; + + // catch chaos qclass requests + if(p.qclass == QClass::CHAOS) { + if (doChaosRequest(p,r,target)) + goto sendit; + else return r; - } + } - // for qclass ANY the response should never be authoritative unless the response covers all classes. - if(p.qclass==QClass::ANY) - r->setA(false); + // we only know about qclass IN (and ANY), send Refused for everything else. + if(p.qclass != QClass::IN && p.qclass!=QClass::ANY) { + r->setRcode(RCode::Refused); + return r; + } + // send TC for udp ANY query if any-to-tcp is enabled. + if(p.qtype.getCode() == QType::ANY && !p.d_tcp && g_anyToTcp) { + r->d.tc = 1; + r->commitD(); + return r; + } - retargeted:; - if(retargetcount > 10) { // XXX FIXME, retargetcount++? - g_log<setRcode(RCode::ServFail); - return r; - } + // for qclass ANY the response should never be authoritative unless the response covers all classes. + if(p.qclass==QClass::ANY) + r->setA(false); - if (retargetcount > 0 && !d_doResolveAcrossZones && !target.isPartOf(r->qdomainzone)) { - // We are following a retarget outside the initial zone (and do not need to check getAuth to know this). Config asked us not to do that. - // This is a performance optimization, the generic case is checked after getAuth below. - goto sendit; // NOLINT(cppcoreguidelines-avoid-goto) - } + retargeted:; + if(retargetcount > 10) { // XXX FIXME, retargetcount++? + g_log<setRcode(RCode::ServFail); + return r; + } - if(!B.getAuth(target, p.qtype, &d_sd)) { - DLOG(g_log<setA(false); // drop AA if we never had a SOA in the first place - r->setRcode(RCode::Refused); // send REFUSED - but only on empty 'no idea' - } - goto sendit; - } - DLOG(g_log<setA(false); // drop AA if we never had a SOA in the first place + r->setRcode(RCode::Refused); // send REFUSED - but only on empty 'no idea' } + goto sendit; + } + DLOG(g_log<setRcode(RCode::Refused); - goto sendit; - } + if(p.qtype.getCode() == QType::SOA && d_sd.qname==p.qdomain) { + rr=makeEditedDNSZRFromSOAData(d_dk, d_sd); + r->addRecord(std::move(rr)); + goto sendit; + } - DLOG(g_log<<"Checking for referrals first, unless this is a DS query"<isEmpty()) goto sendit; + } + + // this TRUMPS a cname! + if(p.qtype.getCode() == QType::RRSIG) { + g_log<setRcode(RCode::Refused); + goto sendit; + } + + DLOG(g_log<<"Checking for referrals first, unless this is a DS query"<(rr.dr); - if (!rec) { - continue; - } - if(rec->d_type == QType::CNAME || rec->d_type == p.qtype.getCode() || (p.qtype.getCode() == QType::ANY && rec->d_type != QType::RRSIG)) { - noCache=true; - try { - auto recvec=luaSynth(rec->getCode(), target, rr, d_sd.qname, p, rec->d_type, s_LUA); - if(!recvec.empty()) { - for (const auto& r_it : recvec) { - rr.dr.d_type = rec->d_type; // might be CNAME - rr.dr.setContent(r_it); - rr.scopeMask = p.getRealRemote().getBits(); // this makes sure answer is a specific as your question - rrset.push_back(rr); - } - if(rec->d_type == QType::CNAME && p.qtype.getCode() != QType::CNAME) - weRedirected = true; - else - weDone = true; + if (rr.dr.d_type == QType::LUA && !d_dk.isPresigned(d_sd.qname)) { + if(!doLua) + continue; + auto rec=getRR(rr.dr); + if (!rec) { + continue; + } + if(rec->d_type == QType::CNAME || rec->d_type == p.qtype.getCode() || (p.qtype.getCode() == QType::ANY && rec->d_type != QType::RRSIG)) { + noCache=true; + try { + auto recvec=luaSynth(rec->getCode(), target, rr, d_sd.qname, p, rec->d_type, s_LUA); + if(!recvec.empty()) { + for (const auto& r_it : recvec) { + rr.dr.d_type = rec->d_type; // might be CNAME + rr.dr.setContent(r_it); + rr.scopeMask = p.getRealRemote().getBits(); // this makes sure answer is a specific as your question + rrset.push_back(rr); } + if(rec->d_type == QType::CNAME && p.qtype.getCode() != QType::CNAME) + weRedirected = true; + else + weDone = true; } - catch(std::exception &e) { - B.lookupEnd(); // don't leave DB handle in bad state - - r=p.replyPacket(); - r->setRcode(RCode::ServFail); + } + catch(std::exception &e) { + B.lookupEnd(); // don't leave DB handle in bad state - return r; - } + r=p.replyPacket(); + r->setRcode(RCode::ServFail); + return r; } } + } #endif - //cerr<<"got content: ["<(rr.dr)->getContent(); - aliasScopeMask=rr.scopeMask; - } - - // Filter out all SOA's and add them in later - if(rr.dr.d_type == QType::SOA) + //cerr<<"got content: ["<(rr.dr)->getContent(); + aliasScopeMask=rr.scopeMask; } - /* Add in SOA if required */ - if(target==d_sd.qname) { - rr=makeEditedDNSZRFromSOAData(d_dk, d_sd); - rrset.push_back(rr); - } + // Filter out all SOA's and add them in later + if(rr.dr.d_type == QType::SOA) + continue; + rrset.push_back(rr); + } - DLOG(g_log<<"After first ANY query for '"<completePacket(r, haveAlias, target, aliasScopeMask); - return nullptr; - } + DLOG(g_log<<"After first ANY query for '"<completePacket(r, haveAlias, target, aliasScopeMask); + return nullptr; + } - // referral for DS query - if(p.qtype.getCode() == QType::DS) { - DLOG(g_log<<"Qtype is DS"<qdomainwild=wildcard; - retargetcount++; - goto retargeted; - } - if(nodata) - makeNOError(p, r, target, wildcard, 2); - - goto sendit; - } - try { - if (tryDNAME(p, r, target)) { - retargetcount++; - goto retargeted; - } - } catch (const std::range_error &e) { - // We couldn't make a CNAME..... - r->setRcode(RCode::YXDomain); + if(doReferral) { + DLOG(g_log<<"DS query found no direct result, trying referral now"< 0)) - makeNXDomain(p, r, target, wildcard); + if(rrset.empty()) { + DLOG(g_log<qdomainwild=wildcard; + retargetcount++; + goto retargeted; + } + if(nodata) + makeNOError(p, r, target, wildcard, 2); goto sendit; } + try { + if (tryDNAME(p, r, target)) { + retargetcount++; + goto retargeted; + } + } catch (const std::range_error &e) { + // We couldn't make a CNAME..... + r->setRcode(RCode::YXDomain); + goto sendit; + } - if(weRedirected) { - for(auto& loopRR: rrset) { - if(loopRR.dr.d_type == QType::CNAME) { - r->addRecord(DNSZoneRecord(loopRR)); - target = getRR(loopRR.dr)->getTarget(); - retargetcount++; - goto retargeted; - } + if (!(((p.qtype.getCode() == QType::CNAME) || (p.qtype.getCode() == QType::ANY)) && retargetcount > 0)) + makeNXDomain(p, r, target, wildcard); + + goto sendit; + } + + if(weRedirected) { + for(auto& loopRR: rrset) { + if(loopRR.dr.d_type == QType::CNAME) { + r->addRecord(DNSZoneRecord(loopRR)); + target = getRR(loopRR.dr)->getTarget(); + retargetcount++; + goto retargeted; } } - else if(weDone) { - bool haveRecords = false; - bool presigned = d_dk.isPresigned(d_sd.qname); - for(const auto& loopRR: rrset) { - if (loopRR.dr.d_type == QType::ENT) { - continue; - } - if (loopRR.dr.d_type == QType::ALIAS && d_doExpandALIAS && !presigned) { - continue; - } + } + else if(weDone) { + bool haveRecords = false; + bool presigned = d_dk.isPresigned(d_sd.qname); + for(const auto& loopRR: rrset) { + if (loopRR.dr.d_type == QType::ENT) { + continue; + } + if (loopRR.dr.d_type == QType::ALIAS && d_doExpandALIAS && !presigned) { + continue; + } #ifdef HAVE_LUA_RECORDS - if (loopRR.dr.d_type == QType::LUA && !presigned) { - continue; - } -#endif - if ((p.qtype.getCode() == QType::ANY || loopRR.dr.d_type == p.qtype.getCode()) && loopRR.auth) { - r->addRecord(DNSZoneRecord(loopRR)); - haveRecords = true; - } + if (loopRR.dr.d_type == QType::LUA && !presigned) { + continue; } - - if (haveRecords) { - if(d_dnssec && p.qtype.getCode() == QType::ANY) - completeANYRecords(p, r, target); +#endif + if ((p.qtype.getCode() == QType::ANY || loopRR.dr.d_type == p.qtype.getCode()) && loopRR.auth) { + r->addRecord(DNSZoneRecord(loopRR)); + haveRecords = true; } - else - makeNOError(p, r, target, DNSName(), 0); - - goto sendit; - } - else if(weHaveUnauth) { - DLOG(g_log<<"Have unauth data, so need to hunt for best NS records"<getRRS()) { - if(loopRR.scopeMask) { - noCache=true; - break; - } + if (haveRecords) { + if(d_dnssec && p.qtype.getCode() == QType::ANY) + completeANYRecords(p, r, target); } - if(doSigs) - addRRSigs(d_dk, B, authSet, r->getRRS(), &p); + else + makeNOError(p, r, target, DNSName(), 0); - if(PC.enabled() && !noCache && p.couldBeCached()) - PC.insert(p, *r, r->getMinTTL()); // in the packet cache + goto sendit; } - catch(const DBException &e) { - g_log<setRcode(RCode::ServFail); - S.inc("servfail-packets"); - S.ringAccount("servfail-queries", p.qdomain, p.qtype); + else if(weHaveUnauth) { + DLOG(g_log<<"Have unauth data, so need to hunt for best NS records"<setRcode(RCode::ServFail); - S.inc("servfail-packets"); - S.ringAccount("servfail-queries", p.qdomain, p.qtype); + + sendit:; + doAdditionalProcessing(p, r); + + for(const auto& loopRR: r->getRRS()) { + if(loopRR.scopeMask) { + noCache=true; + break; + } + } + if(doSigs) + addRRSigs(d_dk, B, authSet, r->getRRS(), &p); + + if(PC.enabled() && !noCache && p.couldBeCached()) + PC.insert(p, *r, r->getMinTTL()); // in the packet cache + + return r; +} + +std::unique_ptr PacketHandler::opcodeNotify(DNSPacket& p, bool /* noCache */) +{ + std::unique_ptr r{nullptr}; + S.inc("incoming-notifications"); + int res=processNotify(p); + if(res>=0) { + r=p.replyPacket(); // generate an empty reply packet + r->setRcode(res); + r->setOpcode(Opcode::Notify); + return r; } + return nullptr; +} + +std::unique_ptr PacketHandler::opcodeUpdate(DNSPacket& p, bool /* noCache */) +{ + std::unique_ptr r{nullptr}; + S.inc("dnsupdate-queries"); + int res=processUpdate(p); + if (res == RCode::Refused) + S.inc("dnsupdate-refused"); + else if (res != RCode::ServFail) + S.inc("dnsupdate-answers"); + r=p.replyPacket(); // generate an empty reply packet + r->setRcode(res); + r->setOpcode(Opcode::Update); return r; +} + +std::unique_ptr PacketHandler::opcodeNotImplemented(DNSPacket& p, bool /* noCache */) +{ + std::unique_ptr r{nullptr}; + g_log<setRcode(RCode::NotImp); + return r; } bool PacketHandler::checkForCorrectTSIG(const DNSPacket& packet, DNSName* tsigkeyname, string* secret, TSIGRecordContent* tsigContent) diff --git a/pdns/packethandler.hh b/pdns/packethandler.hh index f1907721c9..429e67c8ac 100644 --- a/pdns/packethandler.hh +++ b/pdns/packethandler.hh @@ -109,6 +109,11 @@ private: void tkeyHandler(const DNSPacket& p, std::unique_ptr& r); // opcodeQuery(DNSPacket&, bool); + std::unique_ptr opcodeNotify(DNSPacket&, bool); + std::unique_ptr opcodeUpdate(DNSPacket&, bool); + std::unique_ptr opcodeNotImplemented(DNSPacket&, bool); + static AtomicCounter s_count; static std::mutex s_rfc2136lock; bool d_logDNSDetails;