// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. decltype(ircd::net::dns::cache::min_ttl) ircd::net::dns::cache::min_ttl { { "name", "ircd.net.dns.cache.min_ttl" }, { "default", 28800L }, }; decltype(ircd::net::dns::cache::error_ttl) ircd::net::dns::cache::error_ttl { { "name", "ircd.net.dns.cache.error_ttl" }, { "default", 1200L }, }; decltype(ircd::net::dns::cache::nxdomain_ttl) ircd::net::dns::cache::nxdomain_ttl { { "name", "ircd.net.dns.cache.nxdomain_ttl" }, { "default", 604800L }, }; decltype(ircd::net::dns::cache::waiting) ircd::net::dns::cache::waiting; decltype(ircd::net::dns::cache::mutex) ircd::net::dns::cache::mutex; decltype(ircd::net::dns::cache::dock) ircd::net::dns::cache::dock; bool ircd::net::dns::cache::put(const hostport &h, const opts &o, const uint &r, const string_view &m) try { using prototype = bool (const hostport &, const opts &, const uint &, const string_view &); static mods::import call { "net_dns_cache", "ircd::net::dns::cache::put" }; return call(h, o, r, m); } catch(const mods::unavailable &e) { thread_local char buf[rfc1035::NAME_BUFSIZE]; log::dwarning { log, "Failed to put error for '%s' in DNS cache :%s", string(buf, h), e.what() }; return false; } bool ircd::net::dns::cache::put(const hostport &h, const opts &o, const records &r) try { using prototype = bool (const hostport &, const opts &, const records &); static mods::import call { "net_dns_cache", "ircd::net::dns::cache::put" }; return call(h, o, r); } catch(const mods::unavailable &e) { thread_local char buf[rfc1035::NAME_BUFSIZE]; log::dwarning { log, "Failed to put '%s' in DNS cache :%s", string(buf, h), e.what() }; return false; } /// This function has an opportunity to respond from the DNS cache. If it /// returns true, that indicates it responded by calling back the user and /// nothing further should be done for them. If it returns false, that /// indicates it did not respond and to proceed normally. The response can /// be of a cached successful result, or a cached error. Both will return /// true. bool ircd::net::dns::cache::get(const hostport &h, const opts &o, const callback &c) try { using prototype = bool (const hostport &, const opts &, const callback &); static mods::import call { "net_dns_cache", "ircd::net::dns::cache::get" }; return call(h, o, c); } catch(const mods::unavailable &e) { thread_local char buf[rfc1035::NAME_BUFSIZE]; log::dwarning { log, "Failed to get '%s' from DNS cache :%s", string(buf, h), e.what() }; return false; } bool ircd::net::dns::cache::for_each(const hostport &h, const opts &o, const closure &c) { using prototype = bool (const hostport &, const opts &, const closure &); static mods::import call { "net_dns_cache", "ircd::net::dns::cache::for_each" }; return call(h, o, c); } bool ircd::net::dns::cache::for_each(const string_view &type, const closure &c) { using prototype = bool (const string_view &, const closure &); static mods::import call { "net_dns_cache", "ircd::net::dns::cache::for_each" }; return call(type, c); } ircd::string_view ircd::net::dns::cache::make_type(const mutable_buffer &out, const uint16_t &type) try { return make_type(out, rfc1035::rqtype.at(type)); } catch(const std::out_of_range &) { throw error { "Record type[%u] is not recognized", type }; } ircd::string_view ircd::net::dns::cache::make_type(const mutable_buffer &out, const string_view &type) { return fmt::sprintf { out, "ircd.dns.rrs.%s", type }; } // // cache::waiter // bool ircd::net::dns::cache::operator==(const waiter &a, const waiter &b) noexcept { return true && a.opts.qtype == b.opts.qtype && a.key && b.key && a.key == b.key; } bool ircd::net::dns::cache::operator!=(const waiter &a, const waiter &b) noexcept { return !operator==(a, b); } // // cache::waiter::waiter // ircd::net::dns::cache::waiter::waiter(const hostport &hp, const dns::opts &opts, dns::callback &&callback) :callback { std::move(callback) } ,opts { opts } ,port { net::port(hp) } ,key { opts.qtype == 33? make_SRV_key(keybuf, hp, opts): tolower(keybuf, host(hp)) } { this->opts.srv = {}; this->opts.proto = {}; assert(this->opts.qtype); } /// Note complications due to reentrance and other factors: /// - This function is invoked from several different places on both the /// timeout and receive contexts, in addition to any evaluator context. /// - This function calls back to users making DNS queries, and they may /// conduct another query in their callback frame -- mid-loop in this /// function. size_t ircd::net::dns::cache::waiter::call(const uint16_t &type, const string_view &tgt, const json::array &rrs) { const ctx::uninterruptible::nothrow ui; size_t ret(0), last; do { const std::lock_guard lock { mutex }; auto it(begin(waiting)); for(last = ret; it != end(waiting);) if(call(*it, type, tgt, rrs)) { it = waiting.erase(it); ++ret; continue; } else ++it; } while(last < ret); if(ret) dock.notify_all(); return ret; } bool ircd::net::dns::cache::waiter::call(waiter &waiter, const uint16_t &type, const string_view &tgt, const json::array &rrs) try { if(tgt != waiter.key) return false; if(type != waiter.opts.qtype) return false; const hostport &target { waiter.opts.qtype == 33? unmake_SRV_key(waiter.key): waiter.key, waiter.port }; assert(waiter.callback); waiter.callback(target, rrs); return true; } catch(const std::exception &e) { log::critical { log, "callback:%p %s,%s :%s", (const void *)&waiter, type, tgt, e.what(), }; return true; }