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

HostDB.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 #define _HOSTDB_CC_
00025 
00026 #include "P_HostDB.h"
00027 #include "I_Layout.h"
00028 #include "Show.h"
00029 
00030 // dxu: turn off all Diags.h 's function.
00031 //#define Debug
00032 //#define Warning
00033 //#define Note
00034 
00035 #include "ink_apidefs.h"
00036 
00037 HostDBProcessor hostDBProcessor;
00038 int HostDBProcessor::hostdb_strict_round_robin = 0;
00039 int HostDBProcessor::hostdb_timed_round_robin = 0;
00040 HostDBProcessor::Options const HostDBProcessor::DEFAULT_OPTIONS;
00041 HostDBContinuation::Options const HostDBContinuation::DEFAULT_OPTIONS;
00042 int hostdb_enable = true;
00043 int hostdb_migrate_on_demand = true;
00044 int hostdb_cluster = false;
00045 int hostdb_cluster_round_robin = false;
00046 int hostdb_lookup_timeout = 120;
00047 int hostdb_insert_timeout = 160;
00048 int hostdb_re_dns_on_reload = false;
00049 int hostdb_ttl_mode = TTL_OBEY;
00050 unsigned int hostdb_current_interval = 0;
00051 unsigned int hostdb_ip_stale_interval = HOST_DB_IP_STALE;
00052 unsigned int hostdb_ip_timeout_interval = HOST_DB_IP_TIMEOUT;
00053 unsigned int hostdb_ip_fail_timeout_interval = HOST_DB_IP_FAIL_TIMEOUT;
00054 unsigned int hostdb_serve_stale_but_revalidate = 0;
00055 char hostdb_filename[PATH_NAME_MAX + 1] = DEFAULT_HOST_DB_FILENAME;
00056 int hostdb_size = DEFAULT_HOST_DB_SIZE;
00057 int hostdb_sync_frequency = 120;
00058 int hostdb_srv_enabled = 0;
00059 int hostdb_disable_reverse_lookup = 0;
00060 
00061 ClassAllocator<HostDBContinuation> hostDBContAllocator("hostDBContAllocator");
00062 
00063 // Static configuration information
00064 
00065 HostDBCache hostDB;
00066 
00067 static  Queue <HostDBContinuation > remoteHostDBQueue[MULTI_CACHE_PARTITIONS];
00068 
00069 char *
00070 HostDBInfo::srvname(HostDBRoundRobin *rr)
00071 {
00072   if (!is_srv || !data.srv.srv_offset)
00073     return NULL;
00074   ink_assert(this - rr->info >= 0 && this - rr->info < rr->rrcount && data.srv.srv_offset < rr->length);
00075   return (char *) rr + data.srv.srv_offset;
00076 }
00077 
00078 static inline int
00079 corrupt_debugging_callout(HostDBInfo * e, RebuildMC & r)
00080 {
00081   Debug("hostdb", "corrupt %ld part %d",
00082     (long)((char *) &e->app.rr.offset - r.data), r.partition);
00083   return -1;
00084 }
00085 
00086 static inline bool
00087 is_addr_valid(
00088   uint8_t af, ///< Address family (format of data)
00089   void* ptr ///< Raw address data (not a sockaddr variant!)
00090 ) {
00091   return
00092     (AF_INET == af && INADDR_ANY != *(reinterpret_cast<in_addr_t*>(ptr)))
00093     || (AF_INET6 == af && !IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast<in6_addr*>(ptr)))
00094     ;
00095 }
00096 
00097 static inline void
00098 ip_addr_set(
00099   sockaddr* ip, ///< Target storage, sockaddr compliant.
00100   uint8_t af, ///< Address format.
00101   void* ptr ///< Raw address data
00102 ) {
00103   if (AF_INET6 == af)
00104     ats_ip6_set(ip, *static_cast<in6_addr*>(ptr));
00105   else if (AF_INET == af)
00106     ats_ip4_set(ip, *static_cast<in_addr_t*>(ptr));
00107   else ats_ip_invalidate(ip);
00108 }
00109 
00110 static inline void
00111 ip_addr_set(
00112   IpAddr& ip, ///< Target storage.
00113   uint8_t af, ///< Address format.
00114   void* ptr ///< Raw address data
00115 ) {
00116   if (AF_INET6 == af)
00117     ip = *static_cast<in6_addr*>(ptr);
00118   else if (AF_INET == af)
00119     ip = *static_cast<in_addr_t*>(ptr);
00120   else
00121     ip.invalidate();
00122 }
00123 
00124 inline void
00125 hostdb_cont_free(HostDBContinuation * cont)
00126 {
00127   if (cont->pending_action)
00128     cont->pending_action->cancel();
00129   cont->mutex = 0;
00130   cont->action.mutex = 0;
00131   hostDBContAllocator.free(cont);
00132 }
00133 
00134 /* Check whether a resolution fail should lead to a retry.
00135    The @a mark argument is updated if appropriate.
00136    @return @c true if @a mark was updated, @c false if no retry should be done.
00137 */
00138 static inline bool
00139 check_for_retry(HostDBMark& mark, HostResStyle style) {
00140   bool zret = true;
00141   if (HOSTDB_MARK_IPV4 == mark && HOST_RES_IPV4 == style)
00142     mark = HOSTDB_MARK_IPV6;
00143   else if (HOSTDB_MARK_IPV6 == mark && HOST_RES_IPV6 == style)
00144     mark = HOSTDB_MARK_IPV4;
00145   else
00146     zret = false;
00147   return zret;
00148 }
00149 
00150 char const*
00151 string_for(HostDBMark mark) {
00152   static char const* STRING[] = {
00153     "Generic", "IPv4", "IPv6", "SRV"
00154   };
00155   return STRING[mark];
00156 }
00157 
00158 //
00159 // Function Prototypes
00160 //
00161 static Action *
00162 register_ShowHostDB(Continuation * c, HTTPHdr * h);
00163 
00164 void
00165 HostDBMD5::refresh() {
00166   if (host_name) {
00167     char const* server_line = dns_server ? dns_server->x_dns_ip_line : 0;
00168     make_md5(hash, host_name, host_len, port, server_line, db_mark);
00169   } else {
00170     // INK_MD5 the ip, pad on both sizes with 0's
00171     // so that it does not intersect the string space
00172     //
00173     uint8_t buff[TS_IP6_SIZE+4];
00174     int n = ip.isIp6() ? sizeof(in6_addr) : sizeof(in_addr_t);
00175     memset(buff, 0, 2);
00176     memcpy(buff+2, ip._addr._byte, n);
00177     memset(buff + 2 + n , 0, 2);
00178     MD5Context().hash_immediate(hash, buff, n+4);
00179 //    hash.encodeBuffer(buff, n+4);
00180   }
00181 }
00182 
00183 HostDBMD5::HostDBMD5()
00184   : host_name(0), host_len(0), port(0),
00185     dns_server(0), db_mark(HOSTDB_MARK_GENERIC)
00186 {
00187 }
00188 
00189 HostDBCache::HostDBCache()
00190 {
00191   tag_bits = HOST_DB_TAG_BITS;
00192   max_hits = (1 << HOST_DB_HITS_BITS) - 1;
00193   version.ink_major = HOST_DB_CACHE_MAJOR_VERSION;
00194   version.ink_minor = HOST_DB_CACHE_MINOR_VERSION;
00195 }
00196 
00197 
00198 int
00199 HostDBCache::rebuild_callout(HostDBInfo * e, RebuildMC & r)
00200 {
00201   if (e->round_robin && e->reverse_dns)
00202     return corrupt_debugging_callout(e, r);
00203   if (e->reverse_dns) {
00204     if (e->data.hostname_offset < 0)
00205       return 0;
00206     if (e->data.hostname_offset > 0) {
00207       if (!valid_offset(e->data.hostname_offset - 1))
00208         return corrupt_debugging_callout(e, r);
00209       char *p = (char *) ptr(&e->data.hostname_offset, r.partition);
00210       if (!p)
00211         return corrupt_debugging_callout(e, r);
00212       char *s = p;
00213       while (*p && p - s < MAXDNAME) {
00214         if (!valid_heap_pointer(p))
00215           return corrupt_debugging_callout(e, r);
00216         p++;
00217       }
00218       if (p - s >= MAXDNAME)
00219         return corrupt_debugging_callout(e, r);
00220     }
00221   }
00222   if (e->round_robin) {
00223     if (e->app.rr.offset < 0)
00224       return 0;
00225     if (!valid_offset(e->app.rr.offset - 1))
00226       return corrupt_debugging_callout(e, r);
00227     HostDBRoundRobin *rr = (HostDBRoundRobin *) ptr(&e->app.rr.offset, r.partition);
00228     if (!rr)
00229       return corrupt_debugging_callout(e, r);
00230     if (rr->rrcount > HOST_DB_MAX_ROUND_ROBIN_INFO || rr->rrcount <= 0 ||
00231         rr->good > HOST_DB_MAX_ROUND_ROBIN_INFO || rr->good <= 0 || rr->good > rr->rrcount)
00232       return corrupt_debugging_callout(e, r);
00233     for (int i = 0; i < rr->good; i++) {
00234       if (!valid_heap_pointer(((char *) &rr->info[i + 1]) - 1))
00235         return -1;
00236       if (!ats_is_ip(rr->info[i].ip()))
00237         return corrupt_debugging_callout(e, r);
00238       if (rr->info[i].md5_high != e->md5_high ||
00239           rr->info[i].md5_low != e->md5_low || rr->info[i].md5_low_low != e->md5_low_low)
00240         return corrupt_debugging_callout(e, r);
00241     }
00242   }
00243   if (e->is_ip_timeout())
00244     return 0;
00245   return 1;
00246 }
00247 
00248 
00249 HostDBCache *
00250 HostDBProcessor::cache()
00251 {
00252   return &hostDB;
00253 }
00254 
00255 
00256 struct HostDBTestRR: public Continuation
00257 {
00258   int fd;
00259   char b[512];
00260   int nb;
00261   int outstanding, success, failure;
00262   int in;
00263 
00264   int mainEvent(int event, Event * e)
00265   {
00266     if (event == EVENT_INTERVAL) {
00267       printf("HostDBTestRR: %d outstanding %d succcess %d failure\n", outstanding, success, failure);
00268     }
00269     if (event == EVENT_HOST_DB_LOOKUP) {
00270       --outstanding;
00271       if (e)
00272         ++success;
00273       else
00274         ++failure;
00275     }
00276     if (in)
00277       return EVENT_CONT;
00278     in = 1;
00279     while (outstanding < 40) {
00280       if (!nb)
00281         goto Lreturn;
00282       char *end = (char *)memchr(b, '\n', nb);
00283       if (!end)
00284         read_some();
00285       end = (char *)memchr(b, '\n', nb);
00286       if (!end)
00287         nb = 0;
00288       else {
00289         *end = 0;
00290         outstanding++;
00291         hostDBProcessor.getbyname_re(this, b, 0);
00292         nb -= ((end + 1) - b);
00293         memcpy(b, end + 1, nb);
00294         if (!nb)
00295           read_some();
00296       }
00297     }
00298   Lreturn:
00299     in = 0;
00300     return EVENT_CONT;
00301   }
00302 
00303 
00304   void read_some()
00305   {
00306     nb = read(fd, b + nb, 512 - nb);
00307     ink_release_assert(nb >= 0);
00308   }
00309 
00310 
00311 HostDBTestRR():Continuation(new_ProxyMutex()), nb(0), outstanding(0), success(0), failure(0), in(0) {
00312     printf("starting HostDBTestRR....\n");
00313     fd = open("hostdb_test.config", O_RDONLY, 0);
00314     ink_release_assert(fd >= 0);
00315     read_some();
00316     SET_HANDLER(&HostDBTestRR::mainEvent);
00317   }
00318 };
00319 
00320 
00321 struct HostDBSyncer: public Continuation
00322 {
00323   int frequency;
00324   ink_hrtime start_time;
00325 
00326   int sync_event(int event, void *edata);
00327   int wait_event(int event, void *edata);
00328 
00329   HostDBSyncer();
00330 };
00331 
00332 
00333 HostDBSyncer::HostDBSyncer():
00334 Continuation(new_ProxyMutex()), frequency(0), start_time(0)
00335 {
00336   SET_HANDLER(&HostDBSyncer::sync_event);
00337 }
00338 
00339 
00340 int
00341 HostDBSyncer::sync_event(int, void *)
00342 {
00343   SET_HANDLER(&HostDBSyncer::wait_event);
00344   start_time = ink_get_hrtime();
00345   hostDBProcessor.cache()->sync_partitions(this);
00346   return EVENT_DONE;
00347 }
00348 
00349 
00350 int
00351 HostDBSyncer::wait_event(int, void *)
00352 {
00353   ink_hrtime next_sync = HRTIME_SECONDS(hostdb_sync_frequency) - (ink_get_hrtime() - start_time);
00354 
00355   SET_HANDLER(&HostDBSyncer::sync_event);
00356   if (next_sync > HRTIME_MSECONDS(100))
00357     mutex->thread_holding->schedule_in_local(this, next_sync);
00358   else
00359     mutex->thread_holding->schedule_imm_local(this);
00360   return EVENT_DONE;
00361 }
00362 
00363 
00364 int
00365 HostDBCache::start(int flags)
00366 {
00367   Store *hostDBStore;
00368   Span *hostDBSpan;
00369   char storage_path[PATH_NAME_MAX + 1];
00370   int storage_size = 33554432; // 32MB default
00371 
00372   bool reconfigure = ((flags & PROCESSOR_RECONFIGURE) ? true : false);
00373   bool fix = ((flags & PROCESSOR_FIX) ? true : false);
00374 
00375   storage_path[0] = '\0';
00376 
00377   // Read configuration
00378   // Command line overrides manager configuration.
00379   //
00380   REC_ReadConfigInt32(hostdb_enable, "proxy.config.hostdb");
00381   REC_ReadConfigString(hostdb_filename, "proxy.config.hostdb.filename", PATH_NAME_MAX);
00382   REC_ReadConfigInt32(hostdb_size, "proxy.config.hostdb.size");
00383   REC_ReadConfigInt32(hostdb_srv_enabled, "proxy.config.srv_enabled");
00384   REC_ReadConfigString(storage_path, "proxy.config.hostdb.storage_path", PATH_NAME_MAX);
00385   REC_ReadConfigInt32(storage_size, "proxy.config.hostdb.storage_size");
00386 
00387   // If proxy.config.hostdb.storage_path is not set, use the local state dir. If it is set to
00388   // a relative path, make it relative to the prefix.
00389   if (storage_path[0] == '\0') {
00390     ats_scoped_str rundir(RecConfigReadRuntimeDir());
00391     ink_strlcpy(storage_path, rundir, sizeof(storage_path));
00392   } else if (storage_path[0] != '/') {
00393     Layout::relative_to(storage_path, sizeof(storage_path), Layout::get()->prefix, storage_path);
00394   }
00395 
00396   Debug("hostdb", "Storage path is %s", storage_path);
00397 
00398   if (access(storage_path, W_OK | R_OK) == -1) {
00399     Warning("Unable to access() directory '%s': %d, %s", storage_path, errno, strerror(errno));
00400     Warning("Please set 'proxy.config.hostdb.storage_path' or 'proxy.config.local_state_dir'");
00401   }
00402 
00403   hostDBStore = new Store;
00404   hostDBSpan = new Span;
00405   hostDBSpan->init(storage_path, storage_size);
00406   hostDBStore->add(hostDBSpan);
00407 
00408   Debug("hostdb", "Opening %s, size=%d", hostdb_filename, hostdb_size);
00409   if (open(hostDBStore, "hostdb.config", hostdb_filename, hostdb_size, reconfigure, fix, false /* slient */ ) < 0) {
00410     ats_scoped_str rundir(RecConfigReadRuntimeDir());
00411     ats_scoped_str config(Layout::relative_to(rundir, "hostdb.config"));
00412 
00413     Note("reconfiguring host database");
00414 
00415     if (unlink(config) < 0)
00416       Debug("hostdb", "unable to unlink %s", (const char *)config);
00417 
00418     delete hostDBStore;
00419     hostDBStore = new Store;
00420     hostDBSpan = new Span;
00421     hostDBSpan->init(storage_path, storage_size);
00422     hostDBStore->add(hostDBSpan);
00423 
00424     if (open(hostDBStore, "hostdb.config", hostdb_filename, hostdb_size, true, fix) < 0) {
00425       Warning("could not initialize host database. Host database will be disabled");
00426       hostdb_enable = 0;
00427       delete hostDBStore;
00428       return -1;
00429     }
00430   }
00431   HOSTDB_SET_DYN_COUNT(hostdb_bytes_stat, totalsize);
00432   //  XXX I don't see this being reference in the previous function calls, so I am going to delete it -bcall
00433   delete hostDBStore;
00434   return 0;
00435 }
00436 
00437 
00438 // Start up the Host Database processor.
00439 // Load configuration, register configuration and statistics and
00440 // open the cache. This doesn't create any threads, so those
00441 // parameters are ignored.
00442 //
00443 int
00444 HostDBProcessor::start(int, size_t)
00445 {
00446   hostDB.alloc_mutexes();
00447 
00448   if (hostDB.start(0) < 0)
00449     return -1;
00450 
00451   if (auto_clear_hostdb_flag)
00452     hostDB.clear();
00453 
00454   HOSTDB_SET_DYN_COUNT(hostdb_total_entries_stat, hostDB.totalelements);
00455 
00456   statPagesManager.register_http("hostdb", register_ShowHostDB);
00457 
00458   //
00459   // Register configuration callback, and establish configuation links
00460   //
00461   REC_EstablishStaticConfigInt32(hostdb_ttl_mode, "proxy.config.hostdb.ttl_mode");
00462   REC_EstablishStaticConfigInt32(hostdb_disable_reverse_lookup, "proxy.config.cache.hostdb.disable_reverse_lookup");
00463   REC_EstablishStaticConfigInt32(hostdb_re_dns_on_reload, "proxy.config.hostdb.re_dns_on_reload");
00464   REC_EstablishStaticConfigInt32(hostdb_migrate_on_demand, "proxy.config.hostdb.migrate_on_demand");
00465   REC_EstablishStaticConfigInt32(hostdb_strict_round_robin, "proxy.config.hostdb.strict_round_robin");
00466   REC_EstablishStaticConfigInt32(hostdb_timed_round_robin, "proxy.config.hostdb.timed_round_robin");
00467   REC_EstablishStaticConfigInt32(hostdb_cluster, "proxy.config.hostdb.cluster");
00468   REC_EstablishStaticConfigInt32(hostdb_cluster_round_robin, "proxy.config.hostdb.cluster.round_robin");
00469   REC_EstablishStaticConfigInt32(hostdb_lookup_timeout, "proxy.config.hostdb.lookup_timeout");
00470   REC_EstablishStaticConfigInt32U(hostdb_ip_timeout_interval, "proxy.config.hostdb.timeout");
00471   REC_EstablishStaticConfigInt32U(hostdb_ip_stale_interval, "proxy.config.hostdb.verify_after");
00472   REC_EstablishStaticConfigInt32U(hostdb_ip_fail_timeout_interval, "proxy.config.hostdb.fail.timeout");
00473   REC_EstablishStaticConfigInt32U(hostdb_serve_stale_but_revalidate, "proxy.config.hostdb.serve_stale_for");
00474   REC_EstablishStaticConfigInt32(hostdb_sync_frequency, "proxy.config.cache.hostdb.sync_frequency");
00475 
00476   //
00477   // Set up hostdb_current_interval
00478   //
00479   hostdb_current_interval = (unsigned int)(ink_get_based_hrtime() / HOST_DB_TIMEOUT_INTERVAL);
00480 
00481   HostDBContinuation *b = hostDBContAllocator.alloc();
00482   SET_CONTINUATION_HANDLER(b, (HostDBContHandler) & HostDBContinuation::backgroundEvent);
00483   b->mutex = new_ProxyMutex();
00484   eventProcessor.schedule_every(b, HOST_DB_TIMEOUT_INTERVAL, ET_DNS);
00485 
00486   //
00487   // Sync HostDB, if we've asked for it.
00488   //
00489   if (hostdb_sync_frequency > 0)
00490     eventProcessor.schedule_imm(new HostDBSyncer);
00491   return 0;
00492 }
00493 
00494 
00495 void
00496 HostDBContinuation::init(HostDBMD5 const& the_md5, Options const& opt)
00497 {
00498   md5 = the_md5;
00499   if (md5.host_name) {
00500     // copy to backing store.
00501     if (md5.host_len > static_cast<int>(sizeof(md5_host_name_store)-1))
00502       md5.host_len = sizeof(md5_host_name_store)-1;
00503     memcpy(md5_host_name_store, md5.host_name, md5.host_len);
00504   } else {
00505     md5.host_len = 0;
00506   }
00507   md5_host_name_store[md5.host_len] = 0;
00508   md5.host_name = md5_host_name_store;
00509 
00510   host_res_style = opt.host_res_style;
00511   dns_lookup_timeout = opt.timeout;
00512   mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
00513   if (opt.cont) {
00514     action = opt.cont;
00515   } else {
00516     //ink_assert(!"this sucks");
00517     action.mutex = mutex;
00518   }
00519 }
00520 
00521 void
00522 HostDBContinuation::refresh_MD5() {
00523   ProxyMutex* old_bucket_mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
00524   // We're not pending DNS anymore.
00525   remove_trigger_pending_dns();
00526   md5.refresh();
00527   // Update the mutex if it's from the bucket.
00528   // Some call sites modify this after calling @c init so need to check.
00529   if (old_bucket_mutex == mutex)
00530     mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
00531 }
00532 
00533 void
00534 make_md5(INK_MD5 & md5, const char *hostname, int len, int port, char const* pDNSServers, HostDBMark mark)
00535 {
00536   INK_DIGEST_CTX ctx;
00537   ink_code_incr_md5_init(&ctx);
00538   ink_code_incr_md5_update(&ctx, hostname, len);
00539   unsigned short p = port;
00540   p = htons(p);
00541   ink_code_incr_md5_update(&ctx, (char *) &p, 2);
00542   uint8_t m = static_cast<uint8_t>(mark);
00543   ink_code_incr_md5_update(&ctx, (char *) &m, sizeof(m));     /* FIXME: check this */
00544   if (pDNSServers)
00545     ink_code_incr_md5_update(&ctx, pDNSServers, strlen(pDNSServers));
00546   ink_code_incr_md5_final((char *) &md5, &ctx);
00547 }
00548 
00549 static bool
00550 reply_to_cont(Continuation * cont, HostDBInfo * r, bool is_srv = false)
00551 {
00552   if (r == NULL || r->is_srv != is_srv || r->failed()) {
00553     cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, NULL);
00554     return false;
00555   }
00556 
00557   if (r->reverse_dns) {
00558     if (!r->hostname()) {
00559       ink_assert(!"missing hostname");
00560       cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, NULL);
00561       Warning("bogus entry deleted from HostDB: missing hostname");
00562       hostDB.delete_block(r);
00563       return false;
00564     }
00565     Debug("hostdb", "hostname = %s", r->hostname());
00566   }
00567 
00568   if (!r->is_srv && r->round_robin) {
00569     if (!r->rr()) {
00570       ink_assert(!"missing round-robin");
00571       cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, NULL);
00572       Warning("bogus entry deleted from HostDB: missing round-robin");
00573       hostDB.delete_block(r);
00574       return false;
00575     }
00576     ip_text_buffer ipb;
00577     Debug("hostdb", "RR of %d with %d good, 1st IP = %s", r->rr()->rrcount, r->rr()->good, ats_ip_ntop(r->ip(), ipb, sizeof ipb));
00578   }
00579 
00580   cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, r);
00581 
00582   if (!r->full) {
00583     Warning("bogus entry deleted from HostDB: none");
00584     hostDB.delete_block(r);
00585     return false;
00586   }
00587 
00588   return true;
00589 }
00590 
00591 inline HostResStyle
00592 host_res_style_for(sockaddr const* ip) {
00593   return ats_is_ip6(ip) ? HOST_RES_IPV6_ONLY : HOST_RES_IPV4_ONLY;
00594 }
00595 
00596 inline HostResStyle
00597 host_res_style_for(HostDBMark mark) {
00598   return HOSTDB_MARK_IPV4 == mark ? HOST_RES_IPV4_ONLY
00599     : HOSTDB_MARK_IPV6 == mark ? HOST_RES_IPV6_ONLY
00600     : HOST_RES_NONE
00601     ;
00602 }
00603 
00604 inline HostDBMark
00605 db_mark_for(HostResStyle style) {
00606   HostDBMark zret = HOSTDB_MARK_GENERIC;
00607   if (HOST_RES_IPV4 == style || HOST_RES_IPV4_ONLY == style)
00608     zret = HOSTDB_MARK_IPV4;
00609   else if (HOST_RES_IPV6 == style || HOST_RES_IPV6_ONLY == style)
00610     zret = HOSTDB_MARK_IPV6;
00611   return zret;
00612 }
00613 
00614 inline HostDBMark
00615 db_mark_for(sockaddr const* ip) {
00616   return ats_is_ip6(ip) ? HOSTDB_MARK_IPV6 : HOSTDB_MARK_IPV4;
00617 }
00618 
00619 HostDBInfo *
00620 probe(ProxyMutex *mutex, HostDBMD5 const& md5, bool ignore_timeout)
00621 {
00622   ink_assert(this_ethread() == hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets))->thread_holding);
00623   if (hostdb_enable) {
00624     uint64_t folded_md5 = fold_md5(md5.hash);
00625     HostDBInfo *r = hostDB.lookup_block(folded_md5, hostDB.levels);
00626     Debug("hostdb", "probe %.*s %" PRIx64 " %d [ignore_timeout = %d]",
00627           md5.host_len, md5.host_name, folded_md5, !!r, ignore_timeout);
00628     if (r && md5.hash[1] == r->md5_high) {
00629 
00630       // Check for timeout (fail probe)
00631       //
00632       if (r->is_deleted()) {
00633         Debug("hostdb", "HostDB entry was set as deleted");
00634         return NULL;
00635       } else if (r->failed()) {
00636         Debug("hostdb", "'%.*s' failed", md5.host_len, md5.host_name);
00637         if (r->is_ip_fail_timeout()) {
00638           Debug("hostdb", "fail timeout %u", r->ip_interval());
00639           return NULL;
00640         }
00641       } else if (!ignore_timeout && r->is_ip_timeout() && !r->serve_stale_but_revalidate()) {
00642         Debug("hostdb", "timeout %u %u %u", r->ip_interval(), r->ip_timestamp, r->ip_timeout_interval);
00643         HOSTDB_INCREMENT_DYN_STAT(hostdb_ttl_expires_stat);
00644         return NULL;
00645       }
00646 //error conditions
00647       if (r->reverse_dns && !r->hostname()) {
00648         Debug("hostdb", "missing reverse dns");
00649         hostDB.delete_block(r);
00650         return NULL;
00651       }
00652       if (r->round_robin && !r->rr()) {
00653         Debug("hostdb", "missing round-robin");
00654         hostDB.delete_block(r);
00655         return NULL;
00656       }
00657       // Check for stale (revalidate offline if we are the owner)
00658       // -or-
00659       // we are beyond our TTL but we choose to serve for another N seconds [hostdb_serve_stale_but_revalidate seconds]
00660       if ((!ignore_timeout && r->is_ip_stale()
00661            && !cluster_machine_at_depth(master_hash(md5.hash))
00662            && !r->reverse_dns) || (r->is_ip_timeout() && r->serve_stale_but_revalidate())) {
00663         Debug("hostdb", "stale %u %u %u, using it and refreshing it", r->ip_interval(),
00664               r->ip_timestamp, r->ip_timeout_interval);
00665         r->refresh_ip();
00666         if (!is_dotted_form_hostname(md5.host_name)) {
00667           HostDBContinuation *c = hostDBContAllocator.alloc();
00668           HostDBContinuation::Options copt;
00669           copt.host_res_style = host_res_style_for(r->ip());
00670           c->init(md5, copt);
00671           c->do_dns();
00672         }
00673       }
00674 
00675       r->hits++;
00676       if (!r->hits)
00677         r->hits--;
00678       return r;
00679     }
00680   }
00681   return NULL;
00682 }
00683 
00684 
00685 //
00686 // Insert a HostDBInfo into the database
00687 // A null value indicates that the block is empty.
00688 //
00689 HostDBInfo *
00690 HostDBContinuation::insert(unsigned int attl)
00691 {
00692   uint64_t folded_md5 = fold_md5(md5.hash);
00693   int bucket = folded_md5 % hostDB.buckets;
00694 
00695   ink_assert(this_ethread() == hostDB.lock_for_bucket(bucket)->thread_holding);
00696   // remove the old one to prevent buildup
00697   HostDBInfo *old_r = hostDB.lookup_block(folded_md5, 3);
00698   if (old_r)
00699     hostDB.delete_block(old_r);
00700   HostDBInfo *r = hostDB.insert_block(folded_md5, NULL, 0);
00701   r->md5_high = md5.hash[1];
00702   if (attl > HOST_DB_MAX_TTL)
00703     attl = HOST_DB_MAX_TTL;
00704   r->ip_timeout_interval = attl;
00705   r->ip_timestamp = hostdb_current_interval;
00706   Debug("hostdb", "inserting for: %.*s: (md5: %" PRIx64 ") bucket: %d now: %u timeout: %u ttl: %u", md5.host_len, md5.host_name, folded_md5, bucket, r->ip_timestamp,
00707         r->ip_timeout_interval, attl);
00708   return r;
00709 }
00710 
00711 
00712 //
00713 // Get an entry by either name or IP
00714 //
00715 Action *
00716 HostDBProcessor::getby(Continuation * cont,
00717                        const char *hostname, int len, sockaddr const* ip, bool aforce_dns, HostResStyle host_res_style, int dns_lookup_timeout)
00718 {
00719   HostDBMD5 md5;
00720   EThread *thread = this_ethread();
00721   ProxyMutex *mutex = thread->mutex;
00722   ip_text_buffer ipb;
00723 
00724   HOSTDB_INCREMENT_DYN_STAT(hostdb_total_lookups_stat);
00725 
00726   if ((!hostdb_enable || (hostname && !*hostname)) || (hostdb_disable_reverse_lookup && ip)) {
00727     MUTEX_TRY_LOCK(lock, cont->mutex, thread);
00728     if (!lock)
00729       goto Lretry;
00730     cont->handleEvent(EVENT_HOST_DB_LOOKUP, NULL);
00731     return ACTION_RESULT_DONE;
00732   }
00733 
00734   // Load the MD5 data.
00735   md5.host_name = hostname;
00736   md5.host_len = hostname ? (len ? len : strlen(hostname)) : 0;
00737   md5.ip.assign(ip);
00738   md5.port = ip ? ats_ip_port_host_order(ip) : 0;
00739   md5.db_mark = db_mark_for(host_res_style);
00740 #ifdef SPLIT_DNS
00741   if (hostname && SplitDNSConfig::isSplitDNSEnabled()) {
00742     const char *scan = hostname;
00743     // Is this a check for IPv4 address? Add check for IPv6?
00744     for (; *scan != '\0' && (ParseRules::is_digit(*scan) || '.' == *scan); scan++);
00745     if ('\0' != *scan) {
00746       SplitDNS* pSD = SplitDNSConfig::acquire();
00747       if (0 != pSD)
00748         md5.dns_server = static_cast<DNSServer*>(pSD->getDNSRecord(hostname));
00749       SplitDNSConfig::release(pSD);
00750     }
00751   }
00752 #endif // SPLIT_DNS
00753   md5.refresh();
00754 
00755   // Attempt to find the result in-line, for level 1 hits
00756   //
00757   if (!aforce_dns) {
00758     bool loop;
00759     do {
00760       loop = false; // Only loop on explicit set for retry.
00761       // find the partition lock
00762       //
00763       // TODO: Could we reuse the "mutex" above safely? I think so but not sure.
00764       ProxyMutex *bmutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
00765       MUTEX_TRY_LOCK(lock, bmutex, thread);
00766       MUTEX_TRY_LOCK(lock2, cont->mutex, thread);
00767 
00768       if (lock && lock2) {
00769         // If we can get the lock and a level 1 probe succeeds, return
00770         HostDBInfo *r = probe(bmutex, md5, aforce_dns);
00771         if (r) {
00772           if (r->failed() && hostname)
00773             loop = check_for_retry(md5.db_mark, host_res_style);
00774           if (!loop) {
00775             // No retry -> final result. Return it.
00776             Debug("hostdb", "immediate answer for %s",
00777                   hostname ? hostname
00778                   : ats_is_ip(ip) ? ats_ip_ntop(ip, ipb, sizeof ipb)
00779                   : "<null>"
00780               );
00781             HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
00782             reply_to_cont(cont, r);
00783             return ACTION_RESULT_DONE;
00784           }
00785           md5.refresh();
00786         }
00787       }
00788     } while (loop);
00789   }
00790   Debug("hostdb", "delaying force %d answer for %s", aforce_dns,
00791     hostname ? hostname
00792     : ats_is_ip(ip) ? ats_ip_ntop(ip, ipb, sizeof ipb)
00793     : "<null>"
00794   );
00795 
00796 Lretry:
00797   // Otherwise, create a continuation to do a deeper probe in the background
00798   //
00799   HostDBContinuation *c = hostDBContAllocator.alloc();
00800   HostDBContinuation::Options opt;
00801   opt.timeout = dns_lookup_timeout;
00802   opt.force_dns = aforce_dns;
00803   opt.cont = cont;
00804   opt.host_res_style = host_res_style;
00805   c->init(md5, opt);
00806   SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::probeEvent);
00807 
00808   // Since ProxyMutexPtr has a cast operator, gcc-3.x get upset
00809   // about ambiguity when doing this comparison, so by reversing
00810   // the operands, I force it to pick the cast operation /leif.
00811   if (thread->mutex == cont->mutex) {
00812     thread->schedule_in(c, MUTEX_RETRY_DELAY);
00813   } else {
00814     dnsProcessor.thread->schedule_imm(c);
00815   }
00816 
00817   return &c->action;
00818 }
00819 
00820 
00821 // Wrapper from getbyname to getby
00822 //
00823 Action *
00824 HostDBProcessor::getbyname_re(Continuation * cont, const char *ahostname, int len, Options const& opt)
00825 {
00826   bool force_dns = false;
00827   EThread *thread = this_ethread();
00828   ProxyMutex *mutex = thread->mutex;
00829 
00830   if (opt.flags & HOSTDB_FORCE_DNS_ALWAYS)
00831     force_dns = true;
00832   else if (opt.flags & HOSTDB_FORCE_DNS_RELOAD) {
00833     force_dns = (hostdb_re_dns_on_reload ? true : false);
00834     if (force_dns)
00835       HOSTDB_INCREMENT_DYN_STAT(hostdb_re_dns_on_reload_stat);
00836   }
00837   return getby(cont, ahostname, len, 0, force_dns, opt.host_res_style, opt.timeout);
00838 }
00839 
00840 
00841 /* Support SRV records */
00842 Action *
00843 HostDBProcessor::getSRVbyname_imm(Continuation * cont, process_srv_info_pfn process_srv_info,
00844                                   const char *hostname, int len, Options const& opt)
00845 {
00846   ink_assert(cont->mutex->thread_holding == this_ethread());
00847   bool force_dns = false;
00848   EThread *thread = cont->mutex->thread_holding;
00849   ProxyMutex *mutex = thread->mutex;
00850 
00851   if (opt.flags & HOSTDB_FORCE_DNS_ALWAYS)
00852     force_dns = true;
00853   else if (opt.flags & HOSTDB_FORCE_DNS_RELOAD) {
00854     force_dns = (hostdb_re_dns_on_reload ? true : false);
00855     if (force_dns)
00856       HOSTDB_INCREMENT_DYN_STAT(hostdb_re_dns_on_reload_stat);
00857   }
00858 
00859   HostDBMD5 md5;
00860 
00861   HOSTDB_INCREMENT_DYN_STAT(hostdb_total_lookups_stat);
00862 
00863   if (!hostdb_enable || !*hostname) {
00864     (cont->*process_srv_info) (NULL);
00865     return ACTION_RESULT_DONE;
00866   }
00867 
00868   md5.host_name = hostname;
00869   md5.host_len = hostname ? (len ? len : strlen(hostname)) : 0;
00870   md5.port = 0;
00871   md5.db_mark = HOSTDB_MARK_SRV;
00872   md5.refresh();
00873 
00874   // Attempt to find the result in-line, for level 1 hits
00875   if (!force_dns) {
00876     // find the partition lock
00877     ProxyMutex *bucket_mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
00878     MUTEX_TRY_LOCK(lock, bucket_mutex, thread);
00879 
00880     // If we can get the lock and a level 1 probe succeeds, return
00881     if (lock) {
00882       HostDBInfo *r = probe(bucket_mutex, md5, false);
00883       if (r) {
00884         Debug("hostdb", "immediate SRV answer for %s from hostdb", hostname);
00885         Debug("dns_srv", "immediate SRV answer for %s from hostdb", hostname);
00886         HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
00887         (cont->*process_srv_info) (r);
00888         return ACTION_RESULT_DONE;
00889       }
00890     }
00891   }
00892 
00893   Debug("dns_srv", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, md5.host_len, md5.host_name, opt.timeout);
00894 
00895   // Otherwise, create a continuation to do a deeper probe in the background
00896   HostDBContinuation *c = hostDBContAllocator.alloc();
00897   HostDBContinuation::Options copt;
00898   copt.timeout = opt.timeout;
00899   copt.cont = cont;
00900   copt.force_dns = force_dns;
00901   c->init(md5, copt);
00902   SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::probeEvent);
00903 
00904   if (thread->mutex == cont->mutex) {
00905     thread->schedule_in(c, MUTEX_RETRY_DELAY);
00906   } else {
00907     dnsProcessor.thread->schedule_imm(c);
00908   }
00909 
00910   return &c->action;
00911 }
00912 
00913 
00914 // Wrapper from getbyname to getby
00915 //
00916 Action *
00917 HostDBProcessor::getbyname_imm(Continuation * cont, process_hostdb_info_pfn process_hostdb_info,
00918                                const char *hostname, int len, Options const& opt)
00919 {
00920   ink_assert(cont->mutex->thread_holding == this_ethread());
00921   bool force_dns = false;
00922   EThread *thread = cont->mutex->thread_holding;
00923   ProxyMutex *mutex = thread->mutex;
00924   HostDBMD5 md5;
00925 
00926   if (opt.flags & HOSTDB_FORCE_DNS_ALWAYS)
00927     force_dns = true;
00928   else if (opt.flags & HOSTDB_FORCE_DNS_RELOAD) {
00929     force_dns = (hostdb_re_dns_on_reload ? true : false);
00930     if (force_dns)
00931       HOSTDB_INCREMENT_DYN_STAT(hostdb_re_dns_on_reload_stat);
00932   }
00933 
00934   HOSTDB_INCREMENT_DYN_STAT(hostdb_total_lookups_stat);
00935 
00936   if (!hostdb_enable || !*hostname) {
00937     (cont->*process_hostdb_info) (NULL);
00938     return ACTION_RESULT_DONE;
00939   }
00940 
00941   md5.host_name = hostname;
00942   md5.host_len = hostname ? (len ? len : strlen(hostname)) : 0;
00943   md5.port = opt.port;
00944   md5.db_mark = db_mark_for(opt.host_res_style);
00945 #ifdef SPLIT_DNS
00946   if (SplitDNSConfig::isSplitDNSEnabled()) {
00947     const char *scan = hostname;
00948     for (; *scan != '\0' && (ParseRules::is_digit(*scan) || '.' == *scan); scan++);
00949     if ('\0' != *scan) {
00950       SplitDNS* pSD = SplitDNSConfig::acquire();
00951       if (0 != pSD)
00952         md5.dns_server = static_cast<DNSServer*>(pSD->getDNSRecord(md5.host_name));
00953       SplitDNSConfig::release(pSD);
00954     }
00955   }
00956 #endif // SPLIT_DNS
00957   md5.refresh();
00958 
00959   // Attempt to find the result in-line, for level 1 hits
00960   if (!force_dns) {
00961     bool loop;
00962     do {
00963       loop = false; // loop only on explicit set for retry
00964       // find the partition lock
00965       ProxyMutex *bucket_mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
00966       MUTEX_LOCK(lock, bucket_mutex, thread);
00967 
00968       if (lock) {
00969         // If we can get the lock do a level 1 probe for immediate result.
00970         HostDBInfo *r = probe(bucket_mutex, md5, false);
00971         if (r) {
00972           if (r->failed()) // fail, see if we should retry with alternate
00973             loop = check_for_retry(md5.db_mark, opt.host_res_style);
00974           if (!loop) {
00975             // No retry -> final result. Return it.
00976             Debug("hostdb", "immediate answer for %.*s", md5.host_len, md5.host_name);
00977             HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
00978             (cont->*process_hostdb_info) (r);
00979             return ACTION_RESULT_DONE;
00980           }
00981           md5.refresh(); // Update for retry.
00982         }
00983       }
00984     } while (loop);
00985   }
00986 
00987   Debug("hostdb", "delaying force %d answer for %.*s [timeout %d]", force_dns, md5.host_len, md5.host_name, opt.timeout);
00988 
00989   // Otherwise, create a continuation to do a deeper probe in the background
00990   HostDBContinuation *c = hostDBContAllocator.alloc();
00991   HostDBContinuation::Options copt;
00992   copt.cont = cont;
00993   copt.force_dns = force_dns;
00994   copt.timeout = opt.timeout;
00995   copt.host_res_style = opt.host_res_style;
00996   c->init(md5, copt);
00997   SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::probeEvent);
00998 
00999   thread->schedule_in(c, HOST_DB_RETRY_PERIOD);
01000 
01001   return &c->action;
01002 }
01003 
01004 
01005 static void
01006 do_setby(HostDBInfo * r, HostDBApplicationInfo * app, const char *hostname, IpAddr const& ip, bool is_srv = false)
01007 {
01008   HostDBRoundRobin *rr = r->rr();
01009 
01010   if (is_srv && (!r->is_srv || !rr))
01011     return;
01012 
01013   if (rr) {
01014     if (is_srv) {
01015       uint32_t key = makeHostHash(hostname);
01016       for (int i = 0; i < rr->rrcount; i++) {
01017         if (key == rr->info[i].data.srv.key && !strcmp(hostname, rr->info[i].srvname(rr))) {
01018           Debug("hostdb", "immediate setby for %s", hostname);
01019           rr->info[i].app.allotment.application1 = app->allotment.application1;
01020           rr->info[i].app.allotment.application2 = app->allotment.application2;
01021           return;
01022         }
01023       }
01024     } else
01025       for (int i = 0; i < rr->rrcount; i++) {
01026         if (rr->info[i].ip() == ip) {
01027           Debug("hostdb", "immediate setby for %s", hostname ? hostname : "<addr>");
01028           rr->info[i].app.allotment.application1 = app->allotment.application1;
01029           rr->info[i].app.allotment.application2 = app->allotment.application2;
01030           return;
01031         }
01032       }
01033   } else {
01034     if (r->reverse_dns || (!r->round_robin && ip == r->ip())) {
01035       Debug("hostdb", "immediate setby for %s", hostname ? hostname : "<addr>");
01036       r->app.allotment.application1 = app->allotment.application1;
01037       r->app.allotment.application2 = app->allotment.application2;
01038     }
01039   }
01040 }
01041 
01042 
01043 void
01044 HostDBProcessor::setby(const char *hostname, int len, sockaddr const* ip, HostDBApplicationInfo * app)
01045 {
01046   if (!hostdb_enable)
01047     return;
01048 
01049   HostDBMD5 md5;
01050   md5.host_name = hostname;
01051   md5.host_len = hostname ? (len ? len : strlen(hostname)) : 0;
01052   md5.ip.assign(ip);
01053   md5.port = ip ? ats_ip_port_host_order(ip) : 0;
01054   md5.db_mark = db_mark_for(ip);
01055   md5.refresh();
01056 
01057   // Attempt to find the result in-line, for level 1 hits
01058 
01059   ProxyMutex *mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
01060   EThread *thread = this_ethread();
01061   MUTEX_TRY_LOCK(lock, mutex, thread);
01062 
01063   if (lock) {
01064     HostDBInfo *r = probe(mutex, md5, false);
01065     if (r)
01066       do_setby(r, app, hostname, md5.ip);
01067     return;
01068   }
01069   // Create a continuation to do a deaper probe in the background
01070 
01071   HostDBContinuation *c = hostDBContAllocator.alloc();
01072   c->init(md5);
01073   c->app.allotment.application1 = app->allotment.application1;
01074   c->app.allotment.application2 = app->allotment.application2;
01075   SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::setbyEvent);
01076   thread->schedule_in(c, MUTEX_RETRY_DELAY);
01077 }
01078 
01079 void
01080 HostDBProcessor::setby_srv(const char *hostname, int len, const char *target, HostDBApplicationInfo * app)
01081 {
01082   if (!hostdb_enable || !hostname || !target)
01083       return;
01084 
01085   HostDBMD5 md5;
01086   md5.host_name = hostname;
01087   md5.host_len = len ? len : strlen(hostname);
01088   md5.port = 0;
01089   md5.db_mark = HOSTDB_MARK_SRV;
01090   md5.refresh();
01091 
01092   // Create a continuation to do a deaper probe in the background
01093 
01094   HostDBContinuation *c = hostDBContAllocator.alloc();
01095   c->init(md5);
01096   ink_strlcpy(c->srv_target_name, target, MAXDNAME);
01097   c->app.allotment.application1 = app->allotment.application1;
01098   c->app.allotment.application2 = app->allotment.application2;
01099   SET_CONTINUATION_HANDLER(c,
01100       (HostDBContHandler) & HostDBContinuation::setbyEvent);
01101   eventProcessor.schedule_imm(c);
01102 }
01103 int
01104 HostDBContinuation::setbyEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
01105 {
01106   HostDBInfo *r = probe(mutex, md5, false);
01107 
01108   if (r)
01109     do_setby(r, &app, md5.host_name, md5.ip, is_srv());
01110 
01111   hostdb_cont_free(this);
01112   return EVENT_DONE;
01113 }
01114 
01115 
01116 static int
01117 remove_round_robin(HostDBInfo * r, const char *hostname, IpAddr const& ip)
01118 {
01119   if (r) {
01120     if (!r->round_robin)
01121       return false;
01122     HostDBRoundRobin *rr = r->rr();
01123     if (!rr)
01124       return false;
01125     for (int i = 0; i < rr->good; i++) {
01126       if (ip == rr->info[i].ip()) {
01127         ip_text_buffer b;
01128         Debug("hostdb", "Deleting %s from '%s' round robin DNS entry", ip.toString(b, sizeof b), hostname);
01129         HostDBInfo tmp = rr->info[i];
01130         rr->info[i] = rr->info[rr->good - 1];
01131         rr->info[rr->good - 1] = tmp;
01132         rr->good--;
01133         if (rr->good <= 0) {
01134           hostDB.delete_block(r);
01135           return false;
01136         } else {
01137           if (diags->on("hostdb")) {
01138             int bufsize = rr->good * INET6_ADDRSTRLEN;
01139             char *rr_ip_list = (char *) alloca(bufsize);
01140             char *p = rr_ip_list;
01141             for (int n = 0; n < rr->good; ++n) {
01142               ats_ip_ntop(rr->info[n].ip(), p, bufsize);
01143               int nbytes = strlen(p);
01144               p += nbytes;
01145               bufsize -= nbytes;
01146             }
01147             Note("'%s' round robin DNS entry updated, entries=%d, IP list: %s", hostname, rr->good, rr_ip_list);
01148           }
01149         }
01150         return true;
01151       }
01152     }
01153   }
01154   return false;
01155 }
01156 
01157 # if 0
01158 Action *
01159 HostDBProcessor::failed_connect_on_ip_for_name(Continuation * cont, sockaddr const* ip, const char *hostname, int len)
01160 {
01161   HostDBMD5 md5;
01162   md5.host_name = hostname;
01163   md5.host_len = hostname ? (len ? len : strlen(hostname)) : 0;
01164   md5.ip.assign(ip);
01165   md5.port = ip ? ats_ip_port_host_order(ip) : 0;
01166   md5.db_mark = db_mark_for(ip);
01167 #ifdef SPLIT_DNS
01168   SplitDNS *pSD = 0;
01169   if (hostname && SplitDNSConfig::isSplitDNSEnabled()) {
01170     pSD = SplitDNSConfig::acquire();
01171 
01172     if (0 != pSD)
01173       md5.dns_server = static_cast<DNSServer*>(pSD->getDNSRecord(hostname));
01174     SplitDNSConfig::release(pSD);
01175   }
01176 #endif // SPLIT_DNS
01177   md5.refresh();
01178 
01179   ProxyMutex *mutex = hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets));
01180   EThread *thread = this_ethread();
01181   MUTEX_TRY_LOCK(lock, mutex, thread);
01182   if (lock) {
01183     if (!hostdb_enable || NULL == md5.dns_server) {
01184       if (cont)
01185         cont->handleEvent(EVENT_HOST_DB_IP_REMOVED, (void *) NULL);
01186       return ACTION_RESULT_DONE;
01187     }
01188     HostDBInfo *r = probe(mutex, md5, false);
01189     bool res = (remove_round_robin(r, hostname, ip) ? true : false);
01190     if (cont)
01191       cont->handleEvent(EVENT_HOST_DB_IP_REMOVED, res ? (void *) ip : (void *) NULL);
01192     return ACTION_RESULT_DONE;
01193   }
01194   HostDBContinuation *c = hostDBContAllocator.alloc();
01195   HostDBContinuation::Options copt;
01196   copt.cont = cont;
01197   c->init(md5, copt);
01198   SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::removeEvent);
01199   thread->schedule_in(c, MUTEX_RETRY_DELAY);
01200   return &c->action;
01201 }
01202 #endif
01203 
01204 int
01205 HostDBContinuation::removeEvent(int /* event ATS_UNUSED */, Event * e)
01206 {
01207   Continuation *cont = action.continuation;
01208 
01209   MUTEX_TRY_LOCK(lock, cont ? (ProxyMutex *) cont->mutex : (ProxyMutex *) NULL, e->ethread);
01210   if (!lock) {
01211     e->schedule_in(HOST_DB_RETRY_PERIOD);
01212     return EVENT_CONT;
01213   }
01214   if (!action.cancelled) {
01215     if (!hostdb_enable) {
01216       if (cont)
01217         cont->handleEvent(EVENT_HOST_DB_IP_REMOVED, (void *) NULL);
01218     } else {
01219       HostDBInfo *r = probe(mutex, md5, false);
01220       bool res = (remove_round_robin(r, md5.host_name, md5.ip) ? true : false);
01221       if (cont)
01222         cont->handleEvent(
01223           EVENT_HOST_DB_IP_REMOVED,
01224           res ? static_cast<void *>(&md5.ip) : static_cast<void *>(NULL)
01225         );
01226     }
01227   }
01228   hostdb_cont_free(this);
01229   return EVENT_DONE;
01230 }
01231 
01232 
01233 // Lookup done, insert into the local table, return data to the
01234 // calling continuation or to the calling cluster node.
01235 //
01236 HostDBInfo *
01237 HostDBContinuation::lookup_done(IpAddr const& ip, char const* aname, bool around_robin, unsigned int ttl_seconds, SRVHosts * srv)
01238 {
01239   HostDBInfo *i = NULL;
01240 
01241   ink_assert(this_ethread() == hostDB.lock_for_bucket((int) (fold_md5(md5.hash) % hostDB.buckets))->thread_holding);
01242   if (!ip.isValid() || !aname || !aname[0]) {
01243     if (is_byname()) {
01244       Debug("hostdb", "lookup_done() failed for '%.*s'", md5.host_len, md5.host_name);
01245     } else if (is_srv()) {
01246       Debug("dns_srv", "SRV failed for '%.*s'", md5.host_len, md5.host_name);
01247     } else {
01248       ip_text_buffer b;
01249       Debug("hostdb", "failed for %s", md5.ip.toString(b, sizeof b));
01250     }
01251     i = insert(hostdb_ip_fail_timeout_interval);        // currently ... 0
01252     i->round_robin = false;
01253     i->is_srv = is_srv();
01254     i->reverse_dns = !is_byname() && !is_srv();
01255 
01256     i->set_failed();
01257   } else {
01258     switch (hostdb_ttl_mode) {
01259     default:
01260       ink_assert(!"bad TTL mode");
01261     case TTL_OBEY:
01262       break;
01263     case TTL_IGNORE:
01264       ttl_seconds = hostdb_ip_timeout_interval * 60;
01265       break;
01266     case TTL_MIN:
01267       if (hostdb_ip_timeout_interval * 60 < ttl_seconds)
01268         ttl_seconds = hostdb_ip_timeout_interval * 60;
01269       break;
01270     case TTL_MAX:
01271       if (hostdb_ip_timeout_interval * 60 > ttl_seconds)
01272         ttl_seconds = hostdb_ip_timeout_interval * 60;
01273       break;
01274     }
01275     HOSTDB_SUM_DYN_STAT(hostdb_ttl_stat, ttl_seconds);
01276     if (!ttl_seconds)
01277       ttl_seconds = 1;          // www.barnsandnobel.com is lame
01278     i = insert(ttl_seconds);
01279     if (is_byname()) {
01280       ip_text_buffer b;
01281       Debug("hostdb", "done %s TTL %d", ip.toString(b, sizeof b), ttl_seconds);
01282       ats_ip_set(i->ip(), ip);
01283       i->round_robin = around_robin;
01284       i->reverse_dns = false;
01285       if (md5.host_name != aname) {
01286         ink_strlcpy(md5_host_name_store, aname, sizeof(md5_host_name_store));
01287       }
01288       i->is_srv = false;
01289     } else if (is_srv()) {
01290       ink_assert(srv && srv->srv_host_count > 0 && srv->srv_host_count <= 16 && around_robin);
01291 
01292       i->data.srv.srv_offset = srv->srv_host_count;
01293       i->reverse_dns = false;
01294       i->is_srv = true;
01295       i->round_robin = around_robin;
01296 
01297       if (md5.host_name != aname) {
01298         ink_strlcpy(md5_host_name_store, aname, sizeof(md5_host_name_store));
01299       }
01300 
01301     } else {
01302       Debug("hostdb", "done '%s' TTL %d", aname, ttl_seconds);
01303       const size_t s_size = strlen(aname) + 1;
01304       void *s = hostDB.alloc(&i->data.hostname_offset, s_size);
01305       if (s) {
01306         ink_strlcpy((char *) s, aname, s_size);
01307         i->round_robin = false;
01308         i->reverse_dns = true;
01309         i->is_srv = false;
01310       } else {
01311         ink_assert(!"out of room in hostdb data area");
01312         Warning("out of room in hostdb for reverse DNS data");
01313         hostDB.delete_block(i);
01314         return NULL;
01315       }
01316     }
01317   }
01318   if (from_cont)
01319     do_put_response(from, i, from_cont);
01320   ink_assert(!i->round_robin || !i->reverse_dns);
01321   return i;
01322 }
01323 
01324 
01325 int
01326 HostDBContinuation::dnsPendingEvent(int event, Event * e)
01327 {
01328   ink_assert(this_ethread() == hostDB.lock_for_bucket(fold_md5(md5.hash) % hostDB.buckets)->thread_holding);
01329   if (timeout) {
01330     timeout->cancel(this);
01331     timeout = NULL;
01332   }
01333   if (event == EVENT_INTERVAL) {
01334     // we timed out, return a failure to the user
01335     MUTEX_TRY_LOCK_FOR(lock, action.mutex, ((Event *) e)->ethread, action.continuation);
01336     if (!lock) {
01337       timeout = eventProcessor.schedule_in(this, HOST_DB_RETRY_PERIOD);
01338       return EVENT_CONT;
01339     }
01340     if (!action.cancelled && action.continuation)
01341       action.continuation->handleEvent(EVENT_HOST_DB_LOOKUP, NULL);
01342     hostDB.pending_dns_for_hash(md5.hash).remove(this);
01343     hostdb_cont_free(this);
01344     return EVENT_DONE;
01345   } else {
01346     SET_HANDLER((HostDBContHandler) & HostDBContinuation::probeEvent);
01347     return probeEvent(EVENT_INTERVAL, NULL);
01348   }
01349 }
01350 
01351 static int
01352 restore_info(HostDBInfo * r, HostDBInfo * old_r, HostDBInfo & old_info, HostDBRoundRobin * old_rr_data)
01353 {
01354   if (old_rr_data) {
01355     for (int j = 0; j < old_rr_data->rrcount; j++)
01356       if (ats_ip_addr_eq(old_rr_data->info[j].ip(), r->ip())) {
01357         r->app = old_rr_data->info[j].app;
01358         return true;
01359       }
01360   } else if (old_r)
01361     if (ats_ip_addr_eq(old_info.ip(), r->ip())) {
01362       r->app = old_info.app;
01363       return true;
01364     }
01365   return false;
01366 }
01367 
01368 
01369 // DNS lookup result state
01370 //
01371 int
01372 HostDBContinuation::dnsEvent(int event, HostEnt * e)
01373 {
01374   ink_assert(this_ethread() == hostDB.lock_for_bucket(fold_md5(md5.hash) % hostDB.buckets)->thread_holding);
01375   if (timeout) {
01376     timeout->cancel(this);
01377     timeout = NULL;
01378   }
01379   EThread *thread = mutex->thread_holding;
01380   if (event == EVENT_INTERVAL) {
01381     if (!action.continuation) {
01382       // give up on insert, it has been too long
01383       remove_trigger_pending_dns();
01384       hostdb_cont_free(this);
01385       return EVENT_DONE;
01386     }
01387     MUTEX_TRY_LOCK_FOR(lock, action.mutex, thread, action.continuation);
01388     if (!lock) {
01389       timeout = thread->schedule_in(this, HOST_DB_RETRY_PERIOD);
01390       return EVENT_CONT;
01391     }
01392     // [amc] Callback to client to indicate a failure due to timeout.
01393     // We don't try a different family here because a timeout indicates
01394     // a server issue that won't be fixed by asking for a different
01395     // address family.
01396     if (!action.cancelled && action.continuation)
01397       action.continuation->handleEvent(EVENT_HOST_DB_LOOKUP, NULL);
01398     action = NULL;
01399     // do not exit yet, wait to see if we can insert into DB
01400     timeout = thread->schedule_in(this, HRTIME_SECONDS(hostdb_insert_timeout));
01401     return EVENT_DONE;
01402   } else {
01403     bool failed = !e;
01404 
01405     bool rr = false;
01406     pending_action = NULL;
01407 
01408     if (is_srv()) {
01409       rr = !failed && (e->srv_hosts.srv_host_count > 0);
01410     } else if (!failed) {
01411       rr = 0 != e->ent.h_addr_list[1];
01412     } else {
01413     }
01414 
01415     ttl = failed ? 0 : e->ttl / 60;
01416     int ttl_seconds = failed ? 0 : e->ttl; //ebalsa: moving to second accuracy
01417 
01418     HostDBInfo *old_r = probe(mutex, md5, true);
01419     HostDBInfo old_info;
01420     if (old_r)
01421       old_info = *old_r;
01422     HostDBRoundRobin *old_rr_data = old_r ? old_r->rr() : NULL;
01423 #ifdef DEBUG
01424     if (old_rr_data) {
01425       for (int i = 0; i < old_rr_data->rrcount; ++i) {
01426         if (old_r->md5_high != old_rr_data->info[i].md5_high ||
01427             old_r->md5_low != old_rr_data->info[i].md5_low ||
01428             old_r->md5_low_low != old_rr_data->info[i].md5_low_low)
01429           ink_assert(0);
01430       }
01431     }
01432 #endif
01433     int n = 0, nn = 0;
01434     void* first = 0;
01435     uint8_t af = e ? e->ent.h_addrtype : AF_UNSPEC; // address family
01436     if (rr) {
01437       if (is_srv() && !failed) {
01438         n = e->srv_hosts.srv_host_count;
01439       } else {
01440         void* ptr; // tmp for current entry.
01441         for (
01442             ; nn < HOST_DB_MAX_ROUND_ROBIN_INFO
01443               && 0 != (ptr = e->ent.h_addr_list[nn])
01444             ; ++nn
01445         ) {
01446           if (is_addr_valid(af, ptr)) {
01447             if (! first) first = ptr;
01448             ++n;
01449           } else {
01450             Warning("Zero address removed from round-robin list for '%s'", md5.host_name);
01451           }
01452           // what's the point of @a n? Should there be something like
01453           // if (n != nn) e->ent.h_addr_list[n] = e->ent->h_addr_list[nn];
01454           // with a final copy of the terminating null? - AMC
01455         }
01456         if (!first) {
01457           failed = true;
01458           rr = false;
01459         }
01460       }
01461     } else if (!failed) {
01462       first = e->ent.h_addr_list[0];
01463     } // else first is 0.
01464 
01465     HostDBInfo *r = NULL;
01466     IpAddr tip; // temp storage if needed.
01467 
01468     if (is_byname()) {
01469       if (first) ip_addr_set(tip, af, first);
01470       r = lookup_done(tip, md5.host_name, rr, ttl_seconds, failed ? 0 : &e->srv_hosts);
01471     } else if (is_srv()) {
01472       if (!failed)
01473         tip._family = AF_INET; // force the tip valid, or else the srv will fail
01474       r = lookup_done(tip,  /* junk: FIXME: is the code in lookup_done() wrong to NEED this? */
01475                       md5.host_name,     /* hostname */
01476                       rr,       /* is round robin, doesnt matter for SRV since we recheck getCount() inside lookup_done() */
01477                       ttl_seconds,      /* ttl in seconds */
01478                       failed ? 0 : &e->srv_hosts);
01479     } else if (failed) {
01480       r = lookup_done(tip, md5.host_name, false, ttl_seconds, 0);
01481     } else {
01482       r = lookup_done(md5.ip, e->ent.h_name, false, ttl_seconds, &e->srv_hosts);
01483     }
01484 
01485     // @c lookup_done should always return a valid value so @a r should be null @c NULL.
01486     ink_assert(r && r->app.allotment.application1 == 0 && r->app.allotment.application2 == 0);
01487 
01488     if (rr) {
01489       const int rrsize = HostDBRoundRobin::size(n, e->srv_hosts.srv_hosts_length);
01490       HostDBRoundRobin *rr_data = (HostDBRoundRobin *) hostDB.alloc(&r->app.rr.offset, rrsize);
01491 
01492       Debug("hostdb", "allocating %d bytes for %d RR at %p %d", rrsize, n, rr_data, r->app.rr.offset);
01493 
01494       if (rr_data) {
01495         rr_data->length = rrsize;
01496         int i = 0, ii = 0;
01497         if (is_srv()) {
01498           int skip = 0;
01499           char *pos = (char *) rr_data + sizeof(HostDBRoundRobin) + n * sizeof(HostDBInfo);
01500           SRV *q[HOST_DB_MAX_ROUND_ROBIN_INFO];
01501           ink_assert(n <= HOST_DB_MAX_ROUND_ROBIN_INFO);
01502           // sort
01503           for (i = 0; i < n; ++i) {
01504             q[i] = &e->srv_hosts.hosts[i];
01505           }
01506           for (i = 0; i < n; ++i) {
01507             for (ii = i + 1; ii < n; ++ii) {
01508               if (*q[ii] < *q[i]) {
01509                 SRV *tmp = q[i];
01510                 q[i] = q[ii];
01511                 q[ii] = tmp;
01512               }
01513             }
01514           }
01515 
01516           for (i = 0; i < n; ++i) {
01517             SRV *t = q[i];
01518             HostDBInfo& item = rr_data->info[i];
01519 
01520             memset(&item, 0, sizeof(item));
01521             item.round_robin = 0;
01522             item.reverse_dns = 0;
01523             item.is_srv = 1;
01524             item.data.srv.srv_weight = t->weight;
01525             item.data.srv.srv_priority = t->priority;
01526             item.data.srv.srv_port = t->port;
01527             item.data.srv.key = t->key;
01528 
01529             ink_assert((skip + t->host_len) <= e->srv_hosts.srv_hosts_length);
01530 
01531             memcpy(pos + skip, t->host, t->host_len);
01532             item.data.srv.srv_offset = (pos - (char *) rr_data) + skip;
01533 
01534             skip += t->host_len;
01535 
01536             item.md5_high = r->md5_high;
01537             item.md5_low = r->md5_low;
01538             item.md5_low_low = r->md5_low_low;
01539             item.full = 1;
01540 
01541             item.app.allotment.application1 = 0;
01542             item.app.allotment.application2 = 0;
01543             Debug("dns_srv", "inserted SRV RR record [%s] into HostDB with TTL: %d seconds", t->host, ttl_seconds);
01544           }
01545           rr_data->good = rr_data->rrcount = n;
01546           rr_data->current = 0;
01547 
01548           // restore
01549           if (old_rr_data) {
01550             for (i = 0; i < rr_data->rrcount; ++i) {
01551               for (ii = 0; ii < old_rr_data->rrcount; ++ii) {
01552                 if (rr_data->info[i].data.srv.key == old_rr_data->info[ii].data.srv.key) {
01553                   char *new_host = rr_data->info[i].srvname(rr_data);
01554                   char *old_host = old_rr_data->info[ii].srvname(old_rr_data);
01555                   if (!strcmp(new_host, old_host))
01556                     rr_data->info[i].app = old_rr_data->info[ii].app;
01557                 }
01558               }
01559             }
01560           }
01561         } else {
01562           for (ii = 0; ii < nn; ++ii) {
01563             if (is_addr_valid(af, e->ent.h_addr_list[ii])) {
01564               HostDBInfo& item = rr_data->info[i];
01565               ip_addr_set(item.ip(), af, e->ent.h_addr_list[ii]);
01566 //              rr_data->info[i].ip() = *(unsigned int *) e->ent.h_addr_list[ii];
01567               item.full = 1;
01568               item.round_robin = 0;
01569               item.reverse_dns = 0;
01570               item.is_srv = 0;
01571               item.md5_high = r->md5_high;
01572               item.md5_low = r->md5_low;
01573               item.md5_low_low = r->md5_low_low;
01574               if (!restore_info(&item, old_r, old_info, old_rr_data)) {
01575                 item.app.allotment.application1 = 0;
01576                 item.app.allotment.application2 = 0;
01577               }
01578               ++i;
01579             }
01580           }
01581           rr_data->good = rr_data->rrcount = n;
01582           rr_data->current = 0;
01583         }
01584       } else {
01585         ink_assert(!"out of room in hostdb data area");
01586         Warning("out of room in hostdb for round-robin DNS data");
01587         r->round_robin = 0;
01588       }
01589     }
01590     if (!failed && !rr && !is_srv())
01591       restore_info(r, old_r, old_info, old_rr_data);
01592     ink_assert(!r || !r->round_robin || !r->reverse_dns);
01593     ink_assert(failed || !r->round_robin || r->app.rr.offset);
01594 
01595     // if we are not the owner, put on the owner
01596     //
01597     ClusterMachine *m = cluster_machine_at_depth(master_hash(md5.hash));
01598     if (m)
01599       do_put_response(m, r, NULL);
01600 
01601     // try to callback the user
01602     //
01603     if (action.continuation) {
01604       // Check for IP family failover
01605       if (failed && check_for_retry(md5.db_mark, host_res_style)) {
01606         this->refresh_MD5();
01607         SET_CONTINUATION_HANDLER(this, (HostDBContHandler) & HostDBContinuation::probeEvent);
01608         thread->schedule_in(this, MUTEX_RETRY_DELAY);
01609         return EVENT_CONT;
01610       }
01611 
01612       MUTEX_TRY_LOCK_FOR(lock, action.mutex, thread, action.continuation);
01613       if (!lock) {
01614         remove_trigger_pending_dns();
01615         SET_HANDLER((HostDBContHandler) & HostDBContinuation::probeEvent);
01616         thread->schedule_in(this, HOST_DB_RETRY_PERIOD);
01617         return EVENT_CONT;
01618       }
01619       if (!action.cancelled)
01620         reply_to_cont(action.continuation, r, is_srv());
01621     }
01622     // wake up everyone else who is waiting
01623     remove_trigger_pending_dns();
01624 
01625     // all done
01626     //
01627     hostdb_cont_free(this);
01628     return EVENT_DONE;
01629   }
01630 }
01631 
01632 
01633 //
01634 // HostDB Get Message
01635 // Used to lookup host information on a remote node in the cluster
01636 //
01637 struct HostDB_get_message {
01638   INK_MD5 md5;
01639   IpEndpoint ip;
01640   Continuation *cont;
01641   int namelen;
01642   char name[MAXDNAME];
01643 };
01644 
01645 
01646 //
01647 // Make a get message
01648 //
01649 int
01650 HostDBContinuation::make_get_message(char *buf, int size)
01651 {
01652   ink_assert(size >= (int) sizeof(HostDB_get_message));
01653 
01654   HostDB_get_message *msg = reinterpret_cast<HostDB_get_message *>(buf);
01655   msg->md5 = md5.hash;
01656   ats_ip_set(&msg->ip.sa, md5.ip, htons(md5.port));
01657   msg->cont = this;
01658 
01659   // name
01660   ink_strlcpy(msg->name, md5.host_name, sizeof(msg->name));
01661 
01662   // length
01663   int len = sizeof(HostDB_get_message) - MAXDNAME + md5.host_len + 1;
01664 
01665   return len;
01666 }
01667 
01668 
01669 //
01670 // Make and send a get message
01671 //
01672 bool HostDBContinuation::do_get_response(Event * /* e ATS_UNUSED */)
01673 {
01674   if (!hostdb_cluster)
01675     return false;
01676 
01677   // find an appropriate Machine
01678   //
01679   ClusterMachine *m = NULL;
01680 
01681   if (hostdb_migrate_on_demand) {
01682     m = cluster_machine_at_depth(master_hash(md5.hash), &probe_depth, past_probes);
01683   } else {
01684     if (probe_depth)
01685       return false;
01686     m = cluster_machine_at_depth(master_hash(md5.hash));
01687     probe_depth = 1;
01688   }
01689 
01690   if (!m)
01691     return false;
01692 
01693   // Make message
01694   //
01695   HostDB_get_message msg;
01696 
01697   memset(&msg, 0, sizeof(msg));
01698   int len = make_get_message((char *) &msg, sizeof(HostDB_get_message));
01699 
01700   // Setup this continuation, with a timeout
01701   //
01702   remoteHostDBQueue[key_partition()].enqueue(this);
01703   SET_HANDLER((HostDBContHandler) & HostDBContinuation::clusterEvent);
01704   timeout = mutex->thread_holding->schedule_in(this, HOST_DB_CLUSTER_TIMEOUT);
01705 
01706   // Send the message
01707   //
01708   clusterProcessor.invoke_remote(m->pop_ClusterHandler(), GET_HOSTINFO_CLUSTER_FUNCTION, (char *) &msg, len);
01709 
01710   return true;
01711 }
01712 
01713 
01714 //
01715 // HostDB Put Message
01716 // This message is used in a response to a cluster node for
01717 // Host inforamation.
01718 //
01719 struct HostDB_put_message {
01720   INK_MD5 md5;
01721   IpEndpoint ip;
01722   unsigned int ttl;
01723   unsigned int missing:1;
01724   unsigned int round_robin:1;
01725   Continuation* cont;
01726   unsigned int application1;
01727   unsigned int application2;
01728   int namelen;
01729   char name[MAXDNAME];
01730 };
01731 
01732 
01733 //
01734 // Build the put message
01735 //
01736 int
01737 HostDBContinuation::make_put_message(HostDBInfo * r, Continuation * c, char *buf, int size)
01738 {
01739   ink_assert(size >= (int) sizeof(HostDB_put_message));
01740 
01741   HostDB_put_message *msg = reinterpret_cast<HostDB_put_message *>(buf);
01742   memset(msg, 0, sizeof(HostDB_put_message));
01743 
01744   msg->md5 = md5.hash;
01745   msg->cont = c;
01746   if (r) {
01747     ats_ip_copy(&msg->ip.sa, r->ip());
01748     msg->application1 = r->app.allotment.application1;
01749     msg->application2 = r->app.allotment.application2;
01750     msg->missing = false;
01751     msg->round_robin = r->round_robin;
01752     msg->ttl = r->ip_time_remaining();
01753   } else {
01754     msg->missing = true;
01755   }
01756 
01757   // name
01758   ink_strlcpy(msg->name, md5.host_name, sizeof(msg->name));
01759 
01760   // length
01761   int len = sizeof(HostDB_put_message) - MAXDNAME + md5.host_len + 1;
01762 
01763   return len;
01764 }
01765 
01766 
01767 //
01768 // Build the put message and send it
01769 //
01770 void
01771 HostDBContinuation::do_put_response(ClusterMachine * m, HostDBInfo * r, Continuation * c)
01772 {
01773   // don't remote fill round-robin DNS entries
01774   // if configured not to cluster them
01775   if (!hostdb_cluster || (!c && r->round_robin && !hostdb_cluster_round_robin))
01776     return;
01777 
01778   HostDB_put_message msg;
01779   int len = make_put_message(r, c, (char *) &msg, sizeof(HostDB_put_message));
01780 
01781   clusterProcessor.invoke_remote(m->pop_ClusterHandler(), PUT_HOSTINFO_CLUSTER_FUNCTION, (char *) &msg, len);
01782 
01783 }
01784 
01785 
01786 //
01787 // Probe state
01788 //
01789 int
01790 HostDBContinuation::probeEvent(int /* event ATS_UNUSED */, Event * e)
01791 {
01792   ink_assert(!link.prev && !link.next);
01793   EThread *t = e ? e->ethread : this_ethread();
01794 
01795   MUTEX_TRY_LOCK_FOR(lock, action.mutex, t, action.continuation);
01796   if (!lock) {
01797     mutex->thread_holding->schedule_in(this, HOST_DB_RETRY_PERIOD);
01798     return EVENT_CONT;
01799   }
01800 
01801   if (action.cancelled) {
01802     hostdb_cont_free(this);
01803     return EVENT_DONE;
01804   }
01805 
01806   if (!hostdb_enable || (!*md5.host_name && !md5.ip.isValid())) {
01807     if (action.continuation)
01808       action.continuation->handleEvent(EVENT_HOST_DB_LOOKUP, NULL);
01809     if (from)
01810       do_put_response(from, 0, from_cont);
01811     hostdb_cont_free(this);
01812     return EVENT_DONE;
01813   }
01814 
01815   if (!force_dns) {
01816 
01817     // Do the probe
01818     //
01819     HostDBInfo *r = probe(mutex, md5, false);
01820 
01821     if (r)
01822       HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
01823 
01824     if (action.continuation && r)
01825       reply_to_cont(action.continuation, r);
01826 
01827     // Respond to any remote node
01828     //
01829     if (from)
01830       do_put_response(from, r, from_cont);
01831 
01832     // If it suceeds or it was a remote probe, we are done
01833     //
01834     if (r || from) {
01835       hostdb_cont_free(this);
01836       return EVENT_DONE;
01837     }
01838     // If it failed, do a remote probe
01839     //
01840     if (do_get_response(e))
01841       return EVENT_CONT;
01842   }
01843   // If there are no remote nodes to probe, do a DNS lookup
01844   //
01845   do_dns();
01846   return EVENT_DONE;
01847 }
01848 
01849 
01850 int
01851 HostDBContinuation::set_check_pending_dns()
01852 {
01853   Queue<HostDBContinuation> &q = hostDB.pending_dns_for_hash(md5.hash);
01854   HostDBContinuation *c = q.head;
01855   for (; c; c = (HostDBContinuation *) c->link.next) {
01856     if (md5.hash == c->md5.hash) {
01857       Debug("hostdb", "enqueuing additional request");
01858       q.enqueue(this);
01859       return false;
01860     }
01861   }
01862   q.enqueue(this);
01863   return true;
01864 }
01865 
01866 
01867 void
01868 HostDBContinuation::remove_trigger_pending_dns()
01869 {
01870   Queue<HostDBContinuation> &q = hostDB.pending_dns_for_hash(md5.hash);
01871   q.remove(this);
01872   HostDBContinuation *c = q.head;
01873   Queue<HostDBContinuation> qq;
01874   while (c) {
01875     HostDBContinuation *n = (HostDBContinuation *) c->link.next;
01876     if (md5.hash == c->md5.hash) {
01877       Debug("hostdb", "dequeuing additional request");
01878       q.remove(c);
01879       qq.enqueue(c);
01880     }
01881     c = n;
01882   }
01883   while ((c = qq.dequeue()))
01884     c->handleEvent(EVENT_IMMEDIATE, NULL);
01885 }
01886 
01887 
01888 //
01889 // Query the DNS processor
01890 //
01891 void
01892 HostDBContinuation::do_dns()
01893 {
01894   ink_assert(!action.cancelled);
01895   if (is_byname()) {
01896     Debug("hostdb", "DNS %s", md5.host_name);
01897     IpAddr tip;
01898     if (0 == tip.load(md5.host_name)) {
01899       // check 127.0.0.1 format // What the heck does that mean? - AMC
01900       if (action.continuation) {
01901         HostDBInfo *r = lookup_done(tip, md5.host_name, false, HOST_DB_MAX_TTL, NULL);
01902         reply_to_cont(action.continuation, r);
01903       }
01904       hostdb_cont_free(this);
01905       return;
01906     }
01907   }
01908   if (hostdb_lookup_timeout)
01909     timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout));
01910   else
01911     timeout = NULL;
01912   if (set_check_pending_dns()) {
01913     DNSProcessor::Options opt;
01914     opt.timeout = dns_lookup_timeout;
01915     opt.host_res_style = host_res_style_for(md5.db_mark);
01916     SET_HANDLER((HostDBContHandler) & HostDBContinuation::dnsEvent);
01917     if (is_byname()) {
01918       if (md5.dns_server)
01919         opt.handler = md5.dns_server->x_dnsH;
01920       pending_action = dnsProcessor.gethostbyname(this, md5.host_name, opt);
01921     } else if (is_srv()) {
01922       Debug("dns_srv", "SRV lookup of %s", md5.host_name);
01923       pending_action = dnsProcessor.getSRVbyname(this, md5.host_name, opt);
01924     } else {
01925       ip_text_buffer ipb;
01926       Debug("hostdb", "DNS IP %s", md5.ip.toString(ipb, sizeof ipb));
01927       pending_action = dnsProcessor.gethostbyaddr(this, &md5.ip, opt);
01928     }
01929   } else {
01930     SET_HANDLER((HostDBContHandler) & HostDBContinuation::dnsPendingEvent);
01931   }
01932 }
01933 
01934 
01935 //
01936 // Handle the response (put message)
01937 //
01938 int
01939 HostDBContinuation::clusterResponseEvent(int/*  event ATS_UNUSED */, Event * e)
01940 {
01941   if (from_cont) {
01942     HostDBContinuation *c;
01943     for (c = (HostDBContinuation *) remoteHostDBQueue[key_partition()].head; c; c = (HostDBContinuation *) c->link.next)
01944       if (c == from_cont)
01945         break;
01946 
01947     // Check to see that we have not already timed out
01948     //
01949     if (c) {
01950       action = c;
01951       from_cont = 0;
01952       MUTEX_TRY_LOCK(lock, c->mutex, e->ethread);
01953       MUTEX_TRY_LOCK(lock2, c->action.mutex, e->ethread);
01954       if (!lock || !lock2) {
01955         e->schedule_in(HOST_DB_RETRY_PERIOD);
01956         return EVENT_CONT;
01957       }
01958       bool failed = missing || (round_robin && !hostdb_cluster_round_robin);
01959       action.continuation->handleEvent(EVENT_HOST_DB_GET_RESPONSE, failed ? 0 : this);
01960     }
01961   } else {
01962     action = 0;
01963     // just a remote fill
01964     ink_assert(!missing);
01965     lookup_done(md5.ip, md5.host_name, false, ttl, NULL);
01966   }
01967   hostdb_cont_free(this);
01968   return EVENT_DONE;
01969 }
01970 
01971 
01972 //
01973 // Wait for the response (put message)
01974 //
01975 int
01976 HostDBContinuation::clusterEvent(int event, Event * e)
01977 {
01978   // remove ourselves from the queue
01979   //
01980   remoteHostDBQueue[key_partition()].remove(this);
01981 
01982   switch (event) {
01983   default:
01984     ink_assert(!"bad case");
01985     hostdb_cont_free(this);
01986     return EVENT_DONE;
01987 
01988     // handle the put response, e is really a HostDBContinuation *
01989     //
01990   case EVENT_HOST_DB_GET_RESPONSE:
01991     if (timeout) {
01992       timeout->cancel(this);
01993       timeout = NULL;
01994     }
01995     if (e) {
01996       HostDBContinuation *c = (HostDBContinuation *) e;
01997       HostDBInfo *r = lookup_done(md5.ip, c->md5.host_name, false, c->ttl, NULL);
01998       r->app.allotment.application1 = c->app.allotment.application1;
01999       r->app.allotment.application2 = c->app.allotment.application2;
02000 
02001       HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
02002 
02003       if (!action.cancelled) {
02004         if (reply_to_cont(action.continuation, r)) {
02005           // if we are not the owner and neither was the sender,
02006           // fill the owner
02007           //
02008           if (hostdb_migrate_on_demand) {
02009             ClusterMachine *m = cluster_machine_at_depth(master_hash(md5.hash));
02010             if (m && m != c->from)
02011               do_put_response(m, r, NULL);
02012           }
02013         }
02014       }
02015       hostdb_cont_free(this);
02016       return EVENT_DONE;
02017     }
02018     return failed_cluster_request(e);
02019 
02020     // did not get the put message in time
02021     //
02022   case EVENT_INTERVAL:{
02023       MUTEX_TRY_LOCK_FOR(lock, action.mutex, e->ethread, action.continuation);
02024       if (!lock) {
02025         e->schedule_in(HOST_DB_RETRY_PERIOD);
02026         return EVENT_CONT;
02027       }
02028       return failed_cluster_request(e);
02029     }
02030   }
02031 }
02032 
02033 
02034 int
02035 HostDBContinuation::failed_cluster_request(Event * e)
02036 {
02037   if (action.cancelled) {
02038     hostdb_cont_free(this);
02039     return EVENT_DONE;
02040   }
02041   // Attempt another remote probe
02042   //
02043   if (do_get_response(e))
02044     return EVENT_CONT;
02045 
02046   // Otherwise, do a DNS lookup
02047   //
02048   do_dns();
02049   return EVENT_DONE;
02050 }
02051 
02052 
02053 void
02054 get_hostinfo_ClusterFunction(ClusterHandler *ch, void *data, int /* len ATS_UNUSED */)
02055 {
02056   HostDBMD5 md5;
02057   HostDB_get_message *msg = (HostDB_get_message *) data;
02058 
02059   md5.host_name = msg->name;
02060   md5.host_len = msg->namelen;
02061   md5.ip.assign(&msg->ip.sa);
02062   md5.port = ats_ip_port_host_order(&msg->ip.sa);
02063   md5.hash = msg->md5;
02064   md5.db_mark = db_mark_for(&msg->ip.sa);
02065 #ifdef SPLIT_DNS
02066   SplitDNS *pSD = 0;
02067   char *hostname = msg->name;
02068   if (hostname && SplitDNSConfig::isSplitDNSEnabled()) {
02069     pSD = SplitDNSConfig::acquire();
02070 
02071     if (0 != pSD) {
02072       md5.dns_server = static_cast<DNSServer*>(pSD->getDNSRecord(hostname));
02073     }
02074     SplitDNSConfig::release(pSD);
02075   }
02076 #endif // SPLIT_DNS
02077 
02078   HostDBContinuation *c = hostDBContAllocator.alloc();
02079   HostDBContinuation::Options copt;
02080   SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::probeEvent);
02081   c->from = ch->machine;
02082   c->from_cont = msg->cont;
02083 
02084   /* -----------------------------------------
02085      we make a big assumption here! we presume
02086      that all the machines in the cluster are
02087      set to use the same configuration for
02088      DNS servers
02089      ----------------------------------------- */
02090 
02091   copt.host_res_style = host_res_style_for(&msg->ip.sa);
02092   c->init(md5, copt);
02093   c->mutex = hostDB.lock_for_bucket(fold_md5(msg->md5) % hostDB.buckets);
02094   c->action.mutex = c->mutex;
02095   dnsProcessor.thread->schedule_imm(c);
02096 }
02097 
02098 
02099 void
02100 put_hostinfo_ClusterFunction(ClusterHandler *ch, void *data, int /* len ATS_UNUSED */)
02101 {
02102   HostDB_put_message *msg = (HostDB_put_message *) data;
02103   HostDBContinuation *c = hostDBContAllocator.alloc();
02104   HostDBContinuation::Options copt;
02105   HostDBMD5 md5;
02106 
02107   SET_CONTINUATION_HANDLER(c, (HostDBContHandler) & HostDBContinuation::clusterResponseEvent);
02108   md5.host_name = msg->name;
02109   md5.host_len = msg->namelen;
02110   md5.ip.assign(&msg->ip.sa);
02111   md5.port = ats_ip_port_host_order(&msg->ip.sa);
02112   md5.hash = msg->md5;
02113   md5.db_mark = db_mark_for(&msg->ip.sa);
02114   copt.host_res_style = host_res_style_for(&msg->ip.sa);
02115   c->init(md5, copt);
02116   c->mutex = hostDB.lock_for_bucket(fold_md5(msg->md5) % hostDB.buckets);
02117   c->from_cont = msg->cont;     // cannot use action if cont freed due to timeout
02118   c->missing = msg->missing;
02119   c->round_robin = msg->round_robin;
02120   c->ttl = msg->ttl;
02121   c->from = ch->machine;
02122   dnsProcessor.thread->schedule_imm(c);
02123 }
02124 
02125 
02126 //
02127 // Background event
02128 // Just increment the current_interval.  Might do other stuff
02129 // here, like move records to the current position in the cluster.
02130 //
02131 int
02132 HostDBContinuation::backgroundEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
02133 {
02134   hostdb_current_interval++;
02135 
02136   return EVENT_CONT;
02137 }
02138 
02139 bool HostDBInfo::match(INK_MD5 & md5, int /* bucket ATS_UNUSED */, int buckets)
02140 {
02141   if (md5[1] != md5_high)
02142     return false;
02143 
02144   uint64_t folded_md5 = fold_md5(md5);
02145   uint64_t ttag = folded_md5 / buckets;
02146 
02147   if (!ttag)
02148     ttag = 1;
02149 
02150   struct
02151   {
02152     unsigned int md5_low_low:24;
02153     unsigned int md5_low;
02154   } tmp;
02155 
02156   tmp.md5_low_low = (unsigned int) ttag;
02157   tmp.md5_low = (unsigned int) (ttag >> 24);
02158 
02159   return tmp.md5_low_low == md5_low_low && tmp.md5_low == md5_low;
02160 }
02161 
02162 
02163 char *
02164 HostDBInfo::hostname()
02165 {
02166   if (!reverse_dns)
02167     return NULL;
02168 
02169   return (char *) hostDB.ptr(&data.hostname_offset, hostDB.ptr_to_partition((char *) this));
02170 }
02171 
02172 
02173 HostDBRoundRobin *
02174 HostDBInfo::rr()
02175 {
02176   if (!round_robin)
02177     return NULL;
02178 
02179   HostDBRoundRobin *r = (HostDBRoundRobin *) hostDB.ptr(&app.rr.offset, hostDB.ptr_to_partition((char *) this));
02180 
02181   if (r && (r->rrcount > HOST_DB_MAX_ROUND_ROBIN_INFO || r->rrcount <= 0 || r->good > HOST_DB_MAX_ROUND_ROBIN_INFO || r->good <= 0)) {
02182     ink_assert(!"bad round-robin");
02183     return NULL;
02184   }
02185   return r;
02186 }
02187 
02188 
02189 int
02190 HostDBInfo::heap_size()
02191 {
02192   if (reverse_dns) {
02193     char *h = hostname();
02194 
02195     if (h)
02196       return strlen(h) + 1;
02197   } else if (round_robin) {
02198     HostDBRoundRobin *r = rr();
02199 
02200     if (r)
02201       return r->length;
02202   }
02203   return 0;
02204 }
02205 
02206 
02207 int *
02208 HostDBInfo::heap_offset_ptr()
02209 {
02210   if (reverse_dns)
02211     return &data.hostname_offset;
02212 
02213   if (round_robin)
02214     return &app.rr.offset;
02215 
02216   return NULL;
02217 }
02218 
02219 
02220 ClusterMachine *
02221 HostDBContinuation::master_machine(ClusterConfiguration * cc)
02222 {
02223   return cc->machine_hash((int) (md5.hash[1] >> 32));
02224 }
02225 
02226 struct ShowHostDB;
02227 typedef int (ShowHostDB::*ShowHostDBEventHandler) (int event, Event * data);
02228 struct ShowHostDB: public ShowCont
02229 {
02230   char *name;
02231   IpEndpoint ip;
02232   bool force;
02233 
02234   int showMain(int event, Event * e)
02235   {
02236     CHECK_SHOW(begin("HostDB"));
02237     CHECK_SHOW(show("<form method = GET action = \"./name\">\n"
02238                     "Lookup by name (e.g. trafficserver.apache.org):<br>\n"
02239                     "<input type=text name=name size=64 maxlength=256>\n"
02240                     "</form>\n"
02241                     "<form method = GET action = \"./ip\">\n"
02242                     "Lookup by IP (e.g. 127.0.0.1):<br>\n"
02243                     "<input type=text name=ip size=64 maxlength=256>\n"
02244                     "</form>\n"
02245                     "<form method = GET action = \"./nameforce\">\n"
02246                     "Force DNS by name (e.g. trafficserver.apache.org):<br>\n"
02247                     "<input type=text name=name size=64 maxlength=256>\n" "</form>\n"));
02248     return complete(event, e);
02249   }
02250 
02251 
02252   int showLookup(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
02253   {
02254     SET_HANDLER(&ShowHostDB::showLookupDone);
02255     if (name)
02256       hostDBProcessor.getbyname_re(this, name, 0, HostDBProcessor::Options().setFlags(force ? HostDBProcessor::HOSTDB_FORCE_DNS_ALWAYS : 0));
02257     else
02258       hostDBProcessor.getbyaddr_re(this, &ip.sa);
02259     return EVENT_CONT;
02260   }
02261 
02262 
02263   int showOne(HostDBInfo * r, bool rr, int event, Event * e)
02264   {
02265     ip_text_buffer b;
02266     CHECK_SHOW(show("<table border=1>\n"));
02267     CHECK_SHOW(show("<tr><td>%s</td><td>%s%s</td></tr>\n",
02268                     "Type", r->round_robin ? "Round-Robin" : "", r->reverse_dns ? "Reverse DNS" : "DNS"));
02269     CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "App1", r->app.allotment.application1));
02270     CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "App2", r->app.allotment.application2));
02271     if (!rr) {
02272       CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Stale", r->is_ip_stale()? "Yes" : "No"));
02273       CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Timed-Out", r->is_ip_timeout()? "Yes" : "No"));
02274       CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "TTL", r->ip_time_remaining()));
02275     }
02276     if (r->reverse_dns) {
02277       CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Hostname", r->hostname()? r->hostname() : "<none>"));
02278     } else {
02279       CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "IP", ats_ip_ntop(r->ip(), b, sizeof b)));
02280     }
02281     CHECK_SHOW(show("</table>\n"));
02282     return EVENT_CONT;
02283   }
02284 
02285 
02286   int showLookupDone(int event, Event * e)
02287   {
02288     HostDBInfo *r = (HostDBInfo *) e;
02289 
02290     CHECK_SHOW(begin("HostDB Lookup"));
02291     if (name) {
02292       CHECK_SHOW(show("<H2>%s</H2>\n", name));
02293     } else {
02294       CHECK_SHOW(show("<H2>%u.%u.%u.%u</H2>\n", PRINT_IP(ip)));
02295     }
02296     if (r) {
02297       showOne(r, false, event, e);
02298       if (r->round_robin) {
02299         HostDBRoundRobin *rr_data = r->rr();
02300         if (rr_data) {
02301           CHECK_SHOW(show("<table border=1>\n"));
02302           CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Total", rr_data->rrcount));
02303           CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Good", rr_data->good));
02304           CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Current", rr_data->current));
02305           CHECK_SHOW(show("</table>\n"));
02306 
02307           for (int i = 0; i < rr_data->rrcount; i++)
02308             showOne(&rr_data->info[i], true, event, e);
02309         }
02310       }
02311     } else {
02312       if (name) {
02313         ip_text_buffer b;
02314         CHECK_SHOW(show("<H2>%s Not Found</H2>\n", ats_ip_ntop(&ip.sa, b, sizeof b)));
02315       } else {
02316         CHECK_SHOW(show("<H2>%s Not Found</H2>\n", name));
02317       }
02318     }
02319     return complete(event, e);
02320   }
02321 
02322 
02323   ShowHostDB(Continuation * c, HTTPHdr * h)
02324     : ShowCont(c, h), name(0), force(0)
02325     {
02326       ats_ip_invalidate(&ip);
02327       SET_HANDLER(&ShowHostDB::showMain);
02328     }
02329 
02330 };
02331 
02332 #define STR_LEN_EQ_PREFIX(_x,_l,_s) (!ptr_len_ncasecmp(_x,_l,_s,sizeof(_s)-1))
02333 
02334 
02335 static Action *
02336 register_ShowHostDB(Continuation * c, HTTPHdr * h)
02337 {
02338   ShowHostDB *s = new ShowHostDB(c, h);
02339   int path_len;
02340   const char *path = h->url_get()->path_get(&path_len);
02341 
02342   SET_CONTINUATION_HANDLER(s, &ShowHostDB::showMain);
02343   if (STR_LEN_EQ_PREFIX(path, path_len, "ip")) {
02344     s->force = !ptr_len_ncasecmp(path + 3, path_len - 3, "force", 5);
02345     int query_len;
02346     const char *query = h->url_get()->query_get(&query_len);
02347     s->sarg = ats_strndup(query, query_len);
02348     char *gn = NULL;
02349     if (s->sarg)
02350       gn = (char *)memchr(s->sarg, '=', strlen(s->sarg));
02351     if (gn) {
02352       ats_ip_pton(gn+1, &s->ip); // hope that's null terminated.
02353     }
02354     SET_CONTINUATION_HANDLER(s, &ShowHostDB::showLookup);
02355   } else if (STR_LEN_EQ_PREFIX(path, path_len, "name")) {
02356     s->force = !ptr_len_ncasecmp(path + 5, path_len - 5, "force", 5);
02357     int query_len;
02358     const char *query = h->url_get()->query_get(&query_len);
02359     s->sarg = ats_strndup(query, query_len);
02360     char *gn = NULL;
02361     if (s->sarg)
02362       gn = (char *)memchr(s->sarg, '=', strlen(s->sarg));
02363     if (gn)
02364       s->name = gn + 1;
02365     SET_CONTINUATION_HANDLER(s, &ShowHostDB::showLookup);
02366   }
02367   this_ethread()->schedule_imm(s);
02368   return &s->action;
02369 }
02370 
02371 
02372 #define HOSTDB_TEST_MAX_OUTSTANDING 100
02373 #define HOSTDB_TEST_LENGTH          100000
02374 
02375 struct HostDBTestReverse;
02376 typedef int (HostDBTestReverse::*HostDBTestReverseHandler) (int, void *);
02377 struct HostDBTestReverse: public Continuation
02378 {
02379   int outstanding;
02380   int total;
02381 #if HAVE_LRAND48_R
02382   struct drand48_data dr;
02383 #endif
02384 
02385   int mainEvent(int event, Event * e)
02386   {
02387     if (event == EVENT_HOST_DB_LOOKUP) {
02388       HostDBInfo *i = (HostDBInfo *) e;
02389       if (i)
02390           printf("HostDBTestReverse: reversed %s\n", i->hostname());
02391         outstanding--;
02392     }
02393     while (outstanding < HOSTDB_TEST_MAX_OUTSTANDING && total < HOSTDB_TEST_LENGTH)
02394     {
02395       long l = 0;
02396 #if HAVE_LRAND48_R
02397       lrand48_r(&dr, &l);
02398 #else
02399       l = lrand48();
02400 #endif
02401       IpEndpoint ip;
02402       ip.sin.sin_addr.s_addr = static_cast<in_addr_t>(l);
02403       outstanding++;
02404       total++;
02405       if (!(outstanding % 1000))
02406         printf("HostDBTestReverse: %d\n", total);
02407       hostDBProcessor.getbyaddr_re(this, &ip.sa);
02408     }
02409     if (!outstanding) {
02410       printf("HostDBTestReverse: done\n");
02411       delete this;
02412     }
02413     return EVENT_CONT;
02414   }
02415 HostDBTestReverse():Continuation(new_ProxyMutex()), outstanding(0), total(0) {
02416     SET_HANDLER((HostDBTestReverseHandler) & HostDBTestReverse::mainEvent);
02417 #if HAVE_SRAND48_R
02418     srand48_r(time(NULL), &dr);
02419 #else
02420     srand48(time(NULL));
02421 #endif
02422   }
02423 };
02424 
02425 
02426 #if TS_HAS_TESTS
02427 void
02428 run_HostDBTest()
02429 {
02430   if (is_action_tag_set("hostdb_test_rr"))
02431     eventProcessor.schedule_every(new HostDBTestRR, HRTIME_SECONDS(1), ET_NET);
02432   if (is_action_tag_set("hostdb_test_reverse")) {
02433     eventProcessor.schedule_imm(new HostDBTestReverse, ET_CACHE);
02434   }
02435 }
02436 #endif
02437 
02438 
02439 RecRawStatBlock *hostdb_rsb;
02440 
02441 void
02442 ink_hostdb_init(ModuleVersion v)
02443 {
02444   static int init_called = 0;
02445 
02446   ink_release_assert(!checkModuleVersion(v, HOSTDB_MODULE_VERSION));
02447   if (init_called)
02448     return;
02449 
02450   init_called = 1;
02451   // do one time stuff
02452   // create a stat block for HostDBStats
02453   hostdb_rsb = RecAllocateRawStatBlock((int) HostDB_Stat_Count);
02454 
02455   //
02456   // Register stats
02457   //
02458 
02459   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS,
02460                      "proxy.process.hostdb.total_entries",
02461                      RECD_INT, RECP_PERSISTENT, (int) hostdb_total_entries_stat, RecRawStatSyncCount);
02462 
02463   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS,
02464                      "proxy.process.hostdb.total_lookups",
02465                      RECD_INT, RECP_PERSISTENT, (int) hostdb_total_lookups_stat, RecRawStatSyncSum);
02466 
02467   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS,
02468                      "proxy.process.hostdb.total_hits",
02469                      RECD_INT, RECP_NON_PERSISTENT, (int) hostdb_total_hits_stat, RecRawStatSyncSum);
02470 
02471   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS,
02472                      "proxy.process.hostdb.ttl", RECD_FLOAT, RECP_PERSISTENT, (int) hostdb_ttl_stat, RecRawStatSyncAvg);
02473 
02474   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS,
02475                      "proxy.process.hostdb.ttl_expires",
02476                      RECD_INT, RECP_PERSISTENT, (int) hostdb_ttl_expires_stat, RecRawStatSyncSum);
02477 
02478   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS,
02479                      "proxy.process.hostdb.re_dns_on_reload",
02480                      RECD_INT, RECP_PERSISTENT, (int) hostdb_re_dns_on_reload_stat, RecRawStatSyncSum);
02481 
02482   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS,
02483                      "proxy.process.hostdb.bytes", RECD_INT, RECP_PERSISTENT, (int) hostdb_bytes_stat, RecRawStatSyncCount);
02484 
02485   ts_host_res_global_init();
02486 }

Generated by  doxygen 1.7.1