00001
00002
00003
00004
00005
00006
00007
00008
00009 static const char rcsid[] __attribute__((used)) = "$Id: DNS.cpp 1197 2014-10-14 22:26:11Z sella $";
00010
00011 #include "DNS.h"
00012 #include "DNSException.h"
00013 #include "../util/ThreadPool.h"
00014
00015 #include <string.h>
00016 #include <fcntl.h>
00017 #include <net/if.h>
00018 #include <arpa/inet.h>
00019 #include <sys/ioctl.h>
00020 #include <sys/types.h>
00021 #include <sys/socket.h>
00022 #include <netinet/in.h>
00023 #include <netdb.h>
00024
00025 union __sockaddr_union {
00026 struct sockaddr_storage *ss;
00027 struct sockaddr *sa;
00028 struct sockaddr_in *sa_in;
00029 struct sockaddr_in6 *sa_in6;
00030 };
00031
00032 using namespace sella::net;
00033 using namespace sella::util;
00034
00035 const std::vector<std::string> DNS::empty;
00036 bool walk(std::vector<IPAddress> &ips, struct addrinfo *result, union __sockaddr_union &u, int protocol);
00037
00038 DNS::DNS(const std::vector<std::string> &nameservers, const std::vector<std::string> &searches) throw (DNSException) :
00039 pool(NULL),
00040 context(NULL),
00041 flags(0)
00042 {
00043 init(nameservers, searches);
00044 }
00045
00046 DNS::DNS(sella::util::ThreadPool *pool, const std::vector<std::string> &nameservers, const std::vector<std::string> &searches) throw (DNSException) :
00047 pool(pool),
00048 context(NULL),
00049 flags(0)
00050 {
00051 init(nameservers, searches);
00052
00053 Task::callback function = std::bind(&DNS::receive, this, 0, 0);
00054 task = Task::shared_ptr();
00055 task->setCallback(function);
00056 }
00057
00058 DNS::~DNS() throw () {
00059 if (pool && task) {
00060 pool->unschedule(task, true);
00061 }
00062
00063 ::dns_free(this->context);
00064 }
00065
00066 std::vector<IPAddress> DNS::resolve(const std::string &name, int protocol) throw (DNSException) {
00067 int s;
00068 union __sockaddr_union u;
00069 struct sockaddr_storage addr;
00070 struct addrinfo hints, *result;
00071 std::vector<IPAddress> ips;
00072
00073 memset(&hints, 0, sizeof(struct addrinfo));
00074 hints.ai_family = AF_UNSPEC;
00075 hints.ai_socktype = SOCK_DGRAM;
00076 hints.ai_flags = AI_PASSIVE;
00077 hints.ai_protocol = 0;
00078 hints.ai_canonname = NULL;
00079 hints.ai_addr = NULL;
00080 hints.ai_next = NULL;
00081
00082 if (protocol == IPv4Required) {
00083 hints.ai_family = AF_INET;
00084 } else if (protocol == IPv6Required) {
00085 hints.ai_family = AF_INET6;
00086 }
00087
00088 if ((s = getaddrinfo(name.c_str(), NULL, &hints, &result)) != 0) {
00089 THROW_CODE(DNSException, s, "getaddrinfo() failed: %s", gai_strerror(s));
00090 }
00091
00092 u.ss = (struct sockaddr_storage*) &addr;
00093 u.sa_in = (struct sockaddr_in*) &addr;
00094 memset(&addr, 0, sizeof(addr));
00095
00096 try {
00097 if (protocol == AnyProtocol) {
00098 walk(ips, result, u, AF_INET);
00099 } else if (protocol == IPv4Preferred) {
00100 if (!walk(ips, result, u, AF_INET)) {
00101 if (!walk(ips, result, u, AF_INET6)) {
00102 THROW_CODE(DNSException, EAI_NODATA, "Failed to resolve %s to an IPv4 or IPv6 address", name.c_str());
00103 }
00104 }
00105 } else if (protocol == IPv4Required) {
00106 if (!walk(ips, result, u, AF_INET)) {
00107 THROW_CODE(DNSException, EAI_NODATA, "Failed to resolve %s to an IPv4 address", name.c_str());
00108 }
00109 } else if (protocol == IPv6Preferred) {
00110 if (!walk(ips, result, u, AF_INET6)) {
00111 if (!walk(ips, result, u, AF_INET)) {
00112 THROW_CODE(DNSException, EAI_NODATA, "Failed to resolve %s to an IPv4 or IPv6 address", name.c_str());
00113 }
00114 }
00115 } else if (protocol == IPv6Required) {
00116 if (!walk(ips, result, u, AF_INET6)) {
00117 THROW_CODE(DNSException, EAI_NODATA, "Failed to resolve %s to an IPv6 address", name.c_str());
00118 }
00119 }
00120
00121 freeaddrinfo(result);
00122 } catch (DNSException &e) {
00123 freeaddrinfo(result);
00124
00125 throw e;
00126 }
00127
00128 return ips;
00129 }
00130
00131 DNSCallback* DNS::resolve(DNSCallback* callback, const std::string &name, int protocol) throw (DNSException) {
00132 if (callback == NULL) {
00133 THROW(DNSException, "invalid NULL callback supplied");
00134 }
00135
00136 callback->qname = name;
00137 callback->protocol = protocol;
00138 callback->flags = flags;
00139
00140 switch (protocol) {
00141 case IPv4Preferred:
00142 case IPv4Required:
00143 ::dns_submit_p(this->context, name.c_str(), DNS_C_IN, DNS_T_A, flags, ::dns_parse_a4, DNS::callback_a, callback);
00144 break;
00145
00146 case IPv6Preferred:
00147 case IPv6Required:
00148 ::dns_submit_p(this->context, name.c_str(), DNS_C_IN, DNS_T_AAAA, flags, ::dns_parse_a6, DNS::callback_aaaa, callback);
00149 break;
00150
00151 default:
00152 THROW(DNSException, "invalid protocol supplied (%d); must be IPv4Preferred, IPv4Required, IPv6Preferred, IPv6Required", protocol);
00153 }
00154
00155 if (pool) {
00156 pool->reschedule(task, 0);
00157 }
00158
00159 return callback;
00160 }
00161
00162 bool DNS::receive(time_t delay, time_t now) throw () {
00163 fd_set rfds;
00164 struct timeval tv = { (delay < 0) ? 0 : delay, 0 };
00165
00166 std::lock_guard<std::mutex> lg(mutex);
00167
00168 if (pool && getActive() > 0) {
00169 pool->reschedule(task, 0, interval);
00170 }
00171
00172 #if 0
00173 if (::dns_timeouts(this->context, -1, now) >= 0) {
00174 int s = getSocket();
00175
00176 FD_ZERO(&rfds);
00177 FD_SET(s, &rfds);
00178
00179 tv.tv_sec = delay;
00180 tv.tv_usec = 0;
00181
00182 if (select(s + 1, &rfds, NULL, NULL, &tv) > 0) {
00183 ::dns_ioevent(this->context, now);
00184
00185 return true;
00186 }
00187 }
00188 #else
00189 int s = getSocket();
00190
00191 FD_ZERO(&rfds);
00192 FD_SET(s, &rfds);
00193
00194 ::dns_timeouts(this->context, -1, now);
00195
00196 if (select(s + 1, &rfds, NULL, NULL, &tv) > 0) {
00197 ::dns_ioevent(this->context, now);
00198
00199 return true;
00200 }
00201 #endif
00202
00203 return false;
00204 }
00205
00206 void DNS::setInterval(uint64_t usec) throw () {
00207 this->interval = (usec > 0) ? usec : DefaultInterval;
00208 }
00209
00210 void DNS::setNoSearch(bool on) throw () {
00211 flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
00212
00213 if (on) {
00214 flags |= DNS_NOSRCH;
00215 } else {
00216 flags &= ~DNS_NOSRCH;
00217 }
00218
00219 dns_set_opt(context, DNS_OPT_FLAGS, flags);
00220 }
00221
00222 void DNS::setNoRecursion(bool on) throw () {
00223 flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
00224
00225 if (on) {
00226 flags |= DNS_NORD;
00227 } else {
00228 flags &= ~DNS_NORD;
00229 }
00230
00231 dns_set_opt(context, DNS_OPT_FLAGS, flags);
00232 }
00233
00234 void DNS::setAuthoritativeOnly(bool on) throw () {
00235 flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
00236
00237 if (on) {
00238 flags |= DNS_AAONLY;
00239 } else {
00240 flags &= ~DNS_AAONLY;
00241 }
00242
00243 dns_set_opt(context, DNS_OPT_FLAGS, flags);
00244 }
00245
00246 void DNS::setTimeout(int seconds) throw () {
00247 dns_set_opt(context, DNS_OPT_TIMEOUT, seconds);
00248 }
00249
00250 void DNS::setRetries(int retries) throw () {
00251 dns_set_opt(context, DNS_OPT_NTRIES, retries);
00252 }
00253
00254 void DNS::setDots(int ndots) throw () {
00255 dns_set_opt(context, DNS_OPT_NDOTS, ndots);
00256 }
00257
00258 void DNS::setUDPSize(int bytes) throw () {
00259 dns_set_opt(context, DNS_OPT_UDPSIZE, bytes);
00260 }
00261
00262 void DNS::setUDPPort(short port) throw () {
00263 dns_set_opt(context, DNS_OPT_PORT, (int) port);
00264 }
00265
00266 int DNS::getActive() throw () {
00267 return dns_active(context);
00268 }
00269
00270 int DNS::getSocket() throw () {
00271 return dns_sock(context);
00272 }
00273
00274 const DNS::shared DNS::shared_ptr() throw () {
00275 return std::make_shared<DNS>();
00276 }
00277
00278 void DNS::callback_a(struct dns_ctx * context, void * result, void * argument) {
00279 DNSCallback *callback = reinterpret_cast<DNSCallback *>(argument);
00280 struct dns_rr_a4 *response = reinterpret_cast<struct dns_rr_a4 *>(result);
00281
00282 if (!response || response->dnsa4_nrr == 0) {
00283 if (callback->protocol == IPv4Required) {
00284 callback->failure(callback->qname, static_cast<DNSCallback::Error>(::dns_status(context)));
00285 } else {
00286 callback->protocol = IPv6Required;
00287
00288 ::dns_submit_p(context, response->dnsa4_qname, DNS_C_IN, DNS_T_AAAA, callback->flags, ::dns_parse_a6, DNS::callback_a, callback);
00289 }
00290 } else {
00291 std::vector<IPAddress> resolved;
00292
00293 for (int i = 0; i < response->dnsa4_nrr; i++) {
00294 try {
00295 resolved.push_back(IPAddress(&response->dnsa4_addr[i]));
00296 } catch (AddressException &e) { }
00297 }
00298
00299 if (strcmp(response->dnsa4_qname, response->dnsa4_cname) != 0) {
00300 callback->success(response->dnsa4_qname, response->dnsa4_cname, response->dnsa4_ttl, resolved);
00301 } else {
00302 callback->success(response->dnsa4_qname, std::string(), response->dnsa4_ttl, resolved);
00303 }
00304 }
00305 }
00306
00307 void DNS::callback_aaaa(struct dns_ctx * context, void * result, void * argument) {
00308 DNSCallback *callback = reinterpret_cast<DNSCallback *>(argument);
00309 struct dns_rr_a6 *response = reinterpret_cast<struct dns_rr_a6 *>(result);
00310
00311 if (!response || response->dnsa6_nrr == 0) {
00312 if (callback->protocol == IPv6Required) {
00313 callback->failure(callback->qname, static_cast<DNSCallback::Error>(::dns_status(context)));
00314 } else {
00315 callback->protocol = IPv4Required;
00316
00317 ::dns_submit_p(context, response->dnsa6_qname, DNS_C_IN, DNS_T_A, callback->flags, ::dns_parse_a4, DNS::callback_a, callback);
00318 }
00319 } else {
00320 std::vector<IPAddress> resolved;
00321
00322 for (int i = 0; i < response->dnsa6_nrr; i++) {
00323 try {
00324 resolved.push_back(IPAddress(&response->dnsa6_addr[i]));
00325 } catch (AddressException &e) { }
00326 }
00327
00328 if (strcmp(response->dnsa6_qname, response->dnsa6_cname) != 0) {
00329 callback->success(response->dnsa6_qname, response->dnsa6_cname, response->dnsa6_ttl, resolved);
00330 } else {
00331 callback->success(response->dnsa6_qname, std::string(), response->dnsa6_ttl, resolved);
00332 }
00333 }
00334 }
00335
00336 void DNS::init(const std::vector<std::string> &nameservers, const std::vector<std::string> &searches) {
00337 if (dns_init(NULL, 0) < 0) {
00338 THROW(DNSException, "failed to initialize default DNS context");
00339 }
00340
00341 if (!(this->context = dns_new(NULL))) {
00342 THROW(DNSException, "failed to allocate DNS context");
00343 }
00344
00345 if (::dns_init(this->context, 0) < 0) {
00346 THROW(DNSException, "failed to initialize DNS context");
00347 }
00348
00349 if (!nameservers.empty()) {
00350 ::dns_add_serv(context, NULL);
00351
00352 for (auto n = nameservers.begin(); n != nameservers.end(); ++n) {
00353 if (::dns_add_serv(context, n->c_str()) < 0) {
00354 THROW2(DNSException, "failed to set nameserver '%s'", n->c_str());
00355 }
00356 }
00357 }
00358
00359 if (!searches.empty()) {
00360 ::dns_add_srch(context, NULL);
00361
00362 for (auto s = searches.begin(); s != searches.end(); ++s) {
00363 if (::dns_add_srch(context, s->c_str()) < 0) {
00364 THROW2(DNSException, "failed to set search '%s'", s->c_str());
00365 }
00366 }
00367 }
00368
00369 this->flags = dns_set_opt(context, DNS_OPT_FLAGS, -1);
00370
00371 if (::dns_open(this->context) < 1) {
00372 THROW(DNSException, "failed to open DNS socket");
00373 }
00374 }
00375
00376 bool walk(std::vector<IPAddress> &ips, struct addrinfo *result, union __sockaddr_union &u, int protocol) {
00377 bool match = false;
00378 struct addrinfo *rp;
00379
00380 try {
00381 for (rp = result; rp != NULL; rp = rp->ai_next) {
00382 if ((protocol == AF_INET || protocol == AF_UNSPEC) && rp->ai_family == AF_INET) {
00383 match = true;
00384 memcpy(u.sa_in, rp->ai_addr, rp->ai_addrlen);
00385 u.ss->ss_family = protocol;
00386
00387 ips.push_back(IPAddress(u.ss));
00388 } else if ((protocol == AF_INET6 || protocol == AF_UNSPEC) && rp->ai_family == AF_INET6) {
00389 match = true;
00390 memcpy(u.sa_in6, rp->ai_addr, rp->ai_addrlen);
00391 u.ss->ss_family = protocol;
00392
00393 ips.push_back(IPAddress(u.ss));
00394 }
00395 }
00396 } catch (AddressException &e) {
00397 RETHROW(DNSException, e);
00398 }
00399
00400 return match;
00401 }
00402
00403
00404
00405