9 static const char rcsid[] __attribute__((used)) =
"$Id: DNS.cpp 1884 2017-04-15 21:42:02Z sella $";
12 #include "DNSException.h"
13 #include "DNSCallback.h"
14 #include "../util/ThreadPool.h"
15 #include "../util/Time.h"
20 #include <arpa/inet.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
29 struct sockaddr_storage *ss;
31 struct sockaddr_in *sa_in;
32 struct sockaddr_in6 *sa_in6;
35 using namespace sella::net;
36 using namespace sella::util;
38 const std::vector<std::string> DNS::empty;
39 bool walk(std::vector<IPAddress> &ips,
struct addrinfo *result,
union __sockaddr_union &u,
int protocol);
41 DNS::DNS(
const std::vector<std::string> &nameservers,
const std::vector<std::string> &searches)
throw (DNSException) :
46 init(nameservers, searches);
49 DNS::DNS(
sella::util::ThreadPool *pool,
const std::vector<std::string> &nameservers,
const std::vector<std::string> &searches)
throw (DNSException) :
54 init(nameservers, searches);
56 Task::callback
function = std::bind(&DNS::receive,
this, 0, 0);
57 task = Task::shared_ptr();
58 task->setCallback(
function);
61 DNS::~DNS() throw () {
63 pool->unschedule(task,
true);
66 ::dns_free(this->context);
69 std::vector<std::string> DNS::resolve(
const std::string &name,
int record,
int protocol)
throw (DNSException) {
72 resolve(&c, name, record, protocol);
74 while (getActive() > 0) {
78 return c.getResponses();
81 sella::variant::Variant DNS::resolve(
const std::vector<std::string> &names,
int record,
int protocol)
throw (DNSException) {
85 for (
size_t i = 0; i < names.size(); i++) {
86 resolve(&(c[i]), names[i], record, protocol);
89 while (getActive() > 0) {
93 for (
size_t i = 0; i < names.size(); i++) {
94 const auto &r = c[i].getResponses();
95 auto &response = responses[c[i].qname][
"response"];
97 for (
auto i = r.begin(); i != r.end(); ++i) {
98 response.push_back(*i);
101 responses[c[i].qname][
"qname"] = c[i].getQname();
102 responses[c[i].qname][
"ttl"] = c[i].getTTL();
103 responses[c[i].qname][
"error"] = c[i].getErrorName();
109 std::vector<std::string> DNS::bulkResolve(
const std::vector<std::string> &names,
int record,
int protocol)
throw (DNSException) {
110 std::vector<std::string> responses;
113 for (
size_t i = 0; i < names.size(); i++) {
114 resolve(&(c[i]), names[i], record, protocol);
117 while (getActive() > 0) {
121 for (
size_t i = 0; i < names.size(); i++) {
122 const auto &r = c[i].getResponses();
124 responses.insert(responses.end(), r.begin(), r.end());
130 std::vector<std::string> DNS::sresolve(
const std::string &name,
int protocol)
throw (DNSException) {
131 const std::vector<IPAddress> &addresses = DNS::sresolve_ip(name, protocol);
132 std::vector<std::string> response;
134 for (
auto a = addresses.begin(); a != addresses.end(); ++a) {
135 response.push_back(a->str());
141 std::vector<IPAddress> DNS::sresolve_ip(
const std::string &name,
int protocol)
throw (DNSException) {
144 struct sockaddr_storage addr;
145 struct addrinfo hints, *result;
146 std::vector<IPAddress> ips;
148 memset(&hints, 0,
sizeof(
struct addrinfo));
149 hints.ai_family = AF_UNSPEC;
150 hints.ai_socktype = SOCK_DGRAM;
151 hints.ai_flags = AI_PASSIVE;
152 hints.ai_protocol = 0;
153 hints.ai_canonname = NULL;
154 hints.ai_addr = NULL;
155 hints.ai_next = NULL;
157 if (protocol == IPv4Required) {
158 hints.ai_family = AF_INET;
159 }
else if (protocol == IPv6Required) {
160 hints.ai_family = AF_INET6;
162 THROW(DNSException,
"unknown protocol (%d)", protocol);
165 if ((s = getaddrinfo(name.c_str(), NULL, &hints, &result)) != 0) {
166 if (s == EAI_NODATA) {
170 THROW_CODE(DNSException, s,
"getaddrinfo() failed: %s", gai_strerror(s));
173 u.ss = (
struct sockaddr_storage*) &addr;
174 u.sa_in = (
struct sockaddr_in*) &addr;
175 memset(&addr, 0,
sizeof(addr));
178 if (protocol == AnyProtocol) {
179 walk(ips, result, u, AF_UNSPEC);
180 }
else if (protocol == IPv4Preferred) {
181 if (!walk(ips, result, u, AF_INET)) {
182 walk(ips, result, u, AF_INET6);
184 }
else if (protocol == IPv4Required) {
185 walk(ips, result, u, AF_INET);
186 }
else if (protocol == IPv6Preferred) {
187 if (!walk(ips, result, u, AF_INET6)) {
188 walk(ips, result, u, AF_INET);
190 }
else if (protocol == IPv6Required) {
191 walk(ips, result, u, AF_INET6);
194 freeaddrinfo(result);
195 }
catch (DNSException &e) {
196 freeaddrinfo(result);
204 DNSCallback* DNS::resolve(
DNSCallback* callback,
const std::string &name,
int record,
int protocol)
throw (DNSException) {
205 if (callback == NULL) {
206 THROW(DNSException,
"invalid NULL callback supplied");
209 callback->qname = name;
210 callback->record = record;
211 callback->protocol = protocol;
212 callback->flags = flags;
220 ::dns_submit_p(this->context, name.c_str(), DNS_C_IN, DNS_T_A, flags, ::dns_parse_a4, DNS::callback_a, callback);
225 ::dns_submit_p(this->context, name.c_str(), DNS_C_IN, DNS_T_AAAA, flags, ::dns_parse_a6, DNS::callback_aaaa, callback);
229 THROW(DNSException,
"invalid protocol supplied (%d); must be AnyProtocol, IPv4Preferred, IPv4Required, IPv6Preferred, IPv6Required", protocol);
237 if (ip.getFamily() == AF_INET) {
238 ::dns_submit_a4ptr(this->context, &(ip.getIPv4AddressStructure()), DNS::callback_ptr, callback);
240 ::dns_submit_a6ptr(this->context, &(ip.getIPv6AddressStructure()), DNS::callback_ptr, callback);
244 }
catch (AddressException &e) {
245 RETHROW(DNSException, e);
249 THROW(DNSException,
"record type %d has not been implemented yet", record);
254 pool->reschedule(task, 0);
260 bool DNS::receive(uint64_t usec, time_t now)
throw () {
262 struct timeval tv = { 0, 0 };
265 tv = sella::util::Time::usectotv(usec);
272 std::lock_guard<std::mutex> lg(mutex);
274 if (pool && getActive() > 0) {
275 pool->reschedule(task, 0, interval);
278 ::dns_timeouts(this->context, -1, now);
280 int s = dns_sock(context);
285 if (select(s + 1, &rfds, NULL, NULL, &tv) > 0) {
286 ::dns_ioevent(this->context, now);
294 void DNS::setInterval(uint64_t usec)
throw () {
295 this->interval = (usec > 0) ? usec : DefaultInterval;
298 void DNS::setNoSearch(
bool on)
throw () {
299 flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
304 flags &= ~DNS_NOSRCH;
307 dns_set_opt(context, DNS_OPT_FLAGS, flags);
310 void DNS::setNoRecursion(
bool on)
throw () {
311 flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
319 dns_set_opt(context, DNS_OPT_FLAGS, flags);
322 void DNS::setAuthoritativeOnly(
bool on)
throw () {
323 flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
328 flags &= ~DNS_AAONLY;
331 dns_set_opt(context, DNS_OPT_FLAGS, flags);
334 void DNS::setTimeout(
int seconds)
throw () {
335 dns_set_opt(context, DNS_OPT_TIMEOUT, seconds);
338 void DNS::setRetries(
int retries)
throw () {
339 dns_set_opt(context, DNS_OPT_NTRIES, retries);
342 void DNS::setDots(
int ndots)
throw () {
343 dns_set_opt(context, DNS_OPT_NDOTS, ndots);
346 void DNS::setUDPSize(
int bytes)
throw () {
347 dns_set_opt(context, DNS_OPT_UDPSIZE, bytes);
350 void DNS::setUDPPort(
short port)
throw () {
351 dns_set_opt(context, DNS_OPT_PORT, (
int) port);
354 int DNS::getActive() throw () {
355 return dns_active(context);
358 int DNS::getFD() throw () {
359 return dns_sock(context);
362 const DNS::shared DNS::shared_ptr(
const std::vector<std::string> &nameservers,
const std::vector<std::string> &searches)
throw (DNSException) {
363 return std::make_shared<DNS>(nameservers, searches);
366 const DNS::shared DNS::shared_ptr(
sella::util::ThreadPool *pool,
const std::vector<std::string> &nameservers,
const std::vector<std::string> &searches)
throw (DNSException) {
367 return std::make_shared<DNS>(pool, nameservers, searches);
370 const IPAddress DNS::convertArpa(
const std::string &qname)
throw (DNSException) {
371 if (qname.size() < 5 || qname.substr(qname.size() - 5).compare(
".arpa") != 0) {
372 THROW(DNSException,
"Input is not a valid .in-addr.arpa or .ip6.arapa record");
377 const auto &tokens = String::split(qname,
'.',
false);
379 if (tokens.size() == 6) {
380 buf.append(tokens[3]).append(
".").append(tokens[2]).append(
".").append(tokens[1]).append(
".").append(tokens[0]);
381 }
else if (tokens.size() == 34) {
382 for (ssize_t i = 31; i >= 0; i--) {
383 buf.append(tokens[i]);
385 if (i % 4 == 0 && i > 0) {
390 THROW(DNSException,
"Input is not a valid .in-addr.arpa or .ip6.arapa record");
394 }
catch (AddressException &e) {
395 THROW(DNSException,
"Input is not a valid .in-addr.arpa or .ip6.arapa record");
399 void DNS::callback_a(
struct dns_ctx *context,
void *result,
void *argument) {
400 std::vector<std::string> resolved;
402 struct dns_rr_a4 *response =
reinterpret_cast<struct dns_rr_a4 *
>(result);
407 int status = ::dns_status(context);
409 if (status != DNS_E_NOERROR && status != DNS_E_NODATA) {
411 callback->failure(callback->qname, static_cast<DNSCallback::Error>(::dns_status(context)));
412 }
else if (!response || response->dnsa4_nrr == 0) {
414 if (callback->protocol != IPv4Required) {
415 if (callback->protocol == IPv4Preferred) {
416 callback->protocol = IPv6Required;
420 ::dns_submit_p(context, callback->qname.c_str(), DNS_C_IN, DNS_T_AAAA, callback->flags, ::dns_parse_a6, DNS::callback_aaaa, callback);
423 callback->success(callback->qname, std::string(), -1, resolved);
427 for (
int i = 0; i < response->dnsa4_nrr; i++) {
429 resolved.push_back(
IPAddress(&response->dnsa4_addr[i]).str());
430 }
catch (AddressException &e) { }
434 if (strcmp(response->dnsa4_qname, response->dnsa4_cname) != 0) {
435 callback->success(response->dnsa4_qname, response->dnsa4_cname, response->dnsa4_ttl, resolved);
437 callback->success(response->dnsa4_qname, std::string(), response->dnsa4_ttl, resolved);
440 if (callback->protocol != IPv4Required && callback->protocol != IPv4Preferred) {
442 ::dns_submit_p(context, response->dnsa4_qname, DNS_C_IN, DNS_T_AAAA, callback->flags, ::dns_parse_a6, DNS::callback_aaaa, callback);
447 void DNS::callback_aaaa(
struct dns_ctx *context,
void *result,
void *argument) {
448 std::vector<std::string> resolved;
450 struct dns_rr_a6 *response =
reinterpret_cast<struct dns_rr_a6 *
>(result);
455 int status = ::dns_status(context);
457 if (status != DNS_E_NOERROR && status != DNS_E_NODATA) {
459 callback->failure(callback->qname, static_cast<DNSCallback::Error>(::dns_status(context)));
460 }
else if (!response || response->dnsa6_nrr == 0) {
462 if (callback->protocol != IPv6Required && callback->protocol != AnyProtocol) {
463 if (callback->protocol == IPv6Preferred) {
464 callback->protocol = IPv4Required;
468 ::dns_submit_p(context, callback->qname.c_str(), DNS_C_IN, DNS_T_A, callback->flags, ::dns_parse_a4, DNS::callback_a, callback);
471 callback->success(callback->qname, std::string(), -1, resolved);
475 for (
int i = 0; i < response->dnsa6_nrr; i++) {
477 resolved.push_back(
IPAddress(&response->dnsa6_addr[i]).str());
478 }
catch (AddressException &e) { }
482 if (strcmp(response->dnsa6_qname, response->dnsa6_cname) != 0) {
483 callback->success(response->dnsa6_qname, response->dnsa6_cname, response->dnsa6_ttl, resolved);
485 callback->success(response->dnsa6_qname, std::string(), response->dnsa6_ttl, resolved);
488 if (callback->protocol != IPv6Required && callback->protocol != IPv6Preferred && callback->protocol != AnyProtocol) {
490 ::dns_submit_p(context, response->dnsa6_qname, DNS_C_IN, DNS_T_A, callback->flags, ::dns_parse_a4, DNS::callback_a, callback);
495 void DNS::callback_ptr(
struct dns_ctx *context, dns_rr_ptr *result,
void *argument) {
496 std::vector<std::string> reverses;
498 struct dns_rr_ptr *response =
reinterpret_cast<struct dns_rr_ptr*
>(result);
503 int status = ::dns_status(context);
505 if (status != DNS_E_NOERROR && status != DNS_E_NODATA) {
507 callback->failure(callback->qname, static_cast<DNSCallback::Error>(::dns_status(context)));
508 }
else if (!response || response->dnsptr_nrr == 0) {
509 callback->success(callback->qname, std::string(), -1, reverses);
512 for (
int i = 0; i < response->dnsptr_nrr; i++) {
513 reverses.push_back(response->dnsptr_ptr[i]);
517 if (strcmp(response->dnsptr_qname, response->dnsptr_cname) != 0) {
518 callback->success(response->dnsptr_qname, response->dnsptr_cname, response->dnsptr_ttl, reverses);
520 callback->success(response->dnsptr_qname, std::string(), response->dnsptr_ttl, reverses);
525 void DNS::init(
const std::vector<std::string> &nameservers,
const std::vector<std::string> &searches) {
526 if (dns_init(NULL, 0) < 0) {
527 THROW(DNSException,
"failed to initialize default DNS context");
530 if (!(this->context = dns_new(NULL))) {
531 THROW(DNSException,
"failed to allocate DNS context");
534 if (::dns_init(this->context, 0) < 0) {
535 THROW(DNSException,
"failed to initialize DNS context");
538 if (!nameservers.empty()) {
539 ::dns_add_serv(context, NULL);
541 for (
auto n = nameservers.begin(); n != nameservers.end(); ++n) {
542 if (::dns_add_serv(context, n->c_str()) < 0) {
543 THROW2(DNSException,
"failed to set nameserver '%s'", n->c_str());
548 if (!searches.empty()) {
549 ::dns_add_srch(context, NULL);
551 for (
auto s = searches.begin(); s != searches.end(); ++s) {
552 if (::dns_add_srch(context, s->c_str()) < 0) {
553 THROW2(DNSException,
"failed to set search '%s'", s->c_str());
558 this->flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
560 if (::dns_open(this->context) < 1) {
561 THROW(DNSException,
"failed to open DNS socket");
565 bool walk(std::vector<IPAddress> &ips,
struct addrinfo *result,
union __sockaddr_union &u,
int protocol) {
570 for (rp = result; rp != NULL; rp = rp->ai_next) {
571 if ((protocol == AF_INET || protocol == AF_UNSPEC) && rp->ai_family == AF_INET) {
573 memcpy(u.sa_in, rp->ai_addr, rp->ai_addrlen);
574 u.ss->ss_family = protocol;
577 }
else if ((protocol == AF_INET6 || protocol == AF_UNSPEC) && rp->ai_family == AF_INET6) {
579 memcpy(u.sa_in6, rp->ai_addr, rp->ai_addrlen);
580 u.ss->ss_family = protocol;
585 }
catch (AddressException &e) {
586 RETHROW(DNSException, e);