#include "dnsdist-cache.hh"
#include "dnsdist-dynbpf.hh"
#include "dnsname.hh"
+#include "doh.hh"
#include "ednsoptions.hh"
#include "gettime.hh"
#include "iputils.hh"
const uint16_t* flags = getFlagsFromDNSHeader(dh);
origFlags = *flags;
}
+ DNSQuestion(const DNSQuestion&) = delete;
+ DNSQuestion& operator=(const DNSQuestion&) = delete;
+ DNSQuestion(DNSQuestion&&) = default;
#ifdef HAVE_PROTOBUF
boost::optional<boost::uuids::uuid> uniqueId;
#endif
Netmask ecs;
boost::optional<Netmask> subnet;
+ std::string sni; /* Server Name Indication, if any (DoT or DoH) */
const DNSName* qname{nullptr};
const ComboAddress* local{nullptr};
const ComboAddress* remote{nullptr};
std::shared_ptr<DNSDistPacketCache> packetCache{nullptr};
struct dnsheader* dh{nullptr};
const struct timespec* queryTime{nullptr};
+ struct DOHUnit* du{nullptr};
size_t size;
unsigned int consumed{0};
int delayMsec{0};
{
DNSResponse(const DNSName* name, uint16_t type, uint16_t class_, unsigned int consumed, const ComboAddress* lc, const ComboAddress* rem, struct dnsheader* header, size_t bufferSize, uint16_t responseLen, bool isTcp, const struct timespec* queryTime_):
DNSQuestion(name, type, class_, consumed, lc, rem, header, bufferSize, responseLen, isTcp, queryTime_) { }
+ DNSResponse(const DNSResponse&) = delete;
+ DNSResponse& operator=(const DNSResponse&) = delete;
+ DNSResponse(DNSResponse&&) = default;
};
/* so what could you do:
extern vector<pair<struct timeval, std::string> > g_confDelta;
+extern uint64_t getLatencyCount(const std::string&);
+
struct DNSDistStats
{
using stat_t=std::atomic<uint64_t>; // aww yiss ;-)
stat_t noPolicy{0};
stat_t cacheHits{0};
stat_t cacheMisses{0};
- stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0};
+ stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}, latencySum{0};
stat_t securityStatus{0};
double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0};
{"fd-usage", getOpenFileDescriptors},
{"dyn-blocked", &dynBlocked},
{"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }},
- {"security-status", &securityStatus}
+ {"security-status", &securityStatus},
+ // Latency histogram
+ {"latency-sum", &latencySum},
+ {"latency-count", getLatencyCount},
};
};
{ "dyn-blocked", MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of a dynamic block")},
{ "dyn-block-nmg-size", MetricDefinition(PrometheusMetricType::gauge, "Number of dynamic blocks entries") },
{ "security-status", MetricDefinition(PrometheusMetricType::gauge, "Security status of this software. 0=unknown, 1=OK, 2=upgrade recommended, 3=upgrade mandatory") },
+ // Latency histogram
+ { "latency-sum", MetricDefinition(PrometheusMetricType::counter, "Total response time in milliseconds")},
+ { "latency-count", MetricDefinition(PrometheusMetricType::counter, "Number of queries contributing to response time histogram")},
};
};
tempFailureTTL = orig.tempFailureTTL;
}
+ /* We use this value to detect whether this state is in use, in addition to
+ its use to send the response over UDP.
+ For performance reasons we don't want to use a lock here, but that means
+ we need to be very careful when modifying this value. Modifications happen
+ from:
+ - one of the UDP or DoH 'client' threads receiving a query, selecting a backend
+ then picking one of the states associated to this backend (via the idOffset).
+ Most of the time this state should not be in use and origFD is -1, but we
+ might not yet have received a response for the query previously associated to this
+ state, meaning that we will 'reuse' this state and erase the existing state.
+ If we ever receive a response for this state, it will be discarded. This is
+ mostly fine for UDP except that we still need to be careful in order to miss
+ the 'outstanding' counters, which should only be increased when we are picking
+ an empty state, and not when reusing ;
+ For DoH, though, we have dynamically allocated a DOHUnit object that needs to
+ be freed, as well as internal objects internals to libh2o.
+ - one of the UDP receiver threads receiving a response from a backend, picking
+ the corresponding state and sending the response to the client ;
+ - the 'healthcheck' thread scanning the states to actively discover timeouts,
+ mostly to keep some counters like the 'outstanding' one sane.
+ */
std::atomic<int> origFD; // set to <0 to indicate this state is empty // 4
ComboAddress origRemote; // 28
std::shared_ptr<DNSDistPacketCache> packetCache{nullptr};
std::shared_ptr<QTag> qTag{nullptr};
const ClientState* cs{nullptr};
+ DOHUnit* du{nullptr};
uint32_t cacheKey; // 4
uint32_t cacheKeyNoECS; // 4
uint16_t age; // 4
};
typedef std::unordered_map<string, unsigned int> QueryCountRecords;
-typedef std::function<std::tuple<bool, string>(DNSQuestion dq)> QueryCountFilter;
+typedef std::function<std::tuple<bool, string>(const DNSQuestion* dq)> QueryCountFilter;
struct QueryCount {
QueryCount()
{
struct ClientState
{
+ ClientState(const ComboAddress& local_, bool isTCP, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set<int>& cpus_): cpus(cpus_), local(local_), interface(itfName), fastOpenQueueSize(fastOpenQueue), tcp(isTCP), reuseport(doReusePort)
+ {
+ }
+
std::set<int> cpus;
ComboAddress local;
std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr};
- shared_ptr<TLSFrontend> tlsFrontend;
+ std::shared_ptr<TLSFrontend> tlsFrontend{nullptr};
+ std::shared_ptr<DOHFrontend> dohFrontend{nullptr};
+ std::string interface;
std::atomic<uint64_t> queries{0};
std::atomic<uint64_t> tcpDiedReadingQuery{0};
std::atomic<uint64_t> tcpDiedSendingResponse{0};
std::atomic<double> tcpAvgConnectionDuration{0.0};
int udpFD{-1};
int tcpFD{-1};
+ int fastOpenQueueSize{0};
bool muted{false};
+ bool tcp;
+ bool reuseport;
+ bool ready{false};
int getSocket() const
{
{
std::string result = udpFD != -1 ? "UDP" : "TCP";
- if (tlsFrontend) {
+ if (dohFrontend) {
+ result += " (DNS over HTTPS)";
+ }
+ else if (tlsFrontend) {
result += " (DNS over TLS)";
}
else if (dnscryptCtx) {
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::vector<shared_ptr<DOHFrontend>> g_dohlocals;
+extern std::vector<std::unique_ptr<ClientState>> g_frontends;
extern bool g_truncateTC;
extern bool g_fixupCase;
extern int g_tcpRecvTimeout;
extern size_t g_udpVectorSize;
extern bool g_preserveTrailingData;
extern bool g_allowEmptyResponse;
+extern bool g_roundrobinFailOnNoServer;
#ifdef HAVE_EBPF
extern shared_ptr<BPFFilter> g_defaultBPFFilter;
void dnsdistWebserverThread(int sock, const ComboAddress& local);
void tcpAcceptorThread(void* p);
+#ifdef HAVE_DNS_OVER_HTTPS
+void dohThread(ClientState* cs);
+#endif /* HAVE_DNS_OVER_HTTPS */
void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects
void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ side effect calls
bool checkQueryHeaders(const struct dnsheader* dh);
-extern std::vector<std::tuple<ComboAddress, std::shared_ptr<DNSCryptContext>, bool, int, std::string, std::set<int> > > g_dnsCryptLocals;
+extern std::vector<std::shared_ptr<DNSCryptContext>> g_dnsCryptLocals;
int handleDNSCryptQuery(char* packet, uint16_t len, std::shared_ptr<DNSCryptQuery> query, uint16_t* decryptedQueryLen, bool tcp, time_t now, std::vector<uint8_t>& response);
boost::optional<std::vector<uint8_t>> checkDNSCryptQuery(const ClientState& cs, const char* query, uint16_t& len, std::shared_ptr<DNSCryptQuery>& dnsCryptQuery, time_t now, bool tcp);
DNSResponse makeDNSResponseFromIDState(IDState& ids, struct dnsheader* dh, size_t bufferSize, uint16_t responseLen, bool isTCP);
void setIDStateFromDNSQuestion(IDState& ids, DNSQuestion& dq, DNSName&& qname);
+
+int pickBackendSocketForSending(std::shared_ptr<DownstreamState>& state);
+ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& ss, const int sd, const char* request, const size_t requestLen, bool healthCheck=false);