-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathupstream_system.cpp
79 lines (68 loc) · 3 KB
/
upstream_system.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include "upstream_system.h"
#include <net/if.h>
namespace ag::dns {
SystemUpstream::SystemUpstream(const UpstreamOptions &opts, const UpstreamFactoryConfig &config)
: Upstream(opts, config)
, m_log(AG_FMT("System upstream ({})", opts.address)) {
const auto POS = opts.address.find("://");
if (POS != std::string::npos) {
m_interface = opts.address.substr(POS + 3);
}
}
Error<Upstream::InitError> SystemUpstream::init() {
uint32_t if_index = 0;
if (!m_interface.empty()) {
if_index = if_nametoindex(m_interface.c_str());
if (if_index == 0) {
return make_error(InitError::AE_INVALID_ADDRESS, AG_FMT("Invalid interface name: {}", m_interface));
}
}
auto result = SystemResolver::create(&config().loop, config().timeout, if_index);
if (result.has_error()) {
return make_error(InitError::AE_SYSTEMRESOLVER_INIT_FAILED,
ErrorCodeToString<SystemResolverError>{}(result.error().get()->value()));
}
m_resolver = std::move(result.value());
return {};
}
coro::Task<Upstream::ExchangeResult> SystemUpstream::exchange(const ldns_pkt *request_pkt, const DnsMessageInfo *info) {
const ldns_rr *question = ldns_rr_list_rr(ldns_pkt_question(request_pkt), 0);
const std::string domain = ldns_rdf2str(ldns_rr_owner(question));
const ldns_rr_type RR_TYPE = ldns_rr_get_type(question);
auto result = co_await m_resolver->resolve(domain, RR_TYPE);
if (result.has_error()) {
auto &error = result.error();
if (error->value() != SystemResolverError::AE_DOMAIN_NOT_FOUND
&& error->value() != SystemResolverError::AE_RECORD_NOT_FOUND) {
if (error->value() == SystemResolverError::AE_TIMED_OUT) {
co_return make_error(DnsError::AE_TIMED_OUT, ErrorCodeToString<SystemResolverError>{}(error->value()));
}
co_return make_error(DnsError::AE_INTERNAL_ERROR, ErrorCodeToString<SystemResolverError>{}(error->value()));
}
}
ldns_pkt *reply_pkt = ldns_pkt_clone(request_pkt);
ldns_pkt_set_qr(reply_pkt, true);
ldns_pkt_set_aa(reply_pkt, false);
ldns_pkt_set_rd(reply_pkt, true);
ldns_pkt_set_ra(reply_pkt, true);
ldns_pkt_set_ad(reply_pkt, false);
ldns_pkt_set_cd(reply_pkt, false);
if (result.has_value()) {
ldns_pkt_set_ancount(reply_pkt, ldns_rr_list_rr_count(result.value().get()));
ldns_pkt_set_answer(reply_pkt, ldns_rr_list_clone(result.value().get()));
ldns_pkt_set_rcode(reply_pkt, LDNS_RCODE_NOERROR);
co_return ldns_pkt_ptr{reply_pkt};
}
switch (result.error()->value()) {
case SystemResolverError::AE_DOMAIN_NOT_FOUND:
ldns_pkt_set_rcode(reply_pkt, LDNS_RCODE_NXDOMAIN);
co_return ldns_pkt_ptr{reply_pkt};
case SystemResolverError::AE_RECORD_NOT_FOUND:
ldns_pkt_set_rcode(reply_pkt, LDNS_RCODE_NOERROR);
co_return ldns_pkt_ptr{reply_pkt};
default:
assert(0);
co_return ldns_pkt_ptr{};
}
}
} // namespace ag::dns