• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

DNS.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #include "P_DNS.h" /* MAGIC_EDITING_TAG */
00025 #include <ts/ink_inet.h>
00026 
00027 #ifdef SPLIT_DNS
00028 #include "I_SplitDNS.h"
00029 #endif
00030 
00031 #define SRV_COST    (RRFIXEDSZ+0)
00032 #define SRV_WEIGHT  (RRFIXEDSZ+2)
00033 #define SRV_PORT    (RRFIXEDSZ+4)
00034 #define SRV_SERVER  (RRFIXEDSZ+6)
00035 #define SRV_FIXEDSZ (RRFIXEDSZ+6)
00036 
00037 EventType ET_DNS = ET_CALL;
00038 
00039 //
00040 // Config
00041 //
00042 int dns_timeout = DEFAULT_DNS_TIMEOUT;
00043 int dns_retries = DEFAULT_DNS_RETRIES;
00044 int dns_search = DEFAULT_DNS_SEARCH;
00045 int dns_failover_number = DEFAULT_FAILOVER_NUMBER;
00046 int dns_failover_period = DEFAULT_FAILOVER_PERIOD;
00047 int dns_failover_try_period = DEFAULT_FAILOVER_TRY_PERIOD;
00048 int dns_max_dns_in_flight = MAX_DNS_IN_FLIGHT;
00049 int dns_validate_qname = 0;
00050 unsigned int dns_handler_initialized = 0;
00051 int dns_ns_rr = 0;
00052 int dns_ns_rr_init_down = 1;
00053 char *dns_ns_list = NULL;
00054 char *dns_resolv_conf = NULL;
00055 char *dns_local_ipv6 = NULL;
00056 char *dns_local_ipv4 = NULL;
00057 int dns_thread = 0;
00058 int dns_prefer_ipv6 = 0;
00059 namespace {
00060   // Currently only used for A and AAAA.
00061   inline char const* QtypeName(int qtype) {
00062     return T_AAAA == qtype ? "AAAA" : T_A == qtype ? "A" : "*";
00063   }
00064   inline bool is_addr_query(int qtype) {
00065     return qtype == T_A || qtype == T_AAAA;
00066   }
00067 }
00068 
00069 DNSProcessor dnsProcessor;
00070 ClassAllocator<DNSEntry> dnsEntryAllocator("dnsEntryAllocator");
00071 // Users are expected to free these entries in short order!
00072 // We could page align this buffer to enable page flipping for recv...
00073 ClassAllocator<HostEnt> dnsBufAllocator("dnsBufAllocator", 2);
00074 
00075 
00076 //
00077 // Function Prototypes
00078 //
00079 static bool dns_process(DNSHandler *h, HostEnt *ent, int len);
00080 static DNSEntry *get_dns(DNSHandler *h, uint16_t id);
00081 // returns true when e is done
00082 static void dns_result(DNSHandler *h, DNSEntry *e, HostEnt *ent, bool retry);
00083 static void write_dns(DNSHandler *h);
00084 static bool write_dns_event(DNSHandler *h, DNSEntry *e);
00085 
00086 // "reliable" name to try. need to build up first.
00087 static int try_servers = 0;
00088 static int local_num_entries = 1;
00089 static int attempt_num_entries = 1;
00090 char try_server_names[DEFAULT_NUM_TRY_SERVER][MAXDNAME];
00091 
00092 static inline char *
00093 strnchr(char *s, char c, int len) {
00094   while (*s && *s != c && len)
00095     ++s, --len;
00096   return *s == c ? s : (char *) NULL;
00097 }
00098 
00099 static inline uint16_t
00100 ink_get16(const uint8_t *src) {
00101   uint16_t dst;
00102 
00103   NS_GET16(dst, src);
00104   return dst;
00105 }
00106 
00107 void HostEnt::free() {
00108   dnsBufAllocator.free(this);
00109 }
00110 
00111 void
00112 make_ipv4_ptr(in_addr_t addr, char *buffer)
00113 {
00114     char *p = buffer;
00115     uint8_t const* u = reinterpret_cast<uint8_t*>(&addr);
00116 
00117     if (u[3] > 99)
00118       *p++ = (u[3] / 100) + '0';
00119     if (u[3] > 9)
00120       *p++ = ((u[3] / 10) % 10) + '0';
00121     *p++ = u[3] % 10 + '0';
00122     *p++ = '.';
00123     if (u[2] > 99)
00124       *p++ = (u[2] / 100) + '0';
00125     if (u[2] > 9)
00126       *p++ = ((u[2] / 10) % 10) + '0';
00127     *p++ = u[2] % 10 + '0';
00128     *p++ = '.';
00129     if (u[1] > 99)
00130       *p++ = (u[1] / 100) + '0';
00131     if (u[1] > 9)
00132       *p++ = ((u[1] / 10) % 10) + '0';
00133     *p++ = u[1] % 10 + '0';
00134     *p++ = '.';
00135     if (u[0] > 99)
00136       *p++ = (u[0] / 100) + '0';
00137     if (u[0] > 9)
00138       *p++ = ((u[0] / 10) % 10) + '0';
00139     *p++ = u[0] % 10 + '0';
00140     *p++ = '.';
00141     ink_strlcpy(p, "in-addr.arpa", MAXDNAME - (p - buffer + 1));
00142 }
00143 
00144 void
00145 make_ipv6_ptr(in6_addr const* addr, char *buffer)
00146 {
00147     const char hex_digit[] = "0123456789abcdef";
00148     char *p = buffer;
00149     uint8_t const* src = addr->s6_addr;
00150     int i;
00151 
00152     for (i = TS_IP6_SIZE-1 ; i >= 0; --i) {
00153         *p++ = hex_digit[src[i] & 0x0f];
00154         *p++ = '.';
00155         *p++ = hex_digit[src[i] >> 4];
00156         *p++ = '.';
00157     }
00158 
00159     ink_strlcpy(p, "ip6.arpa", MAXDNAME - (p - buffer + 1));
00160 }
00161 
00162 //  Public functions
00163 //
00164 //  See documentation is header files and Memos
00165 //
00166 int
00167 DNSProcessor::start(int, size_t stacksize) {
00168   //
00169   // Read configuration
00170   //
00171   REC_EstablishStaticConfigInt32(dns_retries, "proxy.config.dns.retries");
00172   REC_EstablishStaticConfigInt32(dns_timeout, "proxy.config.dns.lookup_timeout");
00173   REC_EstablishStaticConfigInt32(dns_search, "proxy.config.dns.search_default_domains");
00174   REC_EstablishStaticConfigInt32(dns_failover_number, "proxy.config.dns.failover_number");
00175   REC_EstablishStaticConfigInt32(dns_failover_period, "proxy.config.dns.failover_period");
00176   REC_EstablishStaticConfigInt32(dns_max_dns_in_flight, "proxy.config.dns.max_dns_in_flight");
00177   REC_EstablishStaticConfigInt32(dns_validate_qname, "proxy.config.dns.validate_query_name");
00178   REC_EstablishStaticConfigInt32(dns_ns_rr, "proxy.config.dns.round_robin_nameservers");
00179   REC_ReadConfigStringAlloc(dns_ns_list, "proxy.config.dns.nameservers");
00180   REC_ReadConfigStringAlloc(dns_local_ipv4, "proxy.config.dns.local_ipv4");
00181   REC_ReadConfigStringAlloc(dns_local_ipv6, "proxy.config.dns.local_ipv6");
00182   REC_ReadConfigStringAlloc(dns_resolv_conf, "proxy.config.dns.resolv_conf");
00183   REC_EstablishStaticConfigInt32(dns_thread, "proxy.config.dns.dedicated_thread");
00184 
00185   if (dns_thread > 0) {
00186     // TODO: Hmmm, should we just get a single thread some other way?
00187     ET_DNS = eventProcessor.spawn_event_threads(1, "ET_DNS", stacksize);
00188     initialize_thread_for_net(eventProcessor.eventthread[ET_DNS][0]);
00189   } else {
00190     // Initialize the first event thread for DNS.
00191     ET_DNS = ET_CALL;
00192   }
00193   thread = eventProcessor.eventthread[ET_DNS][0];
00194 
00195   dns_failover_try_period = dns_timeout + 1;    // Modify the "default" accordingly
00196 
00197   if (SplitDNSConfig::gsplit_dns_enabled) {
00198     //reconfigure after threads start
00199     SplitDNSConfig::reconfigure();
00200   }
00201 
00202   // Setup the default DNSHandler, it's used both by normal DNS, and SplitDNS (for PTR lookups etc.)
00203   dns_init();
00204   open();
00205 
00206   return 0;
00207 }
00208 
00209 void
00210 DNSProcessor::open(sockaddr const* target, int aoptions)
00211 {
00212   DNSHandler *h = new DNSHandler;
00213 
00214   h->options = aoptions;
00215   h->mutex = thread->mutex;
00216   h->m_res = &l_res;
00217   ats_ip_copy(&h->local_ipv4.sa, &local_ipv4.sa);
00218   ats_ip_copy(&h->local_ipv6.sa, &local_ipv6.sa);
00219 
00220   if (target)
00221     ats_ip_copy(&h->ip, target);
00222   else
00223     ats_ip_invalidate(&h->ip); // marked to use default.
00224 
00225   if (!dns_handler_initialized)
00226     handler = h;
00227 
00228   SET_CONTINUATION_HANDLER(h, &DNSHandler::startEvent);
00229   thread->schedule_imm(h);
00230 }
00231 
00232 //
00233 // Initialization
00234 //
00235 void
00236 DNSProcessor::dns_init()
00237 {
00238   gethostname(try_server_names[0], 255);
00239   Debug("dns", "localhost=%s\n", try_server_names[0]);
00240   Debug("dns", "Round-robin nameservers = %d\n", dns_ns_rr);
00241 
00242   IpEndpoint nameserver[MAX_NAMED];
00243   size_t nserv = 0;
00244 
00245   if (dns_ns_list) {
00246     Debug("dns", "Nameserver list specified \"%s\"\n", dns_ns_list);
00247     int i;
00248     char *last;
00249     char *ns_list = ats_strdup(dns_ns_list);
00250     char *ns = (char *) strtok_r(ns_list, " ,;\t\r", &last);
00251 
00252     for (i = 0, nserv = 0 ; (i < MAX_NAMED) && ns ; ++i) {
00253       Debug("dns", "Nameserver list - parsing \"%s\"\n", ns);
00254       bool err = false;
00255       int prt = DOMAIN_SERVICE_PORT;
00256       char* colon = 0; // where the port colon is.
00257       // Check for IPv6 notation.
00258       if ('[' == *ns) {
00259         char* ndx = strchr(ns+1, ']');
00260         if (ndx) {
00261           if (':' == ndx[1]) colon = ndx+1;
00262         } else {
00263           err = true;
00264           Warning("Unmatched '[' in address for nameserver '%s', discarding.", ns);
00265         }
00266       } else colon = strchr(ns, ':');
00267 
00268       if (!err && colon) {
00269         *colon = '\0';
00270         // coverity[secure_coding]
00271         if (sscanf(colon + 1, "%d%*s", &prt) != 1) {
00272           Debug("dns", "Unable to parse port number '%s' for nameserver '%s', discardin.", colon + 1, ns);
00273           Warning("Unable to parse port number '%s' for nameserver '%s', discarding.", colon + 1, ns);
00274           err = true;
00275         }
00276       }
00277 
00278       if (!err && 0 != ats_ip_pton(ns, &nameserver[nserv].sa)) {
00279         Debug("dns", "Invalid IP address given for nameserver '%s', discarding", ns);
00280         Warning("Invalid IP address given for nameserver '%s', discarding", ns);
00281         err = true;
00282       }
00283 
00284       if (!err) {
00285         ip_port_text_buffer buff;
00286 
00287         ats_ip_port_cast(&nameserver[nserv].sa) = htons(prt);
00288 
00289         Debug("dns", "Adding nameserver %s to nameserver list",
00290           ats_ip_nptop(&nameserver[nserv].sa, buff, sizeof(buff))
00291         );
00292         ++nserv;
00293       }
00294 
00295       ns = (char *) strtok_r(NULL, " ,;\t\r", &last);
00296     }
00297     ats_free(ns_list);
00298   }
00299   // The default domain (4th param) and search list (5th param) will
00300   // come from /etc/resolv.conf.
00301   if (ink_res_init(&l_res, nameserver, nserv, NULL, NULL, dns_resolv_conf) < 0)
00302     Warning("Failed to build DNS res records for the servers (%s).  Using resolv.conf.", dns_ns_list);
00303 
00304   // Check for local forced bindings.
00305 
00306   if (dns_local_ipv6) {
00307     if (0 != ats_ip_pton(dns_local_ipv6, &local_ipv6)) {
00308       ats_ip_invalidate(&local_ipv6);
00309       Warning("Invalid IP address '%s' for dns.local_ipv6 value, discarding.", dns_local_ipv6);
00310     } else if (!ats_is_ip6(&local_ipv6.sa)) {
00311       ats_ip_invalidate(&local_ipv6);
00312       Warning("IP address '%s' for dns.local_ipv6 value was not IPv6, discarding.", dns_local_ipv6);
00313     }
00314   }
00315 
00316   if (dns_local_ipv4) {
00317     if (0 != ats_ip_pton(dns_local_ipv4, &local_ipv4)) {
00318       ats_ip_invalidate(&local_ipv4);
00319       Warning("Invalid IP address '%s' for dns.local_ipv4 value, discarding.", dns_local_ipv4);
00320     } else if (!ats_is_ip4(&local_ipv4.sa)) {
00321       ats_ip_invalidate(&local_ipv4);
00322       Warning("IP address '%s' for dns.local_ipv4 value was not IPv4, discarding.", dns_local_ipv4);
00323     }
00324   }
00325 }
00326 
00327 /**
00328   Inter-OS portability for dn_expand.  dn_expand() expands the compressed
00329   domain name comp_dn to a full domain name. Expanded names are converted
00330   to upper case. msg is a pointer to the beginning of the message,
00331   exp_dn is a pointer to a buffer of size length for the result. The
00332   size of compressed name is returned or -1 if there was an error.
00333 
00334 */
00335 inline int
00336 ink_dn_expand(const u_char *msg, const u_char *eom, const u_char *comp_dn, u_char *exp_dn, int length)
00337 {
00338   return::dn_expand((unsigned char *) msg, (unsigned char *) eom, (unsigned char *) comp_dn, (char *) exp_dn, length);
00339 }
00340 
00341 DNSProcessor::DNSProcessor()
00342   : thread(NULL), handler(NULL)
00343 {
00344   ink_zero(l_res);
00345   ink_zero(local_ipv6);
00346   ink_zero(local_ipv4);
00347 }
00348 
00349 void
00350 DNSEntry::init(const char *x, int len, int qtype_arg, Continuation* acont,
00351                DNSProcessor::Options const& opt)
00352 {
00353   qtype = qtype_arg;
00354   host_res_style = opt.host_res_style;
00355   if (is_addr_query(qtype)) {
00356       // adjust things based on family preference.
00357       if (HOST_RES_IPV4 == host_res_style ||
00358           HOST_RES_IPV4_ONLY == host_res_style) {
00359           qtype = T_A;
00360       } else if (HOST_RES_IPV6 == host_res_style ||
00361                  HOST_RES_IPV6_ONLY == host_res_style) {
00362           qtype = T_AAAA;
00363       }
00364   }
00365   submit_time = ink_get_hrtime();
00366   action = acont;
00367   submit_thread = acont->mutex->thread_holding;
00368 
00369 #ifdef SPLIT_DNS
00370   if (SplitDNSConfig::gsplit_dns_enabled) {
00371     dnsH = opt.handler ? opt.handler : dnsProcessor.handler;
00372   } else {
00373     dnsH = dnsProcessor.handler;
00374   }
00375 #else
00376   dnsH = dnsProcessor.handler;
00377 #endif // SPLIT_DNS
00378 
00379   dnsH->txn_lookup_timeout = opt.timeout;
00380 
00381   mutex = dnsH->mutex;
00382 
00383   if (is_addr_query(qtype) || qtype == T_SRV) {
00384     if (len) {
00385       len = len > (MAXDNAME - 1) ? (MAXDNAME - 1) : len;
00386       memcpy(qname, x, len);
00387       qname[len] = 0;
00388       orig_qname_len = qname_len = len;
00389     } else {
00390       qname_len = ink_strlcpy(qname, x, MAXDNAME);
00391       orig_qname_len = qname_len;
00392     }
00393   } else {                    //T_PTR
00394     IpAddr const* ip = reinterpret_cast<IpAddr const*>(x);
00395     if (ip->isIp6())
00396       make_ipv6_ptr(&ip->_addr._ip6, qname);
00397     else if (ip->isIp4())
00398       make_ipv4_ptr(ip->_addr._ip4, qname);
00399     else
00400       ink_assert(!"T_PTR query to DNS must be IP address.");
00401   }
00402 
00403   SET_HANDLER((DNSEntryHandler) & DNSEntry::mainEvent);
00404 }
00405 
00406 /**
00407   Open (and close) connections as necessary and also assures that the
00408   epoll fd struct is properly updated.
00409 
00410 */
00411 void
00412 DNSHandler::open_con(sockaddr const* target, bool failed, int icon)
00413 {
00414   ip_port_text_buffer ip_text;
00415   PollDescriptor *pd = get_PollDescriptor(dnsProcessor.thread);
00416 
00417   if (!icon && target) {
00418     ats_ip_copy(&ip, target);
00419   } else if (!target) {
00420     target = &ip.sa;
00421   }
00422 
00423   Debug("dns", "open_con: opening connection %s", ats_ip_nptop(target, ip_text, sizeof ip_text));
00424 
00425   if (con[icon].fd != NO_FD) {  // Remove old FD from epoll fd
00426     con[icon].eio.stop();
00427     con[icon].close();
00428   }
00429 
00430   if (con[icon].connect(
00431       target, DNSConnection::Options()
00432         .setNonBlockingConnect(true)
00433         .setNonBlockingIo(true)
00434         .setUseTcp(false)
00435         .setBindRandomPort(true)
00436         .setLocalIpv6(&local_ipv6.sa)
00437         .setLocalIpv4(&local_ipv4.sa)
00438     ) < 0) {
00439     Debug("dns", "opening connection %s FAILED for %d", ip_text, icon);
00440     if (!failed) {
00441       if (dns_ns_rr)
00442         rr_failure(icon);
00443       else
00444         failover();
00445     }
00446     return;
00447   } else {
00448     ns_down[icon] = 0;
00449     if (con[icon].eio.start(pd, &con[icon], EVENTIO_READ) < 0) {
00450       Error("[iocore_dns] open_con: Failed to add %d server to epoll list\n", icon);
00451     } else {
00452       con[icon].num = icon;
00453       Debug("dns", "opening connection %s SUCCEEDED for %d", ip_text, icon);
00454     }
00455   }
00456 }
00457 
00458 void
00459 DNSHandler::validate_ip() {
00460   if (!ip.isValid()) {
00461     // Invalid, switch to default.
00462     // seems that res_init always sets m_res.nscount to at least 1!
00463     if (!m_res->nscount || !ats_ip_copy(&ip.sa, &m_res->nsaddr_list[0].sa)) {
00464       Warning("bad nameserver config, fallback to loopback");
00465       ip.setToLoopback(AF_INET);
00466     }
00467   }
00468 }
00469 /**
00470   Initial state of the DNSHandler. Can reinitialize the running DNS
00471   handler to a new nameserver.
00472 
00473 */
00474 int
00475 DNSHandler::startEvent(int /* event ATS_UNUSED */, Event *e)
00476 {
00477   //
00478   // If this is for the default server, get it
00479   Debug("dns", "DNSHandler::startEvent: on thread %d\n", e->ethread->id);
00480 
00481   this->validate_ip();
00482 
00483   if (!dns_handler_initialized) {
00484     //
00485     // If we are THE handler, open connection and configure for
00486     // periodic execution.
00487     //
00488     dns_handler_initialized = 1;
00489     SET_HANDLER(&DNSHandler::mainEvent);
00490     if (dns_ns_rr) {
00491       int max_nscount = m_res->nscount;
00492       if (max_nscount > MAX_NAMED)
00493         max_nscount = MAX_NAMED;
00494       n_con = 0;
00495       for (int i = 0; i < max_nscount; i++) {
00496         ip_port_text_buffer buff;
00497         sockaddr *sa = &m_res->nsaddr_list[i].sa;
00498         if (ats_is_ip(sa)) {
00499           open_con(sa, false, n_con);
00500           ++n_con;
00501           Debug("dns_pas", "opened connection to %s, n_con = %d",
00502             ats_ip_nptop(sa, buff, sizeof(buff)),
00503             n_con
00504           );
00505         }
00506       }
00507       dns_ns_rr_init_down = 0;
00508     } else {
00509       open_con(0); // use current target address.
00510       n_con = 1;
00511     }
00512     e->ethread->schedule_every(this, DNS_PERIOD);
00513 
00514     return EVENT_CONT;
00515   } else {
00516     ink_assert(false);          // I.e. this should never really happen
00517     return EVENT_DONE;
00518   }
00519 }
00520 
00521 /**
00522   Initial state of the DSNHandler. Can reinitialize the running DNS
00523   hander to a new nameserver.
00524 */
00525 int
00526 DNSHandler::startEvent_sdns(int /* event ATS_UNUSED */, Event *e)
00527 {
00528   Debug("dns", "DNSHandler::startEvent_sdns: on thread %d\n", e->ethread->id);
00529   this->validate_ip();
00530 
00531   SET_HANDLER(&DNSHandler::mainEvent);
00532   open_con(&ip.sa, false, n_con);
00533   ++n_con;                      // TODO should n_con be zeroed?
00534 
00535   e->schedule_every(DNS_PERIOD);
00536   return EVENT_CONT;
00537 }
00538 
00539 static inline int
00540 _ink_res_mkquery(ink_res_state res, char *qname, int qtype, char *buffer)
00541 {
00542   int r = ink_res_mkquery(res, QUERY, qname, C_IN, qtype,
00543                           NULL, 0, NULL, (unsigned char *) buffer,
00544                           MAX_DNS_PACKET_LEN);
00545   return r;
00546 }
00547 
00548 void
00549 DNSHandler::recover()
00550 {
00551   ip_text_buffer buff;
00552   Warning("connection to DNS server %s restored", ats_ip_ntop(&ip.sa, buff, sizeof(buff)));
00553   name_server = 0;
00554   switch_named(name_server);
00555 }
00556 
00557 void
00558 DNSHandler::retry_named(int ndx, ink_hrtime t, bool reopen)
00559 {
00560   if (reopen && ((t - last_primary_reopen) > DNS_PRIMARY_REOPEN_PERIOD)) {
00561     Debug("dns", "retry_named: reopening DNS connection for index %d", ndx);
00562     last_primary_reopen = t;
00563     con[ndx].close();
00564     open_con(&m_res->nsaddr_list[ndx].sa, true, ndx);
00565   }
00566 
00567   char buffer[MAX_DNS_PACKET_LEN];
00568   Debug("dns", "trying to resolve '%s' from DNS connection, ndx %d", try_server_names[try_servers], ndx);
00569   int r = _ink_res_mkquery(m_res, try_server_names[try_servers], T_A, buffer);
00570   try_servers = (try_servers + 1) % countof(try_server_names);
00571   ink_assert(r >= 0);
00572   if (r >= 0) {                 // looking for a bounce
00573     int res = socketManager.send(con[ndx].fd, buffer, r, 0);
00574     Debug("dns", "ping result = %d", res);
00575   }
00576 }
00577 
00578 void
00579 DNSHandler::try_primary_named(bool reopen)
00580 {
00581   ink_hrtime t = ink_get_hrtime();
00582   if (reopen && ((t - last_primary_reopen) > DNS_PRIMARY_REOPEN_PERIOD)) {
00583     Debug("dns", "try_primary_named: reopening primary DNS connection");
00584     last_primary_reopen = t;
00585     open_con(&ip.sa, true, 0);
00586   }
00587   if ((t - last_primary_retry) > DNS_PRIMARY_RETRY_PERIOD) {
00588     char buffer[MAX_DNS_PACKET_LEN];
00589 
00590     last_primary_retry = t;
00591     Debug("dns", "trying to resolve '%s' from primary DNS connection", try_server_names[try_servers]);
00592     int r = _ink_res_mkquery(m_res, try_server_names[try_servers], T_A, buffer);
00593     // if try_server_names[] is not full, round-robin within the
00594     // filled entries.
00595     if (local_num_entries < DEFAULT_NUM_TRY_SERVER)
00596       try_servers = (try_servers + 1) % local_num_entries;
00597     else
00598       try_servers = (try_servers + 1) % countof(try_server_names);
00599     ink_assert(r >= 0);
00600     if (r >= 0) {               // looking for a bounce
00601       int res = socketManager.send(con[0].fd, buffer, r, 0);
00602       Debug("dns", "ping result = %d", res);
00603     }
00604   }
00605 }
00606 
00607 
00608 void
00609 DNSHandler::switch_named(int ndx)
00610 {
00611   for (DNSEntry *e = entries.head; e; e = (DNSEntry *) e->link.next) {
00612     e->written_flag = 0;
00613     if (e->retries < dns_retries)
00614       ++(e->retries);           // give them another chance
00615   }
00616   in_flight = 0;
00617   received_one(ndx);            // reset failover counters
00618 }
00619 
00620 /** Fail over to another name server. */
00621 void
00622 DNSHandler::failover()
00623 {
00624   Debug("dns", "failover: initiating failover attempt, current name_server=%d", name_server);
00625   // no hope, if we have only one server
00626   if (m_res->nscount > 1) {
00627     ip_text_buffer buff1, buff2;
00628     int max_nscount = m_res->nscount;
00629 
00630     if (max_nscount > MAX_NAMED)
00631       max_nscount = MAX_NAMED;
00632     sockaddr const* old_addr = &m_res->nsaddr_list[name_server].sa;
00633     name_server = (name_server + 1) % max_nscount;
00634     Debug("dns", "failover: failing over to name_server=%d", name_server);
00635 
00636     IpEndpoint target;
00637     ats_ip_copy(&target.sa, &m_res->nsaddr_list[name_server].sa);
00638 
00639     Warning("failover: connection to DNS server %s lost, move to %s",
00640       ats_ip_ntop(old_addr, buff1, sizeof(buff1)),
00641       ats_ip_ntop(&target.sa, buff2, sizeof(buff2))
00642     );
00643 
00644     if (!target.isValid()) target.setToLoopback(AF_INET);
00645 
00646     open_con(&target.sa, true, name_server);
00647     if (n_con <= name_server)
00648       n_con = name_server + 1;
00649     switch_named(name_server);
00650   } else {
00651     ip_text_buffer buff;
00652     Warning("failover: connection to DNS server %s lost, retrying",
00653       ats_ip_ntop(&ip.sa, buff, sizeof(buff))
00654     );
00655   }
00656 }
00657 
00658 /** Mark one of the nameservers as down. */
00659 void
00660 DNSHandler::rr_failure(int ndx)
00661 {
00662   // no hope, if we have only one server
00663   if (!ns_down[ndx]) {
00664     ip_text_buffer buff;
00665     // mark this nameserver as down
00666     Debug("dns", "rr_failure: Marking nameserver %d as down", ndx);
00667     ns_down[ndx] = 1;
00668     Warning("connection to DNS server %s lost, marking as down",
00669       ats_ip_ntop(&m_res->nsaddr_list[ndx].sa, buff, sizeof(buff))
00670     );
00671   }
00672 
00673   int nscount = m_res->nscount;
00674   if (nscount > MAX_NAMED)
00675     nscount = MAX_NAMED;
00676 
00677   // See if all nameservers are down
00678   int all_down = 1;
00679 
00680   for (int i = 0; i < nscount && all_down; i++) {
00681     Debug("dns", "nsdown[%d]=%d", i, ns_down[i]);
00682     if (!ns_down[i]) {
00683       all_down = 0;
00684     }
00685   }
00686 
00687   if (all_down && !dns_ns_rr_init_down) {
00688     Warning("connection to all DNS servers lost, retrying");
00689     // actual retries will be done in retry_named called from mainEvent
00690     // mark any outstanding requests as not sent for later retry
00691     for (DNSEntry *e = entries.head; e; e = (DNSEntry *) e->link.next) {
00692       e->written_flag = 0;
00693       if (e->retries < dns_retries)
00694         ++(e->retries);         // give them another chance
00695       --in_flight;
00696       DNS_DECREMENT_DYN_STAT(dns_in_flight_stat);
00697     }
00698   } else {
00699     // move outstanding requests that were sent to this nameserver to another
00700     for (DNSEntry *e = entries.head; e; e = (DNSEntry *) e->link.next) {
00701       if (e->which_ns == ndx) {
00702         e->written_flag = 0;
00703         if (e->retries < dns_retries)
00704           ++(e->retries);       // give them another chance
00705         --in_flight;
00706         DNS_DECREMENT_DYN_STAT(dns_in_flight_stat);
00707       }
00708     }
00709   }
00710 }
00711 
00712 static inline unsigned int get_rcode(char* buff) {
00713   return reinterpret_cast<HEADER*>(buff)->rcode;
00714 }
00715 
00716 static inline unsigned int get_rcode(HostEnt* ent) {
00717   return get_rcode(reinterpret_cast<char*>(ent));
00718 }
00719 
00720 static bool
00721 good_rcode(char *buff) {
00722   unsigned int r = get_rcode(buff);
00723   return NOERROR == r || NXDOMAIN == r;
00724 }
00725 
00726 
00727 void
00728 DNSHandler::recv_dns(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
00729 {
00730   DNSConnection *dnsc = NULL;
00731   ip_text_buffer ipbuff1, ipbuff2;
00732 
00733   while ((dnsc = (DNSConnection *) triggered.dequeue())) {
00734     while (1) {
00735       IpEndpoint from_ip;
00736       socklen_t from_length = sizeof(from_ip);
00737 
00738       if (!hostent_cache)
00739         hostent_cache = dnsBufAllocator.alloc();
00740       HostEnt *buf = hostent_cache;
00741 
00742       int res = socketManager.recvfrom(dnsc->fd, buf->buf, MAX_DNS_PACKET_LEN, 0, &from_ip.sa, &from_length);
00743 
00744       if (res == -EAGAIN)
00745         break;
00746       if (res <= 0) {
00747         Debug("dns", "named error: %d", res);
00748         if (dns_ns_rr)
00749           rr_failure(dnsc->num);
00750         else if (dnsc->num == name_server)
00751           failover();
00752         break;
00753       }
00754 
00755       // verify that this response came from the correct server
00756       if (!ats_ip_addr_eq(&dnsc->ip.sa, &from_ip.sa)) {
00757         Warning("unexpected DNS response from %s (expected %s)",
00758           ats_ip_ntop(&from_ip.sa, ipbuff1, sizeof ipbuff1),
00759           ats_ip_ntop(&dnsc->ip.sa, ipbuff2, sizeof ipbuff2)
00760         );
00761         continue;
00762       }
00763       hostent_cache = 0;
00764       buf->packet_size = res;
00765       Debug("dns", "received packet size = %d", res);
00766       if (dns_ns_rr) {
00767         Debug("dns", "round-robin: nameserver %d DNS response code = %d", dnsc->num, get_rcode(buf));
00768         if (good_rcode(buf->buf)) {
00769           received_one(dnsc->num);
00770           if (ns_down[dnsc->num]) {
00771             Warning("connection to DNS server %s restored",
00772               ats_ip_ntop(&m_res->nsaddr_list[dnsc->num].sa, ipbuff1, sizeof ipbuff1)
00773             );
00774             ns_down[dnsc->num] = 0;
00775           }
00776         }
00777       } else {
00778         if (!dnsc->num) {
00779           Debug("dns", "primary DNS response code = %d", get_rcode(buf));
00780           if (good_rcode(buf->buf)) {
00781             if (name_server)
00782               recover();
00783             else
00784               received_one(name_server);
00785           }
00786         }
00787       }
00788       Ptr<HostEnt> protect_hostent = make_ptr(buf);
00789       if (dns_process(this, buf, res)) {
00790         if (dnsc->num == name_server)
00791           received_one(name_server);
00792       }
00793     }
00794   }
00795 }
00796 
00797 /** Main event for the DNSHandler. Attempt to read from and write to named. */
00798 int
00799 DNSHandler::mainEvent(int event, Event *e)
00800 {
00801   recv_dns(event, e);
00802   if (dns_ns_rr) {
00803     ink_hrtime t = ink_get_hrtime();
00804     if (t - last_primary_retry > DNS_PRIMARY_RETRY_PERIOD) {
00805       for (int i = 0; i < n_con; i++) {
00806         if (ns_down[i]) {
00807           Debug("dns", "mainEvent: nameserver = %d is down", i);
00808           retry_named(i, t, true);
00809         }
00810       }
00811       last_primary_retry = t;
00812     }
00813     for (int i = 0; i < n_con; i++) {
00814       if (!ns_down[i] && failover_soon(i)) {
00815         Debug("dns", "mainEvent: nameserver = %d failover soon", name_server);
00816         if (failover_now(i))
00817           rr_failure(i);
00818         else {
00819           Debug("dns", "mainEvent: nameserver = %d no failover now - retrying", i);
00820           retry_named(i, t, false);
00821           ++failover_soon_number[i];
00822         }
00823       }
00824     }
00825   } else {
00826     if (failover_soon(name_server)) {
00827       Debug("dns", "mainEvent: will failover soon");
00828       if (failover_now(name_server)) {
00829         Debug("dns", "mainEvent: failing over now to another nameserver");
00830         failover();
00831       } else {
00832         try_primary_named(false);
00833         ++failover_soon_number[name_server];
00834       }
00835     } else if (name_server)     // not on the primary named
00836       try_primary_named(true);
00837   }
00838 
00839   if (entries.head)
00840     write_dns(this);
00841 
00842   return EVENT_CONT;
00843 }
00844 
00845 /** Find a DNSEntry by id. */
00846 inline static DNSEntry *
00847 get_dns(DNSHandler *h, uint16_t id)
00848 {
00849   for (DNSEntry *e = h->entries.head; e; e = (DNSEntry *) e->link.next) {
00850     if (e->once_written_flag) {
00851       for (int j = 0; j < MAX_DNS_RETRIES; j++) {
00852         if (e->id[j] == id) {
00853           return e;
00854         } else if (e->id[j] < 0) {
00855           goto Lnext;
00856         }
00857       }
00858     }
00859   Lnext:;
00860   }
00861   return NULL;
00862 }
00863 
00864 /** Find a DNSEntry by query name and type. */
00865 inline static DNSEntry *
00866 get_entry(DNSHandler *h, char *qname, int qtype)
00867 {
00868   for (DNSEntry *e = h->entries.head; e; e = (DNSEntry *) e->link.next) {
00869     if (e->qtype == qtype) {
00870       if (is_addr_query(qtype)) {
00871         if (!strcmp(qname, e->qname))
00872           return e;
00873       } else if (0 == memcmp(qname, e->qname, e->qname_len))
00874         return e;
00875     }
00876   }
00877   return NULL;
00878 }
00879 
00880 /** Write up to dns_max_dns_in_flight entries. */
00881 static void
00882 write_dns(DNSHandler *h)
00883 {
00884   ProxyMutex *mutex = h->mutex;
00885   DNS_INCREMENT_DYN_STAT(dns_total_lookups_stat);
00886   int max_nscount = h->m_res->nscount;
00887   if (max_nscount > MAX_NAMED)
00888     max_nscount = MAX_NAMED;
00889 
00890   if (h->in_write_dns)
00891     return;
00892   h->in_write_dns = true;
00893   // Debug("dns", "in_flight: %d, dns_max_dns_in_flight: %d", h->in_flight, dns_max_dns_in_flight);
00894   if (h->in_flight < dns_max_dns_in_flight) {
00895     DNSEntry *e = h->entries.head;
00896     while (e) {
00897       DNSEntry *n = (DNSEntry *) e->link.next;
00898       if (!e->written_flag) {
00899         if (dns_ns_rr) {
00900           int ns_start = h->name_server;
00901           do {
00902             h->name_server = (h->name_server + 1) % max_nscount;
00903           } while (h->ns_down[h->name_server] && h->name_server != ns_start);
00904         }
00905         if (!write_dns_event(h, e))
00906           break;
00907       }
00908       if (h->in_flight >= dns_max_dns_in_flight)
00909         break;
00910       e = n;
00911     }
00912   }
00913   h->in_write_dns = false;
00914 }
00915 
00916 uint16_t
00917 DNSHandler::get_query_id()
00918 {
00919   uint16_t q1, q2;
00920   q2 = q1 = (uint16_t)(generator.random() & 0xFFFF);
00921   if (query_id_in_use(q2)) {
00922     uint16_t i = q2>>6;
00923     while (qid_in_flight[i] == UINT64_MAX) {
00924       if (++i ==  sizeof(qid_in_flight)/sizeof(uint64_t)) {
00925         i = 0;
00926       }
00927       if (i == q1>>6) {
00928         Error("[iocore_dns] get_query_id: Exhausted all DNS query ids");
00929         return q1;
00930       }
00931     }
00932     i <<= 6;
00933     q2 &= 0x3F;
00934     while (query_id_in_use(i+q2)) {
00935       ++q2;
00936       q2 &= 0x3F;
00937       if (q2 == (q1 & 0x3F)) {
00938         Error("[iocore_dns] get_query_id: Exhausted all DNS query ids");
00939         return q1;
00940       }
00941     }
00942     q2 += i;
00943   }
00944 
00945   set_query_id_in_use(q2);
00946   return q2;
00947 }
00948 
00949 /**
00950   Construct and Write the request for a single entry (using send(3N)).
00951 
00952   @return true = keep going, false = give up for now.
00953 
00954 */
00955 static bool
00956 write_dns_event(DNSHandler *h, DNSEntry *e)
00957 {
00958   ProxyMutex *mutex = h->mutex;
00959   union {
00960     HEADER _h;
00961     char _b[MAX_DNS_PACKET_LEN];
00962   } blob;
00963   int r = 0;
00964 
00965   if ((r = _ink_res_mkquery(h->m_res, e->qname, e->qtype, blob._b)) <= 0) {
00966     Debug("dns", "cannot build query: %s", e->qname);
00967     dns_result(h, e, NULL, false);
00968     return true;
00969   }
00970 
00971   uint16_t i = h->get_query_id();
00972   blob._h.id = htons(i);
00973   if (e->id[dns_retries - e->retries] >= 0) {
00974     //clear previous id in case named was switched or domain was expanded
00975     h->release_query_id(e->id[dns_retries - e->retries]);
00976   }
00977   e->id[dns_retries - e->retries] = i;
00978   Debug("dns", "send query (qtype=%d) for %s to fd %d", e->qtype, e->qname, h->con[h->name_server].fd);
00979 
00980   int s = socketManager.send(h->con[h->name_server].fd, blob._b, r, 0);
00981   if (s != r) {
00982     Debug("dns", "send() failed: qname = %s, %d != %d, nameserver= %d", e->qname, s, r, h->name_server);
00983     // changed if condition from 'r < 0' to 's < 0' - 8/2001 pas
00984     if (s < 0) {
00985       if (dns_ns_rr)
00986         h->rr_failure(h->name_server);
00987       else
00988         h->failover();
00989     }
00990     return false;
00991   }
00992 
00993   e->written_flag = true;
00994   e->which_ns = h->name_server;
00995   e->once_written_flag = true;
00996   ++h->in_flight;
00997   DNS_INCREMENT_DYN_STAT(dns_in_flight_stat);
00998 
00999   e->send_time = ink_get_hrtime();
01000 
01001   if (e->timeout)
01002     e->timeout->cancel();
01003 
01004   if (h->txn_lookup_timeout) {
01005     e->timeout = h->mutex->thread_holding->schedule_in(e, HRTIME_MSECONDS(h->txn_lookup_timeout));      //this is in msec
01006   } else {
01007     e->timeout = h->mutex->thread_holding->schedule_in(e, HRTIME_SECONDS(dns_timeout));
01008   }
01009 
01010   Debug("dns", "sent qname = %s, id = %u, nameserver = %d", e->qname, e->id[dns_retries - e->retries], h->name_server);
01011   h->sent_one();
01012   return true;
01013 }
01014 
01015 
01016 int
01017 DNSEntry::delayEvent(int event, Event *e)
01018 {
01019   (void) event;
01020   if (dnsProcessor.handler) {
01021     SET_HANDLER((DNSEntryHandler) & DNSEntry::mainEvent);
01022     return handleEvent(EVENT_IMMEDIATE, e);
01023   }
01024   e->schedule_in(DNS_DELAY_PERIOD);
01025   return EVENT_CONT;
01026 }
01027 
01028 /** Handle timeout events. */
01029 int
01030 DNSEntry::mainEvent(int event, Event *e)
01031 {
01032   switch (event) {
01033   default:
01034     ink_assert(!"bad case");
01035     return EVENT_DONE;
01036   case EVENT_IMMEDIATE:{
01037       if (!dnsH)
01038         dnsH = dnsProcessor.handler;
01039       if (!dnsH) {
01040         Debug("dns", "handler not found, retrying...");
01041         SET_HANDLER((DNSEntryHandler) & DNSEntry::delayEvent);
01042         return handleEvent(event, e);
01043       }
01044 
01045       // trailing '.' indicates no domain expansion
01046       if (dns_search && ('.' != qname[orig_qname_len - 1])) {
01047         domains = dnsH->m_res->dnsrch;
01048         // start domain expansion straight away
01049         // if lookup name has no '.'
01050         if (domains && !strnchr(qname, '.', MAXDNAME)) {
01051           qname[orig_qname_len] = '.';
01052           qname_len = orig_qname_len + 1 + ink_strlcpy(qname + orig_qname_len + 1, *domains,
01053                                                        MAXDNAME - (orig_qname_len + 1));
01054           ++domains;
01055         }
01056       } else {
01057         domains = NULL;
01058       }
01059       Debug("dns", "enqueing query %s", qname);
01060       DNSEntry *dup = get_entry(dnsH, qname, qtype);
01061       if (dup) {
01062         Debug("dns", "collapsing NS request");
01063         dup->dups.enqueue(this);
01064       } else {
01065         Debug("dns", "adding first to collapsing queue");
01066         dnsH->entries.enqueue(this);
01067         write_dns(dnsH);
01068       }
01069       return EVENT_DONE;
01070     }
01071   case EVENT_INTERVAL:
01072     Debug("dns", "timeout for query %s", qname);
01073     if (dnsH->txn_lookup_timeout) {
01074       timeout = NULL;
01075       dns_result(dnsH, this, result_ent, false);        //do not retry -- we are over TXN timeout on DNS alone!
01076       return EVENT_DONE;
01077     }
01078     if (written_flag) {
01079       Debug("dns", "marking %s as not-written", qname);
01080       written_flag = false;
01081       --(dnsH->in_flight);
01082       DNS_DECREMENT_DYN_STAT(dns_in_flight_stat);
01083     }
01084     timeout = NULL;
01085     dns_result(dnsH, this, result_ent, true);
01086     return EVENT_DONE;
01087   }
01088 }
01089 
01090 Action *
01091 DNSProcessor::getby(const char *x, int len, int type, Continuation *cont, Options const& opt)
01092 {
01093   Debug("dns", "received query %s type = %d, timeout = %d", x, type, opt.timeout);
01094   if (type == T_SRV) {
01095     Debug("dns_srv", "DNSProcessor::getby attempting an SRV lookup for %s, timeout = %d", x, opt.timeout);
01096   }
01097   DNSEntry *e = dnsEntryAllocator.alloc();
01098   e->retries = dns_retries;
01099   e->init(x, len, type, cont, opt);
01100   MUTEX_TRY_LOCK(lock, e->mutex, this_ethread());
01101   if (!lock)
01102     thread->schedule_imm(e);
01103   else
01104     e->handleEvent(EVENT_IMMEDIATE, 0);
01105   return &e->action;
01106 }
01107 
01108 /**
01109   We have a result for an entry, return it to the user or retry if it
01110   is a retry-able and we have retries left.
01111 */
01112 static void
01113 dns_result(DNSHandler *h, DNSEntry *e, HostEnt *ent, bool retry) {
01114   ProxyMutex *mutex = h->mutex;
01115   bool cancelled = (e->action.cancelled ? true : false);
01116 
01117   if (!ent && !cancelled) {
01118     // try to retry operation
01119     if (retry && e->retries) {
01120       Debug("dns", "doing retry for %s", e->qname);
01121 
01122       DNS_INCREMENT_DYN_STAT(dns_retries_stat);
01123 
01124       --(e->retries);
01125       write_dns(h);
01126       return;
01127     } else if (e->domains && *e->domains) {
01128       do {
01129         Debug("dns", "domain extending, last tried '%s', original '%.*s'", e->qname, e->orig_qname_len, e->qname);
01130 
01131         // Make sure the next try fits
01132         if (e->orig_qname_len + strlen(*e->domains) + 2 > MAXDNAME) {
01133           Debug("dns", "domain too large %.*s + %s", e->orig_qname_len, e->qname, *e->domains);
01134         } else {
01135           e->qname[e->orig_qname_len] = '.';
01136           e->qname_len = e->orig_qname_len + 1 + ink_strlcpy(e->qname + e->orig_qname_len + 1, *e->domains,
01137                                                              MAXDNAME - (e->orig_qname_len + 1));
01138           ++(e->domains);
01139           e->retries = dns_retries;
01140           Debug("dns", "new name = %s retries = %d", e->qname, e->retries);
01141           write_dns(h);
01142 
01143           return;
01144         }
01145 
01146         // Try another one
01147         ++(e->domains);
01148       } while (*e->domains);
01149     } else {
01150       e->qname[e->qname_len] = 0;
01151       if (!strchr(e->qname, '.') && !e->last) {
01152         e->last = true;
01153         write_dns(h);
01154         return;
01155       }
01156     }
01157     if (retry) {
01158       DNS_INCREMENT_DYN_STAT(dns_max_retries_exceeded_stat);
01159     }
01160   }
01161   if (ent == BAD_DNS_RESULT)
01162     ent = NULL;
01163   if (!cancelled) {
01164     if (!ent) {
01165       DNS_SUM_DYN_STAT(dns_fail_time_stat, ink_get_hrtime() - e->submit_time);
01166     } else {
01167       DNS_SUM_DYN_STAT(dns_success_time_stat, ink_get_hrtime() - e->submit_time);
01168     }
01169   }
01170   h->entries.remove(e);
01171 
01172   if (is_debug_tag_set("dns")) {
01173     if (is_addr_query(e->qtype)) {
01174       ip_text_buffer buff;
01175       char const* ptr = "<none>";
01176       char const* result = "FAIL";
01177       if (ent) {
01178         result = "SUCCESS";
01179         ptr = inet_ntop(e->qtype == T_AAAA ? AF_INET6 : AF_INET, ent->ent.h_addr_list[0], buff, sizeof(buff));
01180       }
01181       Debug("dns", "%s result for %s = %s retry %d", result, e->qname, ptr, retry);
01182     } else {
01183       if (ent) {
01184         Debug("dns", "SUCCESS result for %s = %s af=%d retry %d", e->qname, ent->ent.h_name, ent->ent.h_addrtype, retry);
01185       } else {
01186         Debug("dns", "FAIL result for %s = <not found> retry %d", e->qname, retry);
01187       }
01188     }
01189   }
01190 
01191   if (ent) {
01192     DNS_INCREMENT_DYN_STAT(dns_lookup_success_stat);
01193   } else {
01194     DNS_INCREMENT_DYN_STAT(dns_lookup_fail_stat);
01195   }
01196 
01197   DNSEntry *dup = NULL;
01198   while ((dup = e->dups.dequeue())) {
01199     if (dup->post(h, ent)) {
01200       e->dups.enqueue(dup);
01201       goto Lretry;
01202     }
01203   }
01204 
01205   if (e->timeout) {
01206     e->timeout->cancel(e);
01207     e->timeout = NULL;
01208   }
01209   e->result_ent = ent;
01210 
01211   if (h->mutex->thread_holding == e->submit_thread) {
01212     MUTEX_TRY_LOCK(lock, e->action.mutex, h->mutex->thread_holding);
01213     if (!lock) {
01214       Debug("dns", "failed lock for result %s", e->qname);
01215       goto Lretry;
01216     }
01217     for (int i = 0; i < MAX_DNS_RETRIES; i++) {
01218       if (e->id[i] < 0)
01219         break;
01220       h->release_query_id(e->id[i]);
01221     }
01222     e->postEvent(0, 0);
01223   } else {
01224     for (int i = 0; i < MAX_DNS_RETRIES; i++) {
01225       if (e->id[i] < 0)
01226         break;
01227       h->release_query_id(e->id[i]);
01228     }
01229     e->mutex = e->action.mutex;
01230     SET_CONTINUATION_HANDLER(e, &DNSEntry::postEvent);
01231     e->submit_thread->schedule_imm_signal(e);
01232   }
01233   return;
01234 Lretry:
01235   e->result_ent = ent;
01236   e->retries = 0;
01237   if (e->timeout)
01238     e->timeout->cancel();
01239   e->timeout = h->mutex->thread_holding->schedule_in(e, DNS_PERIOD);
01240 }
01241 
01242 int
01243 DNSEntry::post(DNSHandler *h, HostEnt *ent)
01244 {
01245   if (timeout) {
01246     timeout->cancel(this);
01247     timeout = NULL;
01248   }
01249   result_ent = ent;
01250   if (h->mutex->thread_holding == submit_thread) {
01251     MUTEX_TRY_LOCK(lock, action.mutex, h->mutex->thread_holding);
01252     if (!lock) {
01253       Debug("dns", "failed lock for result %s", qname);
01254       return 1;
01255     }
01256     postEvent(0, 0);
01257   } else {
01258     mutex = action.mutex;
01259     SET_HANDLER(&DNSEntry::postEvent);
01260     submit_thread->schedule_imm_signal(this);
01261   }
01262   return 0;
01263 }
01264 
01265 int
01266 DNSEntry::postEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
01267 {
01268   if (!action.cancelled) {
01269     Debug("dns", "called back continuation for %s", qname);
01270     action.continuation->handleEvent(DNS_EVENT_LOOKUP, result_ent);
01271   }
01272   result_ent = NULL;
01273   action.mutex = NULL;
01274   mutex = NULL;
01275   dnsEntryAllocator.free(this);
01276   return EVENT_DONE;
01277 }
01278 
01279 /** Decode the reply from "named". */
01280 static bool
01281 dns_process(DNSHandler *handler, HostEnt *buf, int len)
01282 {
01283   ProxyMutex *mutex = handler->mutex;
01284   HEADER *h = (HEADER *) (buf->buf);
01285   DNSEntry *e = get_dns(handler, (uint16_t) ntohs(h->id));
01286   bool retry = false;
01287   bool server_ok = true;
01288   uint32_t temp_ttl = 0;
01289 
01290   //
01291   // Do we have an entry for this id?
01292   //
01293   if (!e || !e->written_flag) {
01294     Debug("dns", "unknown DNS id = %u", (uint16_t) ntohs(h->id));
01295     return false;               // cannot count this as a success
01296   }
01297   //
01298   // It is no longer in flight
01299   //
01300   e->written_flag = false;
01301   --(handler->in_flight);
01302   DNS_DECREMENT_DYN_STAT(dns_in_flight_stat);
01303 
01304   DNS_SUM_DYN_STAT(dns_response_time_stat, ink_get_hrtime() - e->send_time);
01305 
01306   if (h->rcode != NOERROR || !h->ancount) {
01307     Debug("dns", "received rcode = %d", h->rcode);
01308     switch (h->rcode) {
01309     default:
01310       Warning("Unknown DNS error %d for [%s]", h->rcode, e->qname);
01311       retry = true;
01312       server_ok = false;        // could be server problems
01313       goto Lerror;
01314     case SERVFAIL:             // recoverable error
01315       retry = true;
01316     case FORMERR:              // unrecoverable errors
01317     case REFUSED:
01318     case NOTIMP:
01319       Debug("dns", "DNS error %d for [%s]", h->rcode, e->qname);
01320       server_ok = false;        // could be server problems
01321       goto Lerror;
01322     case NOERROR:
01323     case NXDOMAIN:
01324     case 6:                    // YXDOMAIN
01325     case 7:                    // YXRRSET
01326     case 8:                    // NOTAUTH
01327     case 9:                    // NOTAUTH
01328     case 10:                   // NOTZONE
01329       Debug("dns", "DNS error %d for [%s]", h->rcode, e->qname);
01330       goto Lerror;
01331     }
01332   } else {
01333 
01334     //
01335     // Initialize local data
01336     //
01337     //    struct in_addr host_addr;            unused
01338     u_char tbuf[MAXDNAME + 1];
01339     buf->ent.h_name = NULL;
01340 
01341     int ancount = ntohs(h->ancount);
01342     unsigned char *bp = buf->hostbuf;
01343     int buflen = sizeof(buf->hostbuf);
01344     u_char *cp = ((u_char *) h) + HFIXEDSZ;
01345     u_char *eom = (u_char *) h + len;
01346     int n;
01347     ink_assert(buf->srv_hosts.srv_host_count == 0 && buf->srv_hosts.srv_hosts_length == 0);
01348     buf->srv_hosts.srv_host_count = 0;
01349     buf->srv_hosts.srv_hosts_length = 0;
01350     unsigned& num_srv = buf->srv_hosts.srv_host_count;
01351     int rname_len = -1;
01352 
01353     //
01354     // Expand name
01355     //
01356     if ((n = ink_dn_expand((u_char *) h, eom, cp, bp, buflen)) < 0)
01357       goto Lerror;
01358 
01359     // Should we validate the query name?
01360     if (dns_validate_qname) {
01361       int qlen = e->qname_len;
01362       int rlen = strlen((char *)bp);
01363 
01364       rname_len = rlen; // Save for later use
01365       if ((qlen > 0) && ('.' == e->qname[qlen-1]))
01366         --qlen;
01367       if ((rlen > 0) && ('.' == bp[rlen-1]))
01368         --rlen;
01369       // TODO: At some point, we might want to care about the case here, and use an algorithm
01370       // to randomly pick upper case characters in the query, and validate the response with
01371       // case sensitivity.
01372       if ((qlen != rlen) || (strncasecmp(e->qname, (const char*)bp, qlen) != 0)) {
01373         // Bad mojo, forged?
01374         Warning("received DNS response with query name of '%s', but response query name is '%s'", e->qname, bp);
01375         goto Lerror;
01376       } else {
01377         Debug("dns", "query name validated properly for %s", e->qname);
01378       }
01379     }
01380 
01381     cp += n + QFIXEDSZ;
01382     if (is_addr_query(e->qtype)) {
01383       if (-1 == rname_len)
01384         n = strlen((char *)bp) + 1;
01385       else
01386         n = rname_len + 1;
01387       buf->ent.h_name = (char *) bp;
01388       bp += n;
01389       buflen -= n;
01390     }
01391     //
01392     // Configure HostEnt data structure
01393     //
01394     u_char **ap = buf->host_aliases;
01395     buf->ent.h_aliases = (char **) buf->host_aliases;
01396     u_char **hap = (u_char **) buf->h_addr_ptrs;
01397     *hap = NULL;
01398     buf->ent.h_addr_list = (char **) buf->h_addr_ptrs;
01399 
01400     //
01401     // INKqa10938: For customer (i.e. USPS) with closed environment, need to
01402     // build up try_server_names[] with names already successfully resolved.
01403     // try_server_names[] gets filled up with every success dns response.
01404     // Once it's full, a new entry get inputted into try_server_names round-
01405     // robin style every 50 success dns response.
01406 
01407     // TODO: Why do we do strlen(e->qname) ? That should be available in
01408     // e->qname_len, no ?
01409     if (local_num_entries >= DEFAULT_NUM_TRY_SERVER) {
01410       if ((attempt_num_entries % 50) == 0) {
01411         try_servers = (try_servers + 1) % countof(try_server_names);
01412         ink_strlcpy(try_server_names[try_servers], e->qname, MAXDNAME);
01413         memset(&try_server_names[try_servers][strlen(e->qname)], 0, 1);
01414         attempt_num_entries = 0;
01415       }
01416       ++attempt_num_entries;
01417     } else {
01418       // fill up try_server_names for try_primary_named
01419       try_servers = local_num_entries++;
01420       ink_strlcpy(try_server_names[try_servers], e->qname, MAXDNAME);
01421       memset(&try_server_names[try_servers][strlen(e->qname)], 0, 1);
01422     }
01423 
01424     /* added for SRV support [ebalsa]
01425        this skips the query section (qdcount)
01426      */
01427     unsigned char *here = (unsigned char *) buf->buf + HFIXEDSZ;
01428     if (e->qtype == T_SRV) {
01429       for (int ctr = ntohs(h->qdcount); ctr > 0; ctr--) {
01430         int strlen = dn_skipname(here, eom);
01431         here += strlen + QFIXEDSZ;
01432       }
01433     }
01434     //
01435     // Decode each answer
01436     //
01437     int answer = false, error = false;
01438 
01439     while (ancount-- > 0 && cp < eom && !error) {
01440       n = ink_dn_expand((u_char *) h, eom, cp, bp, buflen);
01441       if (n < 0) {
01442         ++error;
01443         break;
01444       }
01445       cp += n;
01446       short int type;
01447       NS_GET16(type, cp);
01448       cp += NS_INT16SZ;  // NS_GET16(cls, cp);
01449       NS_GET32(temp_ttl, cp); // NOTE: this is not a "long" but 32-bits (from nameser_compat.h)
01450       if ((temp_ttl < buf->ttl) || (buf->ttl == 0))
01451         buf->ttl = temp_ttl;
01452       NS_GET16(n, cp);
01453 
01454       //
01455       // Decode cname
01456       //
01457       if (is_addr_query(e->qtype) && type == T_CNAME) {
01458         if (ap >= &buf->host_aliases[DNS_MAX_ALIASES - 1])
01459           continue;
01460         n = ink_dn_expand((u_char *) h, eom, cp, tbuf, sizeof(tbuf));
01461         if (n < 0) {
01462           ++error;
01463           break;
01464         }
01465         cp += n;
01466         *ap++ = (unsigned char *) bp;
01467         n = strlen((char *) bp) + 1;
01468         bp += n;
01469         buflen -= n;
01470         n = strlen((char *) tbuf) + 1;
01471         if (n > buflen) {
01472           ++error;
01473           break;
01474         }
01475         ink_strlcpy((char *) bp, (char *) tbuf, buflen);
01476         bp += n;
01477         buflen -= n;
01478         Debug("dns", "received cname = %s", tbuf);
01479         continue;
01480       }
01481       if (e->qtype != type) {
01482         ++error;
01483         break;
01484       }
01485       //
01486       // Decode names
01487       //
01488       if (type == T_PTR) {
01489         n = ink_dn_expand((u_char *) h, eom, cp, bp, buflen);
01490         if (n < 0) {
01491           ++error;
01492           break;
01493         }
01494         cp += n;
01495         if (!answer) {
01496           buf->ent.h_name = (char *) bp;
01497           Debug("dns", "received PTR name = %s", bp);
01498           n = strlen((char *) bp) + 1;
01499           bp += n;
01500           buflen -= n;
01501         } else if (ap < &buf->host_aliases[DNS_MAX_ALIASES - 1]) {
01502           *ap++ = bp;
01503           Debug("dns", "received PTR alias = %s", bp);
01504           n = strlen((char *) bp) + 1;
01505           bp += n;
01506           buflen -= n;
01507         }
01508       } else if (type == T_SRV) {
01509         if (num_srv >= HOST_DB_MAX_ROUND_ROBIN_INFO)
01510           break;
01511         cp = here;              /* hack */
01512         int strlen = dn_skipname(cp, eom);
01513         cp += strlen;
01514         const unsigned char *srv_off = cp;
01515         cp += SRV_FIXEDSZ;
01516         cp += dn_skipname(cp, eom);
01517         here = cp;              /* hack */
01518         SRV *srv = &buf->srv_hosts.hosts[num_srv];
01519         int r = ink_ns_name_ntop(srv_off + SRV_SERVER, srv->host, MAXDNAME);
01520         if (r <= 0) {
01521           /* FIXME: is this really an error? or just a continue; */
01522           ++error;
01523           goto Lerror;
01524         }
01525         Debug("dns_srv", "Discovered SRV record [from NS lookup] with cost:%d weight:%d port:%d with host:%s",
01526             ink_get16(srv_off + SRV_COST),
01527             ink_get16(srv_off + SRV_WEIGHT), ink_get16(srv_off + SRV_PORT), srv->host);
01528 
01529         srv->port = ink_get16(srv_off + SRV_PORT);
01530         srv->priority = ink_get16(srv_off + SRV_COST);
01531         srv->weight = ink_get16(srv_off + SRV_WEIGHT);
01532         srv->host_len = r;
01533         srv->host[r-1] = '\0';
01534         srv->key = makeHostHash(srv->host);
01535 
01536         if (srv->host[0] != '\0')
01537           buf->srv_hosts.srv_hosts_length += r;
01538         else
01539           continue;
01540         ++num_srv;
01541       } else if (is_addr_query(type)) {
01542         if (answer) {
01543           if (n != buf->ent.h_length) {
01544             cp += n;
01545             continue;
01546           }
01547         } else {
01548           int nn;
01549           buf->ent.h_length = n;
01550           buf->ent.h_addrtype = T_A == type ? AF_INET : AF_INET6;
01551           buf->ent.h_name = (char *) bp;
01552           nn = strlen((char *) bp) + 1;
01553           Debug("dns", "received %s name = %s", QtypeName(type), bp);
01554           bp += nn;
01555           buflen -= nn;
01556         }
01557         // attempt to use the original buffer (if it is word aligned)
01558         if (!(((uintptr_t) cp) % sizeof(unsigned int))) {
01559           *hap++ = cp;
01560           cp += n;
01561         } else {
01562           ip_text_buffer ip_string;
01563           bp = (unsigned char *) align_pointer_forward(bp, sizeof(int));
01564           if (bp + n >= buf->hostbuf + DNS_HOSTBUF_SIZE) {
01565             ++error;
01566             break;
01567           }
01568           memcpy((*hap++ = bp), cp, n);
01569           Debug("dns", "received %s = %s", QtypeName(type),
01570             inet_ntop(T_AAAA == type ? AF_INET6 : AF_INET, bp, ip_string, sizeof(ip_string))
01571           );
01572           bp += n;
01573           cp += n;
01574         }
01575       } else
01576         goto Lerror;
01577       ++answer;
01578     }
01579     if (answer) {
01580       *ap = NULL;
01581       *hap = NULL;
01582       //
01583       // If the named didn't send us the name, insert the one
01584       // the user gave us...
01585       //
01586       if (!buf->ent.h_name) {
01587         Debug("dns", "inserting name = %s", e->qname);
01588         ink_strlcpy((char *) bp, e->qname, sizeof(buf->hostbuf) - (bp - buf->hostbuf));
01589         buf->ent.h_name = (char *) bp;
01590       }
01591       dns_result(handler, e, buf, retry);
01592       return server_ok;
01593     }
01594   }
01595 Lerror:;
01596   DNS_INCREMENT_DYN_STAT(dns_lookup_fail_stat);
01597   dns_result(handler, e, NULL, retry);
01598   return server_ok;
01599 }
01600 
01601 
01602 RecRawStatBlock *dns_rsb;
01603 
01604 void
01605 ink_dns_init(ModuleVersion v)
01606 {
01607   static int init_called = 0;
01608 
01609   Debug("dns", "ink_dns_init: called with init_called = %d", init_called);
01610 
01611   ink_release_assert(!checkModuleVersion(v, HOSTDB_MODULE_VERSION));
01612   if (init_called)
01613     return;
01614 
01615   init_called = 1;
01616   // do one time stuff
01617   // create a stat block for HostDBStats
01618   dns_rsb = RecAllocateRawStatBlock((int) DNS_Stat_Count);
01619 
01620   //
01621   // Register statistics callbacks
01622   //
01623   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01624                      "proxy.process.dns.total_dns_lookups",
01625                      RECD_INT, RECP_PERSISTENT, (int) dns_total_lookups_stat, RecRawStatSyncSum);
01626 
01627   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01628                      "proxy.process.dns.lookup_avg_time",
01629                      RECD_INT, RECP_PERSISTENT, (int) dns_response_time_stat, RecRawStatSyncHrTimeAvg);
01630 
01631   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01632                      "proxy.process.dns.success_avg_time",
01633                      RECD_INT, RECP_NON_PERSISTENT, (int) dns_success_time_stat, RecRawStatSyncHrTimeAvg);
01634 
01635   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01636                      "proxy.process.dns.lookup_successes",
01637                      RECD_INT, RECP_PERSISTENT, (int) dns_lookup_success_stat, RecRawStatSyncSum);
01638 
01639   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01640                      "proxy.process.dns.fail_avg_time",
01641                      RECD_INT, RECP_PERSISTENT, (int) dns_fail_time_stat, RecRawStatSyncHrTimeAvg);
01642 
01643   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01644                      "proxy.process.dns.lookup_failures",
01645                      RECD_INT, RECP_PERSISTENT, (int) dns_lookup_fail_stat, RecRawStatSyncSum);
01646 
01647   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01648                      "proxy.process.dns.retries", RECD_INT, RECP_PERSISTENT, (int) dns_retries_stat, RecRawStatSyncSum);
01649 
01650   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01651                      "proxy.process.dns.max_retries_exceeded",
01652                      RECD_INT, RECP_PERSISTENT, (int) dns_max_retries_exceeded_stat, RecRawStatSyncSum);
01653 
01654   RecRegisterRawStat(dns_rsb, RECT_PROCESS,
01655                      "proxy.process.dns.in_flight",
01656                      RECD_INT, RECP_NON_PERSISTENT, (int) dns_in_flight_stat, RecRawStatSyncSum);
01657 
01658 }
01659 
01660 
01661 #ifdef TS_HAS_TESTS
01662 struct DNSRegressionContinuation;
01663 typedef int (DNSRegressionContinuation::*DNSRegContHandler) (int, void *);
01664 
01665 struct DNSRegressionContinuation: public Continuation
01666 {
01667   int hosts;
01668   const char **hostnames;
01669   int type;
01670   int *status;
01671   int found;
01672   int tofind;
01673   int i;
01674   RegressionTest *test;
01675 
01676   int mainEvent(int event, HostEnt *he)
01677   {
01678     (void) event;
01679     if (event == DNS_EVENT_LOOKUP) {
01680       if (he) {
01681         struct in_addr in;
01682         ++found;
01683         in.s_addr = *(unsigned int *) he->ent.h_addr_list[0];
01684         rprintf(test, "host %s [%s] = %s\n", hostnames[i - 1], he->ent.h_name, inet_ntoa(in));
01685       } else {
01686         rprintf(test, "host %s not found\n", hostnames[i - 1]);
01687       }
01688     }
01689     if (i < hosts) {
01690         dnsProcessor.gethostbyname(this, hostnames[i], DNSProcessor::Options().setHostResStyle(HOST_RES_IPV4_ONLY));
01691       ++i;
01692       return EVENT_CONT;
01693     } else {
01694       if (found == tofind)
01695         *status = REGRESSION_TEST_PASSED;
01696       else
01697         *status = REGRESSION_TEST_FAILED;
01698       return EVENT_DONE;
01699     }
01700   }
01701 
01702   DNSRegressionContinuation(int ahosts, int atofind, const char **ahostnames, RegressionTest *t, int atype, int *astatus)
01703    :  Continuation(new_ProxyMutex()), hosts(ahosts), hostnames(ahostnames), type(atype),
01704       status(astatus), found(0), tofind(atofind), i(0), test(t) {
01705     SET_HANDLER((DNSRegContHandler) & DNSRegressionContinuation::mainEvent);
01706   }
01707 };
01708 
01709 static const char *dns_test_hosts[] = {
01710   "www.apple.com",
01711   "www.ibm.com",
01712   "www.microsoft.com",
01713   "www.coke.com"
01714 };
01715 
01716 REGRESSION_TEST(DNS) (RegressionTest *t, int atype, int *pstatus) {
01717   eventProcessor.schedule_in(new DNSRegressionContinuation(4, 4, dns_test_hosts, t, atype, pstatus),
01718                              HRTIME_SECONDS(1));
01719 }
01720 
01721 #endif

Generated by  doxygen 1.7.1