3 #include "DNS-iostream.hpp"
13 #include <arpa/nameser.h>
15 #include <glog/logging.h>
19 DEFINE_bool(log_dns_data
, false, "log all DNS TCP protocol data");
20 DEFINE_bool(random_dns_servers
, false, "Pick starting DNS server at random");
23 // The default timeout in glibc is 5 seconds. My setup with unbound
24 // in front of stubby with DNSSEC checking and all that seems to work
25 // better with just a little more time.
27 auto constexpr read_timeout
{std::chrono::seconds(7)};
28 auto constexpr write_timeout
{std::chrono::seconds(1)};
30 enum class sock_type
: bool { stream
, dgram
};
33 char const* host
; // name used to match cert
39 constexpr nameserver nameservers
[]{
80 "2001:4860:4860::8888",
86 "2001:4860:4860::8844",
91 "1dot1dot1dot1.cloudflare-dns.com",
97 "1dot1dot1dot1.cloudflare-dns.com",
98 "2606:4700:4700::1111",
103 "1dot1dot1dot1.cloudflare-dns.com",
104 "2606:4700:4700::1001",
134 } // namespace Config
136 template <typename T
, std::size_t N
>
137 constexpr std::size_t countof(T
const (&)[N
]) noexcept
144 Resolver::Resolver(fs::path config_path
)
146 auto tries
= countof(Config::nameservers
);
148 if (FLAGS_random_dns_servers
) {
149 std::uniform_int_distribution
<int> uniform_dist(
150 0, countof(Config::nameservers
) - 1);
151 ns_
= uniform_dist(rng_
);
154 ns_
= static_cast<int>(countof(Config::nameservers
) - 1);
159 // try the next one, with wrap
160 if (++ns_
== countof(Config::nameservers
))
163 auto const& nameserver
= Config::nameservers
[ns_
];
164 auto typ
= (nameserver
.typ
== Config::sock_type::stream
) ? SOCK_STREAM
168 osutil::get_port(nameserver
.port
, (typ
== SOCK_STREAM
) ? "tcp" : "udp");
171 if (IP4::is_address(nameserver
.addr
)) {
172 ns_fd_
= socket(AF_INET
, typ
, 0);
173 PCHECK(ns_fd_
>= 0) << "socket() failed";
175 auto in4
{sockaddr_in
{}};
176 in4
.sin_family
= AF_INET
;
177 in4
.sin_port
= htons(port
);
178 CHECK_EQ(inet_pton(AF_INET
, nameserver
.addr
,
179 reinterpret_cast<void*>(&in4
.sin_addr
)),
181 if (connect(ns_fd_
, reinterpret_cast<const sockaddr
*>(&in4
),
183 PLOG(INFO
) << "connect failed " << nameserver
.host
<< '['
184 << nameserver
.addr
<< "]:" << nameserver
.port
;
190 else if (IP6::is_address(nameserver
.addr
)) {
191 ns_fd_
= socket(AF_INET6
, typ
, 0);
192 PCHECK(ns_fd_
>= 0) << "socket() failed";
194 auto in6
{sockaddr_in6
{}};
195 in6
.sin6_family
= AF_INET6
;
196 in6
.sin6_port
= htons(port
);
197 CHECK_EQ(inet_pton(AF_INET6
, nameserver
.addr
,
198 reinterpret_cast<void*>(&in6
.sin6_addr
)),
200 if (connect(ns_fd_
, reinterpret_cast<const sockaddr
*>(&in6
),
202 PLOG(INFO
) << "connect failed " << nameserver
.host
<< '['
203 << nameserver
.addr
<< "]:" << nameserver
.port
;
210 POSIX::set_nonblocking(ns_fd_
);
212 if (nameserver
.typ
== Config::sock_type::stream
) {
213 ns_sock_
= std::make_unique
<Sock
>(ns_fd_
, ns_fd_
);
214 if (FLAGS_log_dns_data
) {
215 ns_sock_
->log_data_on();
218 ns_sock_
->log_data_off();
222 DNS::RR_collection tlsa_rrs
; // empty FIXME!
223 ns_sock_
->starttls_client(config_path
, nullptr, nameserver
.host
,
225 if (ns_sock_
->verified()) {
239 LOG(FATAL
) << "no nameservers left to try";
242 message
Resolver::xchg(message
const& q
)
244 if (Config::nameservers
[ns_
].typ
== Config::sock_type::stream
) {
245 CHECK_EQ(ns_fd_
, -1);
247 auto const sp
= static_cast<std::span
<DNS::message::octet
const>>(q
);
249 uint16_t sz
= sp
.size();
252 ns_sock_
->out().write(reinterpret_cast<char const*>(&sz
), sizeof sz
);
253 ns_sock_
->out().write(reinterpret_cast<char const*>(sp
.data()), sp
.size());
254 ns_sock_
->out().flush();
257 ns_sock_
->in().read(reinterpret_cast<char*>(&sz
), sizeof sz
);
260 DNS::message::container_t
bfr(sz
);
261 ns_sock_
->in().read(reinterpret_cast<char*>(bfr
.data()), sz
);
262 CHECK_EQ(ns_sock_
->in().gcount(), std::streamsize(sz
));
264 if (!ns_sock_
->in()) {
265 LOG(WARNING
) << "Resolver::xchg was able to read only "
266 << ns_sock_
->in().gcount() << " octets";
269 return message
{std::move(bfr
)};
272 CHECK(Config::nameservers
[ns_
].typ
== Config::sock_type::dgram
);
277 auto const sp
= static_cast<std::span
<DNS::message::octet
const>>(q
);
280 POSIX::write(ns_fd_
, reinterpret_cast<char const*>(sp
.data()), sp
.size(),
281 Config::write_timeout
, t_o
);
282 if (wrlen
!= std::streamsize(sp
.size())) {
283 LOG(WARNING
) << "DNS write failed";
287 LOG(WARNING
) << "DNS write timed out";
291 DNS::message::container_t
bfr(Config::max_udp_sz
);
293 auto constexpr hook
{[]() {}};
294 auto const a_rdlen
= POSIX::read(ns_fd_
, reinterpret_cast<char*>(bfr
.data()),
295 bfr
.size(), hook
, Config::read_timeout
, t_o
);
297 LOG(WARNING
) << "DNS read failed";
301 LOG(WARNING
) << "DNS read timed out";
305 bfr
.resize(a_rdlen
); // down from max_udp_sz
308 return message
{std::move(bfr
)};
311 RR_collection
Resolver::get_records(RR_type typ
, char const* name
)
313 Query
q(*this, typ
, name
);
314 return q
.get_records();
317 std::vector
<std::string
> Resolver::get_strings(RR_type typ
, char const* name
)
319 Query
q(*this, typ
, name
);
320 return q
.get_strings();
323 bool Query::xchg_(Resolver
& res
, uint16_t id
)
331 auto const a_sp
= static_cast<std::span
<DNS::message::octet
const>>(a_
);
334 bogus_or_indeterminate_
= true;
335 LOG(WARNING
) << "no reply from nameserver";
339 if (a_sp
.size() < message::min_sz()) {
340 bogus_or_indeterminate_
= true;
341 LOG(WARNING
) << "packet too small";
348 LOG(WARNING
) << "packet out of order; ids don't match, got " << a_
.id()
349 << " expecting " << id
;
356 bogus_or_indeterminate_
= true;
357 LOG(WARNING
) << "no tries left, giving up";
362 Query::Query(Resolver
& res
, RR_type type
, char const* name
)
365 uint16_t id
= res
.rnd_id();
366 uint16_t cls
= ns_c_in
;
368 q_
= create_question(name
, type
, cls
, id
);
373 auto const a_sp
= static_cast<std::span
<DNS::message::octet
const>>(a_
);
375 if (a_sp
.size() < message::min_sz()) {
376 bogus_or_indeterminate_
= true;
377 LOG(INFO
) << "bad (or no) reply for " << name
<< '/' << type
;
381 check_answer(nx_domain_
, bogus_or_indeterminate_
, rcode_
, extended_rcode_
,
382 truncation_
, authentic_data_
, has_record_
, q_
, a_
, type
, name
);
385 // if UDP, retry with TCP
386 bogus_or_indeterminate_
= true;
387 LOG(INFO
) << "truncated answer for " << name
<< '/' << type
;
391 RR_collection
Query::get_records()
393 if (bogus_or_indeterminate_
)
394 return RR_collection
{};
396 return DNS::get_records(a_
, bogus_or_indeterminate_
);
399 std::vector
<std::string
> Query::get_strings()
401 std::vector
<std::string
> ret
;
403 auto const rr_set
= get_records();
405 for (auto rr
: rr_set
) {
407 [&ret
, type
= type_
](auto const& r
) {
408 if (type
== r
.rr_type()) {
409 auto const s
= r
.as_str();