/* * This file is part of PowerDNS or dnsdist. * Copyright -- PowerDNS.COM B.V. and its contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * In addition, for the avoidance of any doubt, permission is granted to * link this program with OpenSSL and to (re)distribute the binaries * produced as the result of such linking. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "iputils.hh" #include "dolog.hh" #include "sstuff.hh" #include "namespaces.hh" #include "dnsdist.hh" #include "threadname.hh" GlobalStateHolder > g_carbon; static time_t s_start = time(nullptr); uint64_t uptimeOfProcess(const std::string& str) { return time(nullptr) - s_start; } void carbonDumpThread() { try { setThreadName("dnsdist/carbon"); auto localCarbon = g_carbon.getLocal(); for(int numloops=0;;++numloops) { if(localCarbon->empty()) { sleep(1); continue; } /* this is wrong, we use the interval of the first server for every single one of them */ if(numloops) { const unsigned int interval = localCarbon->at(0).interval; sleep(interval); } for (const auto& conf : *localCarbon) { const auto& server = conf.server; const std::string& namespace_name = conf.namespace_name; std::string hostname = conf.ourname; if (hostname.empty()) { try { hostname = getCarbonHostName(); } catch(const std::exception& e) { throw std::runtime_error(std::string("The 'ourname' setting in 'carbonServer()' has not been set and we are unable to determine the system's hostname: ") + e.what()); } } const std::string& instance_name = conf.instance_name; try { Socket s(server.sin4.sin_family, SOCK_STREAM); s.setNonBlocking(); s.connect(server); // we do the connect so the attempt happens while we gather stats ostringstream str; time_t now=time(0); for(const auto& e : g_stats.entries) { str<(&e.second)) str<<(*val)->load(); else if (const auto& dval = boost::get(&e.second)) str<<**dval; else str<<(*boost::get(&e.second))(e.first); str<<' '<getName().empty() ? state->remote.toStringWithPort() : state->getName(); boost::replace_all(serverName, ".", "_"); const string base = namespace_name + "." + hostname + "." + instance_name + ".servers." + serverName + "."; str<queries.load() << " " << now << "\r\n"; str<responses.load() << " " << now << "\r\n"; str<reuseds.load() << " " << now << "\r\n"; str<availability != DownstreamState::Availability::Down ? state->latencyUsec/1000.0 : 0) << " " << now << "\r\n"; str<sendErrors.load() << " " << now << "\r\n"; str<outstanding.load() << " " << now << "\r\n"; str<tcpDiedSendingQuery.load() << " " << now << "\r\n"; str<tcpDiedReadingResponse.load() << " " << now << "\r\n"; str<tcpGaveUp.load() << " " << now << "\r\n"; str<tcpReadTimeouts.load() << " " << now << "\r\n"; str<tcpWriteTimeouts.load() << " " << now << "\r\n"; str<tcpConnectTimeouts.load() << " " << now << "\r\n"; str<tcpCurrentConnections.load() << " " << now << "\r\n"; str<tcpMaxConcurrentConnections.load() << " " << now << "\r\n"; str<tcpNewConnections.load() << " " << now << "\r\n"; str<tcpReusedConnections.load() << " " << now << "\r\n"; str<tlsResumptions.load() << " " << now << "\r\n"; str<tcpAvgQueriesPerConnection.load() << " " << now << "\r\n"; str<tcpAvgConnectionDuration.load() << " " << now << "\r\n"; } std::map frontendDuplicates; for(const auto& front : g_frontends) { if (front->udpFD == -1 && front->tcpFD == -1) continue; string frontName = front->local.toStringWithPort() + (front->udpFD >= 0 ? "_udp" : "_tcp"); boost::replace_all(frontName, ".", "_"); auto dupPair = frontendDuplicates.insert({frontName, 1}); if (!dupPair.second) { frontName = frontName + "_" + std::to_string(dupPair.first->second); ++(dupPair.first->second); } const string base = namespace_name + "." + hostname + "." + instance_name + ".frontends." + frontName + "."; str<queries.load() << " " << now << "\r\n"; str<responses.load() << " " << now << "\r\n"; str<tcpDiedReadingQuery.load() << " " << now << "\r\n"; str<tcpDiedSendingResponse.load() << " " << now << "\r\n"; str<tcpGaveUp.load() << " " << now << "\r\n"; str<tcpClientTimeouts.load() << " " << now << "\r\n"; str<tcpDownstreamTimeouts.load() << " " << now << "\r\n"; str<tcpCurrentConnections.load() << " " << now << "\r\n"; str<tcpMaxConcurrentConnections.load() << " " << now << "\r\n"; str<tcpAvgQueriesPerConnection.load() << " " << now << "\r\n"; str<tcpAvgConnectionDuration.load() << " " << now << "\r\n"; str<tls10queries.load() << " " << now << "\r\n"; str<tls11queries.load() << " " << now << "\r\n"; str<tls12queries.load() << " " << now << "\r\n"; str<tls13queries.load() << " " << now << "\r\n"; str<tlsUnknownqueries.load() << " " << now << "\r\n"; str<tlsNewSessions.load() << " " << now << "\r\n"; str<tlsResumptions.load() << " " << now << "\r\n"; str<tlsUnknownTicketKey.load() << " " << now << "\r\n"; str<tlsInactiveTicketKey.load() << " " << now << "\r\n"; const TLSErrorCounters* errorCounters = nullptr; if (front->tlsFrontend != nullptr) { errorCounters = &front->tlsFrontend->d_tlsCounters; } else if (front->dohFrontend != nullptr) { errorCounters = &front->dohFrontend->d_tlsCounters; } if (errorCounters != nullptr) { str<d_dhKeyTooSmall << " " << now << "\r\n"; str<d_inappropriateFallBack << " " << now << "\r\n"; str<d_noSharedCipher << " " << now << "\r\n"; str<d_unknownCipherType << " " << now << "\r\n"; str<d_unknownKeyExchangeType << " " << now << "\r\n"; str<d_unknownProtocol << " " << now << "\r\n"; str<d_unsupportedEC << " " << now << "\r\n"; str<d_unsupportedProtocol << " " << now << "\r\n"; } } auto localPools = g_pools.getLocal(); for (const auto& entry : *localPools) { string poolName = entry.first; boost::replace_all(poolName, ".", "_"); if (poolName.empty()) { poolName = "_default_"; } const string base = namespace_name + "." + hostname + "." + instance_name + ".pools." + poolName + "."; const std::shared_ptr pool = entry.second; str<countServers(false) << " " << now << "\r\n"; str<countServers(true) << " " << now << "\r\n"; if (pool->packetCache != nullptr) { const auto& cache = pool->packetCache; str<getMaxEntries() << " " << now << "\r\n"; str<getEntriesCount() << " " << now << "\r\n"; str<getHits() << " " << now << "\r\n"; str<getMisses() << " " << now << "\r\n"; str<getDeferredInserts() << " " << now << "\r\n"; str<getDeferredLookups() << " " << now << "\r\n"; str<getLookupCollisions() << " " << now << "\r\n"; str<getInsertCollisions() << " " << now << "\r\n"; str<getTTLTooShorts() << " " << now << "\r\n"; } } #ifdef HAVE_DNS_OVER_HTTPS { std::map dohFrontendDuplicates; const string base = "dnsdist." + hostname + ".main.doh."; for(const auto& doh : g_dohlocals) { string name = doh->d_local.toStringWithPort(); boost::replace_all(name, ".", "_"); boost::replace_all(name, ":", "_"); boost::replace_all(name, "[", "_"); boost::replace_all(name, "]", "_"); auto dupPair = dohFrontendDuplicates.insert({name, 1}); if (!dupPair.second) { name = name + "_" + std::to_string(dupPair.first->second); ++(dupPair.first->second); } vector> v{ {"http-connects", doh->d_httpconnects}, {"http1-queries", doh->d_http1Stats.d_nbQueries}, {"http2-queries", doh->d_http2Stats.d_nbQueries}, {"http1-200-responses", doh->d_http1Stats.d_nb200Responses}, {"http2-200-responses", doh->d_http2Stats.d_nb200Responses}, {"http1-400-responses", doh->d_http1Stats.d_nb400Responses}, {"http2-400-responses", doh->d_http2Stats.d_nb400Responses}, {"http1-403-responses", doh->d_http1Stats.d_nb403Responses}, {"http2-403-responses", doh->d_http2Stats.d_nb403Responses}, {"http1-500-responses", doh->d_http1Stats.d_nb500Responses}, {"http2-500-responses", doh->d_http2Stats.d_nb500Responses}, {"http1-502-responses", doh->d_http1Stats.d_nb502Responses}, {"http2-502-responses", doh->d_http2Stats.d_nb502Responses}, {"http1-other-responses", doh->d_http1Stats.d_nbOtherResponses}, {"http2-other-responses", doh->d_http2Stats.d_nbOtherResponses}, {"get-queries", doh->d_getqueries}, {"post-queries", doh->d_postqueries}, {"bad-requests", doh->d_badrequests}, {"error-responses", doh->d_errorresponses}, {"redirect-responses", doh->d_redirectresponses}, {"valid-responses", doh->d_validresponses} }; for(const auto& item : v) { str<clear(); } const string msg = str.str(); int ret = waitForRWData(s.getHandle(), false, 1 , 0); if(ret <= 0 ) { vinfolog("Unable to write data to carbon server on %s: %s", server.toStringWithPort(), (ret<0 ? stringerror() : "Timeout")); continue; } s.setBlocking(); writen2(s.getHandle(), msg.c_str(), msg.size()); } catch(const std::exception& e) { warnlog("Problem sending carbon data: %s", e.what()); } } } } catch(const std::exception& e) { errlog("Carbon thread died: %s", e.what()); } catch(const PDNSException& e) { errlog("Carbon thread died, PDNSException: %s", e.reason); } catch(...) { errlog("Carbon thread died"); } }