mirror of
https://github.com/matrix-construct/construct
synced 2024-10-03 22:28:52 +02:00
ircd::net::dns: Add crucial support for caching errors.
This commit is contained in:
parent
b166d0744d
commit
d54d6b687c
2 changed files with 94 additions and 18 deletions
|
@ -36,6 +36,7 @@ struct ircd::net::dns::resolver
|
||||||
char reply[64_KiB] alignas(16); // Buffer for recv
|
char reply[64_KiB] alignas(16); // Buffer for recv
|
||||||
|
|
||||||
bool handle_error(const error_code &ec) const;
|
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, tag &);
|
||||||
void handle_reply(const header &, const const_buffer &body);
|
void handle_reply(const header &, const const_buffer &body);
|
||||||
void handle(const error_code &ec, const size_t &) noexcept;
|
void handle(const error_code &ec, const size_t &) noexcept;
|
||||||
|
|
111
ircd/net.cc
111
ircd/net.cc
|
@ -2267,12 +2267,20 @@ ircd::net::dns::operator()(const hostport &hostport,
|
||||||
(*resolver)(hostport, opts, std::move(cb));
|
(*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
|
bool
|
||||||
ircd::net::dns::query_cache(const hostport &hp,
|
ircd::net::dns::query_cache(const hostport &hp,
|
||||||
const opts &opts,
|
const opts &opts,
|
||||||
const callback &cb)
|
const callback &cb)
|
||||||
{
|
{
|
||||||
thread_local const rfc1035::record *record[resolver::MAX_COUNT];
|
thread_local const rfc1035::record *record[resolver::MAX_COUNT];
|
||||||
|
std::exception_ptr eptr;
|
||||||
size_t count{0};
|
size_t count{0};
|
||||||
|
|
||||||
//TODO: Better deduction
|
//TODO: Better deduction
|
||||||
|
@ -2292,21 +2300,30 @@ ircd::net::dns::query_cache(const hostport &hp,
|
||||||
const auto &now{ircd::time()};
|
const auto &now{ircd::time()};
|
||||||
for(auto it(pit.first); it != pit.second; )
|
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)
|
if(rr.ttl < now)
|
||||||
{
|
{
|
||||||
it = map.erase(it);
|
it = map.erase(it);
|
||||||
continue;
|
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;
|
record[count++] = &rr;
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(count)
|
|
||||||
cb({}, vector_view<const rfc1035::record *>(record, count));
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
else // Deduced A query (for now)
|
else // Deduced A query (for now)
|
||||||
{
|
{
|
||||||
|
@ -2318,22 +2335,39 @@ ircd::net::dns::query_cache(const hostport &hp,
|
||||||
const auto &now{ircd::time()};
|
const auto &now{ircd::time()};
|
||||||
for(auto it(pit.first); it != pit.second; )
|
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)
|
if(rr.ttl < now)
|
||||||
{
|
{
|
||||||
it = map.erase(it);
|
it = map.erase(it);
|
||||||
continue;
|
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;
|
record[count++] = &rr;
|
||||||
++it;
|
++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
|
ircd::string_view
|
||||||
|
@ -2639,12 +2673,6 @@ try
|
||||||
"Response header is marked as 'Query' and not 'Response'"
|
"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)
|
if(header.qdcount > MAX_COUNT || header.ancount > MAX_COUNT)
|
||||||
throw error
|
throw error
|
||||||
{
|
{
|
||||||
|
@ -2661,6 +2689,12 @@ try
|
||||||
for(size_t i(0); i < header.qdcount; ++i)
|
for(size_t i(0); i < header.qdcount; ++i)
|
||||||
consume(buffer, size(qd.at(i).parse(buffer)));
|
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
|
// Answers are parsed into this buffer
|
||||||
thread_local std::array<rfc1035::answer, MAX_COUNT> an;
|
thread_local std::array<rfc1035::answer, MAX_COUNT> an;
|
||||||
for(size_t i(0); i < header.ancount; ++i)
|
for(size_t i(0); i < header.ancount; ++i)
|
||||||
|
@ -2753,6 +2787,47 @@ catch(const std::exception &e)
|
||||||
tag.cb(std::current_exception(), {});
|
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
|
bool
|
||||||
ircd::net::dns::resolver::handle_error(const error_code &ec)
|
ircd::net::dns::resolver::handle_error(const error_code &ec)
|
||||||
const
|
const
|
||||||
|
|
Loading…
Reference in a new issue