no U-labels for libsrs2
[ghsmtp.git] / DNS-ldns.cpp
blob9899fe5eba51917e94a4097150f43660046bbbf3
1 #include "DNS-ldns.hpp"
3 #include "DNS-iostream.hpp"
5 #include <algorithm>
6 #include <iomanip>
8 #include <cstdbool> // needs to be above ldns includes
9 #include <ldns/ldns.h>
10 #include <ldns/packet.h>
11 #include <ldns/rr.h>
13 #include <arpa/inet.h>
15 #include <glog/logging.h>
17 #include <fmt/format.h>
19 namespace DNS_ldns {
21 std::string rr_name_str(ldns_rdf const* rdf)
23 auto const sz = ldns_rdf_size(rdf);
25 if (sz > LDNS_MAX_DOMAINLEN) {
26 LOG(WARNING) << "rdf size too large";
27 return "<too long>";
29 if (sz == 1) {
30 return ""; // root label
33 auto const data = ldns_rdf_data(rdf);
35 unsigned char src_pos = 0;
36 unsigned char len = data[src_pos];
38 std::string str;
39 str.reserve(64);
40 while ((len > 0) && (src_pos < sz)) {
41 src_pos++;
42 for (unsigned char i = 0; i < len; ++i) {
43 unsigned char c = data[src_pos];
44 // if (c == '.' || c == ';' || c == '(' || c == ')' || c == '\\') {
45 if (c == '.' || c == '\\') {
46 str += '\\';
47 // str += c;
49 // else if (!(isascii(c) && isgraph(c))) {
50 // str += fmt::format("0x{:02x}", c);
51 // }
52 // else {
53 str += c;
54 // }
55 src_pos++;
57 if (src_pos < sz) {
58 str += '.';
60 len = data[src_pos];
63 if (str.length() && ('.' == str.back())) {
64 str.erase(str.length() - 1);
67 return str;
70 std::string rr_str(ldns_rdf const* rdf)
72 CHECK_NOTNULL(rdf);
74 auto const data = static_cast<char const*>(rdf->_data);
75 auto const udata = static_cast<unsigned char const*>(rdf->_data);
77 return std::string(data + 1, static_cast<std::string::size_type>(*udata));
80 Resolver::Resolver()
82 auto const status = ldns_resolver_new_frm_file(&res_, nullptr);
83 CHECK_EQ(status, LDNS_STATUS_OK) << "failed to initialize DNS resolver: "
84 << ldns_get_errorstr_by_id(status);
87 Resolver::~Resolver() { ldns_resolver_deep_free(res_); }
89 Domain::Domain(char const* domain)
90 : str_(domain)
91 , rdfp_(CHECK_NOTNULL(ldns_dname_new_frm_str(domain)))
95 Domain::Domain(std::string const& domain)
96 : str_(domain)
97 , rdfp_(CHECK_NOTNULL(ldns_dname_new_frm_str(domain.c_str())))
101 Domain::~Domain() { ldns_rdf_deep_free(rdfp_); }
103 Query::Query(Resolver const& res, DNS::RR_type type, std::string const& dom)
104 : Query(res, type, dom.c_str())
108 Query::Query(Resolver const& res, DNS::RR_type type, char const* domain)
110 Domain const dom{domain};
112 ldns_status const status = ldns_resolver_query_status(
113 &p_, res.get(), dom.get(), static_cast<ldns_enum_rr_type>(type),
114 LDNS_RR_CLASS_IN, LDNS_RD | LDNS_AD);
116 if (status != LDNS_STATUS_OK) {
117 bogus_or_indeterminate_ = true;
119 // LOG(WARNING) << "Query (" << dom.str() << "/" << type << ") "
120 // << "ldns_resolver_query_status failed: "
121 // << ldns_get_errorstr_by_id(status);
123 // If we have only one nameserver, reset the RTT otherwise all
124 // future use of this resolver object will fail.
126 ldns_resolver_set_nameserver_rtt(res.get(), 0,
127 LDNS_RESOLV_RTT_MIN); // "reachable"
130 if (p_) {
131 authentic_data_ = ldns_pkt_ad(p_);
133 auto const rcode = ldns_pkt_get_rcode(p_);
135 switch (rcode) {
136 case LDNS_RCODE_NOERROR: break;
138 case LDNS_RCODE_NXDOMAIN:
139 nx_domain_ = true;
140 // LOG(WARNING) << "NX domain (" << dom.str() << "/" << type << ")";
141 break;
143 case LDNS_RCODE_SERVFAIL:
144 bogus_or_indeterminate_ = true;
145 // LOG(WARNING) << "DNS server fail (" << dom.str() << "/" << type << ")";
146 break;
148 default:
149 bogus_or_indeterminate_ = true;
150 LOG(WARNING) << "DNS unknown error (" << dom.str() << "/" << type
151 << "), rcode = " << DNS::rcode_c_str(rcode) << " (" << rcode
152 << ")";
153 break;
158 Query::~Query()
160 if (p_)
161 ldns_pkt_free(p_);
164 DNS::RR_collection Query::get_records() const
166 RR_list const rrlst{*this};
167 return rrlst.get_records();
170 std::vector<std::string> Query::get_strings() const
172 RR_list const rrlst{*this};
173 return rrlst.get_strings();
176 RR_list::RR_list(Query const& q)
178 if (q.get()) {
179 // no clones, so no frees required
180 rrlst_answer_ = ldns_pkt_answer(q.get());
181 rrlst_additional_ = ldns_pkt_additional(q.get());
185 RR_list::~RR_list() {}
187 DNS::RR_collection RR_list::get_records() const
189 DNS::RR_collection ret;
191 if (rrlst_answer_) {
193 ret.reserve(ldns_rr_list_rr_count(rrlst_answer_));
195 for (unsigned i = 0; i < ldns_rr_list_rr_count(rrlst_answer_); ++i) {
196 auto const rr = ldns_rr_list_rr(rrlst_answer_, i);
198 if (rr) {
199 // LOG(INFO) << "ldns_rr_rd_count(rr) == " << ldns_rr_rd_count(rr);
201 switch (ldns_rr_get_type(rr)) {
202 case LDNS_RR_TYPE_A: {
203 CHECK_EQ(ldns_rr_rd_count(rr), 1);
204 auto const rdf = ldns_rr_rdf(rr, 0);
205 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_A);
206 ret.emplace_back(DNS::RR_A{ldns_rdf_data(rdf), ldns_rdf_size(rdf)});
207 break;
209 case LDNS_RR_TYPE_CNAME: {
210 CHECK_EQ(ldns_rr_rd_count(rr), 1);
211 auto const rdf = ldns_rr_rdf(rr, 0);
212 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_DNAME);
213 ret.emplace_back(DNS::RR_CNAME{rr_name_str(rdf)});
214 break;
216 case LDNS_RR_TYPE_PTR: {
217 CHECK_EQ(ldns_rr_rd_count(rr), 1);
218 auto const rdf = ldns_rr_rdf(rr, 0);
219 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_DNAME);
220 ret.emplace_back(DNS::RR_PTR{rr_name_str(rdf)});
221 break;
223 case LDNS_RR_TYPE_MX: {
224 CHECK_EQ(ldns_rr_rd_count(rr), 2);
225 auto const rdf_0 = ldns_rr_rdf(rr, 0);
226 CHECK_EQ(ldns_rdf_get_type(rdf_0), LDNS_RDF_TYPE_INT16);
227 auto const rdf_1 = ldns_rr_rdf(rr, 1);
228 CHECK_EQ(ldns_rdf_get_type(rdf_1), LDNS_RDF_TYPE_DNAME);
229 ret.emplace_back(
230 DNS::RR_MX{rr_name_str(rdf_1), ldns_rdf2native_int16(rdf_0)});
231 break;
233 case LDNS_RR_TYPE_TXT: {
234 CHECK_EQ(ldns_rr_rd_count(rr), 1);
235 auto const rdf = ldns_rr_rdf(rr, 0);
236 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_STR);
237 ret.emplace_back(DNS::RR_TXT{rr_str(rdf)});
238 break;
240 case LDNS_RR_TYPE_AAAA: {
241 CHECK_EQ(ldns_rr_rd_count(rr), 1);
242 auto const rdf = ldns_rr_rdf(rr, 0);
243 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_AAAA);
244 ret.emplace_back(
245 DNS::RR_AAAA{ldns_rdf_data(rdf), ldns_rdf_size(rdf)});
246 break;
248 case LDNS_RR_TYPE_TLSA: {
249 CHECK_EQ(ldns_rr_rd_count(rr), 4);
251 auto const usage{[&] {
252 auto const rdf = ldns_rr_rdf(rr, 0);
253 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_CERTIFICATE_USAGE);
254 return ldns_rdf2native_int8(rdf);
255 }()};
257 auto const selector{[&] {
258 auto const rdf = ldns_rr_rdf(rr, 1);
259 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_SELECTOR);
260 return ldns_rdf2native_int8(rdf);
261 }()};
263 auto const matching_type{[&] {
264 auto const rdf = ldns_rr_rdf(rr, 2);
265 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_MATCHING_TYPE);
266 return ldns_rdf2native_int8(rdf);
267 }()};
269 auto const rdf = ldns_rr_rdf(rr, 3);
270 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_HEX);
272 ret.emplace_back(DNS::RR_TLSA{usage, selector, matching_type,
273 ldns_rdf_data(rdf),
274 ldns_rdf_size(rdf)});
275 break;
278 default:
279 LOG(WARNING) << "unknown RR type == " << ldns_rr_get_type(rr);
280 break;
286 if (rrlst_additional_) {
287 auto const rr_count = ldns_rr_list_rr_count(rrlst_additional_);
288 if (rr_count) {
289 LOG(WARNING) << rr_count << " additional RR records";
290 for (unsigned i = 0; i < rr_count; ++i) {
291 auto const rr = ldns_rr_list_rr(rrlst_additional_, i);
292 if (rr) {
293 auto type = ldns_rr_get_type(rr);
294 LOG(WARNING) << "additional record " << i << " type "
295 << DNS::RR_type_c_str(type);
301 return ret;
304 std::vector<std::string> RR_list::get_strings() const
306 std::vector<std::string> ret;
308 if (rrlst_answer_) {
310 ret.reserve(ldns_rr_list_rr_count(rrlst_answer_));
312 for (unsigned i = 0; i < ldns_rr_list_rr_count(rrlst_answer_); ++i) {
313 auto const rr = ldns_rr_list_rr(rrlst_answer_, i);
315 if (rr) {
316 // LOG(INFO) << "ldns_rr_rd_count(rr) == " << ldns_rr_rd_count(rr);
318 switch (ldns_rr_get_type(rr)) {
319 case LDNS_RR_TYPE_A: {
320 CHECK_EQ(ldns_rr_rd_count(rr), 1);
321 auto const rdf = ldns_rr_rdf(rr, 0);
322 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_A);
323 auto const a{DNS::RR_A{ldns_rdf_data(rdf), ldns_rdf_size(rdf)}};
324 ret.emplace_back(a.c_str());
325 break;
327 case LDNS_RR_TYPE_CNAME: {
328 CHECK_EQ(ldns_rr_rd_count(rr), 1);
329 auto const rdf = ldns_rr_rdf(rr, 0);
330 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_DNAME);
331 ret.emplace_back(rr_name_str(rdf));
332 break;
334 case LDNS_RR_TYPE_PTR: {
335 CHECK_EQ(ldns_rr_rd_count(rr), 1);
336 auto const rdf = ldns_rr_rdf(rr, 0);
337 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_DNAME);
338 ret.emplace_back(rr_name_str(rdf));
339 break;
341 case LDNS_RR_TYPE_MX: {
342 CHECK_EQ(ldns_rr_rd_count(rr), 2);
343 auto const rdf_0 = ldns_rr_rdf(rr, 0);
344 CHECK_EQ(ldns_rdf_get_type(rdf_0), LDNS_RDF_TYPE_INT16);
345 auto const rdf_1 = ldns_rr_rdf(rr, 1);
346 CHECK_EQ(ldns_rdf_get_type(rdf_1), LDNS_RDF_TYPE_DNAME);
347 ret.emplace_back(rr_name_str(rdf_1));
348 break;
350 case LDNS_RR_TYPE_TXT: {
351 CHECK_EQ(ldns_rr_rd_count(rr), 1);
352 auto const rdf = ldns_rr_rdf(rr, 0);
353 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_STR);
354 ret.emplace_back(rr_str(rdf));
355 break;
357 case LDNS_RR_TYPE_AAAA: {
358 CHECK_EQ(ldns_rr_rd_count(rr), 1);
359 auto const rdf = ldns_rr_rdf(rr, 0);
360 CHECK_EQ(ldns_rdf_get_type(rdf), LDNS_RDF_TYPE_AAAA);
361 auto const a{DNS::RR_AAAA{ldns_rdf_data(rdf), ldns_rdf_size(rdf)}};
362 ret.emplace_back(a.c_str());
363 break;
365 default:
366 LOG(WARNING) << "unknown RR type == " << ldns_rr_get_type(rr);
367 break;
373 return ret;
376 DNS::RR_collection Resolver::get_records(DNS::RR_type typ,
377 char const* domain) const
379 Query const q{*this, typ, domain};
380 RR_list const rrlst{q};
381 return rrlst.get_records();
384 std::vector<std::string> Resolver::get_strings(DNS::RR_type typ,
385 char const* domain) const
387 Query const q{*this, typ, domain};
388 RR_list const rrlst{q};
389 return rrlst.get_strings();
392 } // namespace DNS_ldns