return;
}
dc->d_tcpConnection->queriesCount++;
- if (g_tcpMaxQueriesPerConn && dc->d_tcpConnection->queriesCount >= g_tcpMaxQueriesPerConn) {
+ if ((g_tcpMaxQueriesPerConn && dc->d_tcpConnection->queriesCount >= g_tcpMaxQueriesPerConn) ||
+ (dc->d_tcpConnection->isDropOnIdle() && dc->d_tcpConnection->d_requestsInFlight == 0)) {
try {
t_fdm->removeReadFD(dc->d_socket);
}
return true;
}
+class RunningTCPResolve {
+public:
+ RunningTCPResolve(std::unique_ptr<DNSComboWriter>& dc) : d_dc(dc) {
+ }
+ ~RunningTCPResolve() {
+ if (!d_handled && d_dc->d_tcp) {
+ finishTCPReply(d_dc, false, true);
+ }
+ }
+ void setHandled() {
+ d_handled = true;
+ }
+ void setDropOnIdle() {
+ d_dc->d_tcpConnection->setDropOnIdle();
+ }
+private:
+ std::unique_ptr<DNSComboWriter>& d_dc;
+ bool d_handled{false};
+};
+
enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop };
-static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& dc, SyncRes& sr, int& res, vector<DNSRecord>& ret, DNSPacketWriter& pw)
+static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& dc, SyncRes& sr, int& res, vector<DNSRecord>& ret, DNSPacketWriter& pw, RunningTCPResolve& tcpGuard)
{
/* don't account truncate actions for TCP queries, since they are not applied */
if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !dc->d_tcp) {
return PolicyResult::NoAction;
case DNSFilterEngine::PolicyKind::Drop:
+ tcpGuard.setDropOnIdle();
++g_stats.policyDrops;
return PolicyResult::Drop;
dq.extendedErrorExtra = &dc->d_extendedErrorExtra;
dq.meta = std::move(dc->d_meta);
+ RunningTCPResolve tcpGuard(dc);
+
if(ednsExtRCode != 0 || dc->d_mdp.d_header.opcode == Opcode::Notify) {
goto sendit;
}
appliedPolicy = DNSFilterEngine::Policy();
}
else {
- auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, tcpGuard);
if (policyResult == PolicyResult::HaveAnswer) {
if (g_dns64Prefix && dq.qtype == QType::AAAA && answerIsNOData(dc->d_mdp.d_qtype, res, ret)) {
res = getFakeAAAARecords(dq.qname, *g_dns64Prefix, ret);
if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) {
throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
}
- auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, tcpGuard);
if (policyResult == PolicyResult::HaveAnswer) {
goto haveAnswer;
}
if (answerIsNOData(dc->d_mdp.d_qtype, res, ret)) {
if (t_pdl && t_pdl->nodata(dq, res, sr.d_eventTrace)) {
shouldNotValidate = true;
- auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, tcpGuard);
if (policyResult == PolicyResult::HaveAnswer) {
goto haveAnswer;
}
}
else if (res == RCode::NXDomain && t_pdl && t_pdl->nxdomain(dq, res, sr.d_eventTrace)) {
shouldNotValidate = true;
- auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, tcpGuard);
if (policyResult == PolicyResult::HaveAnswer) {
goto haveAnswer;
}
if (t_pdl && t_pdl->postresolve(dq, res, sr.d_eventTrace)) {
shouldNotValidate = true;
- auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, tcpGuard);
// haveAnswer case redundant
if (policyResult == PolicyResult::Drop) {
return;
else if (t_pdl) {
// preresolve returned true
shouldNotValidate = true;
- auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, tcpGuard);
// haveAnswer case redundant
if (policyResult == PolicyResult::Drop) {
return;
else {
bool hadError = sendResponseOverTCP(dc, packet);
finishTCPReply(dc, hadError, true);
+ tcpGuard.setHandled();
}
sr.d_eventTrace.add(RecEventTrace::AnswerSent);
}
}
+class RunningTCPGuard {
+public:
+ RunningTCPGuard(int fd) {
+ d_fd = fd;
+ }
+ ~RunningTCPGuard() {
+ if (d_fd != -1) {
+ terminateTCPConnection(d_fd);
+ d_fd = -1;
+ }
+ }
+ void keep() {
+ d_fd = -1;
+ }
+private:
+ int d_fd{-1};
+};
+
static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
{
shared_ptr<TCPConnection> conn=boost::any_cast<shared_ptr<TCPConnection> >(var);
+ RunningTCPGuard tcpGuard{fd};
+
if (conn->state == TCPConnection::PROXYPROTOCOLHEADER) {
ssize_t bytes = recv(conn->getFD(), &conn->data.at(conn->proxyProtocolGot), conn->proxyProtocolNeed, 0);
if (bytes <= 0) {
handleTCPReadResult(fd, bytes);
+ tcpGuard.keep();
return;
}
g_log<<Logger::Error<<"Unable to consume proxy protocol header in packet from TCP client "<< conn->d_remote.toStringWithPort() <<endl;
}
++g_stats.proxyProtocolInvalidCount;
- terminateTCPConnection(fd);
return;
}
else if (remaining < 0) {
conn->proxyProtocolNeed = -remaining;
conn->data.resize(conn->proxyProtocolGot + conn->proxyProtocolNeed);
+ tcpGuard.keep();
return;
}
else {
g_log<<Logger::Error<<"Unable to parse proxy protocol header in packet from TCP client "<< conn->d_remote.toStringWithPort() <<endl;
}
++g_stats.proxyProtocolInvalidCount;
- terminateTCPConnection(fd);
return;
}
else if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
g_log<<Logger::Error<<"Proxy protocol header in packet from TCP client "<< conn->d_remote.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used << "), dropping"<< endl;
}
++g_stats.proxyProtocolInvalidCount;
- terminateTCPConnection(fd);
return;
}
}
++g_stats.unauthorizedTCP;
- terminateTCPConnection(fd);
return;
}
}
if (bytes <= 0) {
handleTCPReadResult(fd, bytes);
+ tcpGuard.keep();
return;
}
}
g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected after first byte"<<endl;
}
}
+ tcpGuard.keep();
return;
}
}
if(g_logCommonErrors) {
g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected while reading question body"<<endl;
}
- }
+ }
+ tcpGuard.keep();
return;
}
else if (bytes > std::numeric_limits<std::uint16_t>::max()) {
if(g_logCommonErrors) {
g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" sent an invalid question size while reading question body"<<endl;
}
- terminateTCPConnection(fd);
return;
}
conn->bytesread+=(uint16_t)bytes;
if (g_logCommonErrors) {
g_log<<Logger::Error<<"Unable to parse packet from TCP client "<< conn->d_remote.toStringWithPort() <<endl;
}
- terminateTCPConnection(fd);
return;
}
dc->d_tcpConnection = conn; // carry the torch
g_log<<Logger::Notice<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] DROPPED TCP question from "<<dc->d_source.toStringWithPort()<<(dc->d_source != dc->d_remote ? " (via "+dc->d_remote.toStringWithPort()+")" : "")<<" based on policy"<<endl;
}
g_stats.policyDrops++;
- terminateTCPConnection(fd);
return;
}
}
if (g_logCommonErrors) {
g_log<<Logger::Error<<"Ignoring answer from TCP client "<< dc->getRemote() <<" on server socket!"<<endl;
}
- terminateTCPConnection(fd);
return;
}
if (dc->d_mdp.d_header.opcode != Opcode::Query && dc->d_mdp.d_header.opcode != Opcode::Notify) {
g_log<<Logger::Error<<"Ignoring unsupported opcode "<<Opcode::to_s(dc->d_mdp.d_header.opcode)<<" from TCP client "<< dc->getRemote() <<" on server socket!"<<endl;
}
sendErrorOverTCP(dc, RCode::NotImp);
+ tcpGuard.keep();
return;
}
else if (dh->qdcount == 0) {
g_log<<Logger::Error<<"Ignoring empty (qdcount == 0) query from "<< dc->getRemote() <<" on server socket!"<<endl;
}
sendErrorOverTCP(dc, RCode::NotImp);
+ tcpGuard.keep();
return;
}
else {
}
g_stats.sourceDisallowedNotify++;
- terminateTCPConnection(fd);
return;
}
}
g_stats.zoneDisallowedNotify++;
- terminateTCPConnection(fd);
return;
}
}
if (dc->d_eventTrace.enabled() && SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) {
g_log << Logger::Info << dc->d_eventTrace.toString() << endl;
}
+ tcpGuard.keep();
return;
} // cache hit
} // query opcode
struct timeval ttd = g_now;
t_fdm->setReadTTD(fd, ttd, g_tcpTimeout);
}
+ tcpGuard.keep();
MT->makeThread(startDoResolve, dc.release()); // deletes dc
} // good query
} // read full query