#ifndef __CLASS_DNS_CACHE_H #define __CLASS_DNS_CACHE_H // Shared DNS cache infrastructure used by both MySQL_Monitor and PgSQL_Monitor. // The cache itself is just a hostname -> [IP] map with TTL; the resolver thread // does getaddrinfo and feeds the cache. Each monitor instantiates its own // DNS_Cache and supplies its own counters, so the two protocols share code but // have independent cache state. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "thread.h" #include "wqueue.h" struct DNS_Cache_Record { DNS_Cache_Record() = default; DNS_Cache_Record(DNS_Cache_Record&&) = default; DNS_Cache_Record(const DNS_Cache_Record&) = default; DNS_Cache_Record& operator=(DNS_Cache_Record&&) = default; DNS_Cache_Record& operator=(const DNS_Cache_Record&) = default; DNS_Cache_Record(const std::string& hostname, const std::vector& ips, unsigned long long ttl = 0) : hostname_(hostname), ttl_(ttl) { std::copy(ips.begin(), ips.end(), std::inserter(ips_, ips_.end())); } DNS_Cache_Record(const std::string& hostname, std::set&& ips, unsigned long long ttl = 0) : hostname_(hostname), ips_(std::move(ips)), ttl_(ttl) { } ~DNS_Cache_Record() = default; std::string hostname_; std::set ips_; unsigned long long ttl_ = 0; }; class DNS_Cache { public: DNS_Cache() : enabled(true) { int rc = pthread_rwlock_init(&rwlock_, nullptr); assert(rc == 0); } ~DNS_Cache() { pthread_rwlock_destroy(&rwlock_); } inline void set_enabled_flag(bool value) { enabled = value; } // Wire the cache up to per-instance counters maintained by the owning // monitor. Pass nullptr to leave any counter unincremented; this is what // keeps MySQL_Monitor and PgSQL_Monitor cache state independent. The // counters are atomic because resolver workers increment them while // p_update_metrics() reads them from another thread. inline void set_counters(std::atomic* queried, std::atomic* lookup_success, std::atomic* record_updated) { counter_queried_ = queried; counter_lookup_success_ = lookup_success; counter_record_updated_ = record_updated; } bool add(const std::string& hostname, std::vector&& ips); bool add_if_not_exist(const std::string& hostname, std::vector&& ips); void remove(const std::string& hostname); void clear(); bool empty() const; std::string lookup(const std::string& hostname, size_t* ip_count) const; private: struct IP_ADDR { std::vector ips; // 'counter' is bumped by get_next_ip() (a const method) for // round-robin selection; the logical state of the cache record is // unchanged, so mutable is the right tool here and lets us drop a // const_cast at the call site. mutable unsigned long counter = 0; }; std::string get_next_ip(const IP_ADDR& ip_addr) const; std::unordered_map records; std::atomic_bool enabled; mutable pthread_rwlock_t rwlock_; std::atomic* counter_queried_ { nullptr }; std::atomic* counter_lookup_success_ { nullptr }; std::atomic* counter_record_updated_ { nullptr }; }; struct DNS_Resolve_Data { std::promise> result; std::shared_ptr dns_cache; std::string hostname; std::set cached_ips; unsigned int ttl = 0; unsigned int refresh_intv = 0; // Address family for getaddrinfo, set by the caller from its protocol's // resolution_family setting. AF_UNSPEC keeps the OS default behavior. int ai_family = AF_UNSPEC; }; // Worker function for the DNS resolver thread pool. Performs getaddrinfo // on dns_resolve_data->hostname, populates the cache, and fulfils the // future on dns_resolve_data->result. void* monitor_dns_resolver_thread(const std::vector& dns_resolve_data_list); // Minimal worker thread that drains a wqueue and calls // monitor_dns_resolver_thread() on each item. Independent of any monitor // singleton so it can back PgSQL_Monitor's DNS cache loop alongside (or // instead of) MySQL_Monitor's existing ConsumerThread-based pool. // // Each item ownership is transferred to this worker: the worker frees it // after running monitor_dns_resolver_thread. A NULL dequeue is the exit // signal — the loop terminates cleanly so callers can shut the pool down by // pushing one NULL per worker. class DNSResolverWorker : public Thread { public: DNSResolverWorker(wqueue& q, const char* name = nullptr) : queue_(q) { if (name && name[0]) snprintf(thr_name_, sizeof(thr_name_), "%.15s", name); else snprintf(thr_name_, sizeof(thr_name_), "DNSResolver"); } void* run() override; private: wqueue& queue_; char thr_name_[16] {}; }; // Return true if 'ip' is a valid IPv4 or IPv6 address string. bool validate_ip(const std::string& ip); // Return the IP address of the peer connected to 'socket_fd', or "" on // failure / non-IP families. std::string get_connected_peer_ip_from_socket(int socket_fd); // Helper: stringify a list of IPs for debug logging. Defined inline because // it's templated over the iterable type used by the various call sites. template std::string debug_iplisttostring(const T& ips) { std::stringstream sstr; for (const std::string& ip : ips) sstr << ip << " "; return sstr.str(); } #endif // __CLASS_DNS_CACHE_H