0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-07-04 17:48:35 +02:00

ircd::net::dns: Add crucial support for caching errors.

This commit is contained in:
Jason Volk 2018-03-01 23:55:10 -08:00
parent b166d0744d
commit d54d6b687c
2 changed files with 94 additions and 18 deletions

View file

@ -36,6 +36,7 @@ struct ircd::net::dns::resolver
char reply[64_KiB] alignas(16); // Buffer for recv
bool handle_error(const error_code &ec) const;
bool handle_error(const header &, const rfc1035::question &, const dns::opts &);
void handle_reply(const header &, const const_buffer &body, tag &);
void handle_reply(const header &, const const_buffer &body);
void handle(const error_code &ec, const size_t &) noexcept;

View file

@ -2267,12 +2267,20 @@ ircd::net::dns::operator()(const hostport &hostport,
(*resolver)(hostport, opts, std::move(cb));
}
/// 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::query_cache(const hostport &hp,
const opts &opts,
const callback &cb)
{
thread_local const rfc1035::record *record[resolver::MAX_COUNT];
std::exception_ptr eptr;
size_t count{0};
//TODO: Better deduction
@ -2292,21 +2300,30 @@ ircd::net::dns::query_cache(const hostport &hp,
const auto &now{ircd::time()};
for(auto it(pit.first); it != pit.second; )
{
const auto &rr{pit.first->second};
const auto &rr{it->second};
// Cached entry is too old, ignore and erase
if(rr.ttl < now)
{
it = map.erase(it);
continue;
}
// Cached entry is a cached error, we set the eptr, but also
// include the record and increment the count like normal.
assert(!eptr);
if(!rr.tgt)
{
const auto rcode{3}; //NXDomain
eptr = std::make_exception_ptr(rfc1035::error
{
"protocol error #%u (cached) :%s", rcode, rfc1035::rcode.at(rcode)
});
}
record[count++] = &rr;
++it;
}
if(count)
cb({}, vector_view<const rfc1035::record *>(record, count));
return count;
}
else // Deduced A query (for now)
{
@ -2318,22 +2335,39 @@ ircd::net::dns::query_cache(const hostport &hp,
const auto &now{ircd::time()};
for(auto it(pit.first); it != pit.second; )
{
const auto &rr{pit.first->second};
const auto &rr{it->second};
// Cached entry is too old, ignore and erase
if(rr.ttl < now)
{
it = map.erase(it);
continue;
}
// Cached entry is a cached error, we set the eptr, but also
// include the record and increment the count like normal.
assert(!eptr);
if(!rr.ip4)
{
const auto rcode{3}; //NXDomain
eptr = std::make_exception_ptr(rfc1035::error
{
"protocol error #%u (cached) :%s", rcode, rfc1035::rcode.at(rcode)
});
}
record[count++] = &rr;
++it;
}
if(count)
cb({}, vector_view<const rfc1035::record *>(record, count));
return count;
}
assert(count || !eptr); // no error if no cache response
assert(!eptr || count == 1); // if error, should only be one entry.
if(count)
cb(std::move(eptr), vector_view<const rfc1035::record *>(record, count));
return count;
}
ircd::string_view
@ -2639,12 +2673,6 @@ try
"Response header is marked as 'Query' and not 'Response'"
};
if(header.rcode)
throw rfc1035::error
{
"protocol error: %s", rfc1035::rcode.at(header.rcode)
};
if(header.qdcount > MAX_COUNT || header.ancount > MAX_COUNT)
throw error
{
@ -2661,6 +2689,12 @@ try
for(size_t i(0); i < header.qdcount; ++i)
consume(buffer, size(qd.at(i).parse(buffer)));
if(!handle_error(header, qd.at(0), tag.opts))
throw rfc1035::error
{
"protocol error #%u :%s", header.rcode, rfc1035::rcode.at(header.rcode)
};
// Answers are parsed into this buffer
thread_local std::array<rfc1035::answer, MAX_COUNT> an;
for(size_t i(0); i < header.ancount; ++i)
@ -2753,6 +2787,47 @@ catch(const std::exception &e)
tag.cb(std::current_exception(), {});
}
bool
ircd::net::dns::resolver::handle_error(const header &header,
const rfc1035::question &q,
const dns::opts &opts)
{
switch(header.rcode)
{
case 0: // NoError; continue
return true;
case 3: // NXDomain; exception
{
if(opts.cache_result) switch(q.qtype)
{
case 1: // A
{
const auto &host{rstrip(q.name, '.')};
rfc1035::record::A record;
record.ttl = ircd::time() + hours(24).count(); //TODO: conf
cache.A.emplace(host, record);
break;
}
case 33:
{
const auto &host{rstrip(q.name, '.')};
rfc1035::record::SRV record;
record.ttl = ircd::time() + hours(24).count(); //TODO: conf
cache.SRV.emplace(host, record);
break;
}
}
return false;
}
default: // Unhandled error; exception
return false;
}
}
bool
ircd::net::dns::resolver::handle_error(const error_code &ec)
const