1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "Main.h"
25 #include "P_HostDB.h"
26 #include "P_RefCountCacheSerializer.h"
27 #include "tscore/I_Layout.h"
28 #include "Show.h"
29 #include "tscore/Tokenizer.h"
30 #include "tscore/ink_apidefs.h"
31 
32 #include <utility>
33 #include <vector>
34 #include <algorithm>
35 #include <random>
36 #include <chrono>
37 
38 HostDBProcessor hostDBProcessor;
39 int HostDBProcessor::hostdb_strict_round_robin = 0;
40 int HostDBProcessor::hostdb_timed_round_robin  = 0;
41 HostDBProcessor::Options const HostDBProcessor::DEFAULT_OPTIONS;
42 HostDBContinuation::Options const HostDBContinuation::DEFAULT_OPTIONS;
43 int hostdb_enable                              = true;
44 int hostdb_migrate_on_demand                   = true;
45 int hostdb_lookup_timeout                      = 30;
46 int hostdb_re_dns_on_reload                    = false;
47 int hostdb_ttl_mode                            = TTL_OBEY;
48 unsigned int hostdb_round_robin_max_count      = 16;
49 unsigned int hostdb_ip_stale_interval          = HOST_DB_IP_STALE;
50 unsigned int hostdb_ip_timeout_interval        = HOST_DB_IP_TIMEOUT;
51 unsigned int hostdb_ip_fail_timeout_interval   = HOST_DB_IP_FAIL_TIMEOUT;
52 unsigned int hostdb_serve_stale_but_revalidate = 0;
53 unsigned int hostdb_hostfile_check_interval    = 86400; // 1 day
54 // Epoch timestamp of the current hosts file check.
55 ink_time_t hostdb_current_interval = 0;
56 // Epoch timestamp of the last time we actually checked for a hosts file update.
57 static ink_time_t hostdb_last_interval = 0;
58 // Epoch timestamp when we updated the hosts file last.
59 static ink_time_t hostdb_hostfile_update_timestamp = 0;
60 static char hostdb_filename[PATH_NAME_MAX]         = DEFAULT_HOST_DB_FILENAME;
61 int hostdb_max_count                               = DEFAULT_HOST_DB_SIZE;
62 char hostdb_hostfile_path[PATH_NAME_MAX]           = "";
63 int hostdb_sync_frequency                          = 0;
64 int hostdb_disable_reverse_lookup                  = 0;
65 int hostdb_max_iobuf_index                         = BUFFER_SIZE_INDEX_32K;
66 
67 // Verify the generic storage is sufficient to cover all alternate members.
68 static_assert(sizeof(HostDBApplicationInfo::allotment) == sizeof(HostDBApplicationInfo),
69               "Generic storage for HostDBApplicationInfo is smaller than the union storage.");
70 
71 ClassAllocator<HostDBContinuation> hostDBContAllocator("hostDBContAllocator");
72 
73 // Static configuration information
74 
75 HostDBCache hostDB;
76 
77 void ParseHostFile(const char *path, unsigned int interval);
78 
79 char *
srvname(HostDBRoundRobin * rr) const80 HostDBInfo::srvname(HostDBRoundRobin *rr) const
81 {
82   if (!is_srv || !data.srv.srv_offset) {
83     return nullptr;
84   }
85   return reinterpret_cast<char *>(rr) + data.srv.srv_offset;
86 }
87 
88 static inline bool
is_addr_valid(uint8_t af,void * ptr)89 is_addr_valid(uint8_t af, ///< Address family (format of data)
90               void *ptr   ///< Raw address data (not a sockaddr variant!)
91 )
92 {
93   return (AF_INET == af && INADDR_ANY != *(reinterpret_cast<in_addr_t *>(ptr))) ||
94          (AF_INET6 == af && !IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast<in6_addr *>(ptr)));
95 }
96 
97 static inline void
ip_addr_set(sockaddr * ip,uint8_t af,void * ptr)98 ip_addr_set(sockaddr *ip, ///< Target storage, sockaddr compliant.
99             uint8_t af,   ///< Address format.
100             void *ptr     ///< Raw address data
101 )
102 {
103   if (AF_INET6 == af) {
104     ats_ip6_set(ip, *static_cast<in6_addr *>(ptr));
105   } else if (AF_INET == af) {
106     ats_ip4_set(ip, *static_cast<in_addr_t *>(ptr));
107   } else {
108     ats_ip_invalidate(ip);
109   }
110 }
111 
112 static inline void
ip_addr_set(IpAddr & ip,uint8_t af,void * ptr)113 ip_addr_set(IpAddr &ip, ///< Target storage.
114             uint8_t af, ///< Address format.
115             void *ptr   ///< Raw address data
116 )
117 {
118   if (AF_INET6 == af) {
119     ip = *static_cast<in6_addr *>(ptr);
120   } else if (AF_INET == af) {
121     ip = *static_cast<in_addr_t *>(ptr);
122   } else {
123     ip.invalidate();
124   }
125 }
126 
127 inline void
hostdb_cont_free(HostDBContinuation * cont)128 hostdb_cont_free(HostDBContinuation *cont)
129 {
130   if (cont->pending_action) {
131     cont->pending_action->cancel();
132   }
133   if (cont->timeout) {
134     cont->timeout->cancel();
135   }
136   cont->mutex        = nullptr;
137   cont->action.mutex = nullptr;
138   hostDBContAllocator.free(cont);
139 }
140 
141 /* Check whether a resolution fail should lead to a retry.
142    The @a mark argument is updated if appropriate.
143    @return @c true if @a mark was updated, @c false if no retry should be done.
144 */
145 static inline bool
check_for_retry(HostDBMark & mark,HostResStyle style)146 check_for_retry(HostDBMark &mark, HostResStyle style)
147 {
148   bool zret = true;
149   if (HOSTDB_MARK_IPV4 == mark && HOST_RES_IPV4 == style) {
150     mark = HOSTDB_MARK_IPV6;
151   } else if (HOSTDB_MARK_IPV6 == mark && HOST_RES_IPV6 == style) {
152     mark = HOSTDB_MARK_IPV4;
153   } else {
154     zret = false;
155   }
156   return zret;
157 }
158 
159 const char *
string_for(HostDBMark mark)160 string_for(HostDBMark mark)
161 {
162   static const char *STRING[] = {"Generic", "IPv4", "IPv6", "SRV"};
163   return STRING[mark];
164 }
165 
166 //
167 // Function Prototypes
168 //
169 static Action *register_ShowHostDB(Continuation *c, HTTPHdr *h);
170 
171 HostDBHash &
set_host(const char * name,int len)172 HostDBHash::set_host(const char *name, int len)
173 {
174   host_name = name;
175   host_len  = len;
176 
177   if (host_name && SplitDNSConfig::isSplitDNSEnabled()) {
178     const char *scan;
179     // I think this is checking for a hostname that is just an address.
180     for (scan = host_name; *scan != '\0' && (ParseRules::is_digit(*scan) || '.' == *scan || ':' == *scan); ++scan) {
181       ;
182     }
183     if ('\0' != *scan) {
184       // config is released in the destructor, because we must make sure values we
185       // get out of it don't evaporate while @a this is still around.
186       if (!pSD) {
187         pSD = SplitDNSConfig::acquire();
188       }
189       if (pSD) {
190         dns_server = static_cast<DNSServer *>(pSD->getDNSRecord(host_name));
191       }
192     } else {
193       dns_server = nullptr;
194     }
195   }
196 
197   return *this;
198 }
199 
200 void
refresh()201 HostDBHash::refresh()
202 {
203   CryptoContext ctx;
204 
205   if (host_name) {
206     const char *server_line = dns_server ? dns_server->x_dns_ip_line : nullptr;
207     uint8_t m               = static_cast<uint8_t>(db_mark); // be sure of the type.
208 
209     ctx.update(host_name, host_len);
210     ctx.update(reinterpret_cast<uint8_t *>(&port), sizeof(port));
211     ctx.update(&m, sizeof(m));
212     if (server_line) {
213       ctx.update(server_line, strlen(server_line));
214     }
215   } else {
216     // CryptoHash the ip, pad on both sizes with 0's
217     // so that it does not intersect the string space
218     //
219     char buff[TS_IP6_SIZE + 4];
220     int n = ip.isIp6() ? sizeof(in6_addr) : sizeof(in_addr_t);
221     memset(buff, 0, 2);
222     memcpy(buff + 2, ip._addr._byte, n);
223     memset(buff + 2 + n, 0, 2);
224     ctx.update(buff, n + 4);
225   }
226   ctx.finalize(hash);
227 }
228 
HostDBHash()229 HostDBHash::HostDBHash() {}
230 
~HostDBHash()231 HostDBHash::~HostDBHash()
232 {
233   if (pSD) {
234     SplitDNSConfig::release(pSD);
235   }
236 }
237 
HostDBCache()238 HostDBCache::HostDBCache()
239 {
240   hosts_file_ptr = new RefCountedHostsFileMap();
241 }
242 
243 bool
is_pending_dns_for_hash(const CryptoHash & hash)244 HostDBCache::is_pending_dns_for_hash(const CryptoHash &hash)
245 {
246   Queue<HostDBContinuation> &q = pending_dns_for_hash(hash);
247   for (HostDBContinuation *c = q.head; c; c = static_cast<HostDBContinuation *>(c->link.next)) {
248     if (hash == c->hash.hash) {
249       return true;
250     }
251   }
252   return false;
253 }
254 
255 HostDBCache *
cache()256 HostDBProcessor::cache()
257 {
258   return &hostDB;
259 }
260 
261 struct HostDBBackgroundTask : public Continuation {
262   int frequency;
263   ink_hrtime start_time;
264 
265   virtual int sync_event(int event, void *edata) = 0;
266   int wait_event(int event, void *edata);
267 
268   HostDBBackgroundTask(int frequency);
269 };
270 
HostDBBackgroundTask(int frequency)271 HostDBBackgroundTask::HostDBBackgroundTask(int frequency) : Continuation(new_ProxyMutex()), frequency(frequency), start_time(0)
272 {
273   SET_HANDLER(&HostDBBackgroundTask::sync_event);
274 }
275 
276 int
wait_event(int,void *)277 HostDBBackgroundTask::wait_event(int, void *)
278 {
279   ink_hrtime next_sync = HRTIME_SECONDS(this->frequency) - (Thread::get_hrtime() - start_time);
280 
281   SET_HANDLER(&HostDBBackgroundTask::sync_event);
282   if (next_sync > HRTIME_MSECONDS(100)) {
283     eventProcessor.schedule_in(this, next_sync, ET_TASK);
284   } else {
285     eventProcessor.schedule_imm(this, ET_TASK);
286   }
287   return EVENT_DONE;
288 }
289 
290 struct HostDBSync : public HostDBBackgroundTask {
291   std::string storage_path;
292   std::string full_path;
HostDBSyncHostDBSync293   HostDBSync(int frequency, const std::string &storage_path, const std::string &full_path)
294     : HostDBBackgroundTask(frequency), storage_path(std::move(storage_path)), full_path(std::move(full_path)){};
295   int
sync_eventHostDBSync296   sync_event(int, void *) override
297   {
298     SET_HANDLER(&HostDBSync::wait_event);
299     start_time = Thread::get_hrtime();
300 
301     new RefCountCacheSerializer<HostDBInfo>(this, hostDBProcessor.cache()->refcountcache, this->frequency, this->storage_path,
302                                             this->full_path);
303     return EVENT_DONE;
304   }
305 };
306 
307 int
start(int flags)308 HostDBCache::start(int flags)
309 {
310   (void)flags; // unused
311   char storage_path[PATH_NAME_MAX];
312   MgmtInt hostdb_max_size = 0;
313   int hostdb_partitions   = 64;
314 
315   storage_path[0] = '\0';
316 
317   // Read configuration
318   // Command line overrides manager configuration.
319   //
320   REC_ReadConfigInt32(hostdb_enable, "proxy.config.hostdb");
321   REC_ReadConfigString(storage_path, "proxy.config.hostdb.storage_path", sizeof(storage_path));
322   REC_ReadConfigString(hostdb_filename, "proxy.config.hostdb.filename", sizeof(hostdb_filename));
323 
324   // Max number of items
325   REC_ReadConfigInt32(hostdb_max_count, "proxy.config.hostdb.max_count");
326   // max size allowed to use
327   REC_ReadConfigInteger(hostdb_max_size, "proxy.config.hostdb.max_size");
328   // number of partitions
329   REC_ReadConfigInt32(hostdb_partitions, "proxy.config.hostdb.partitions");
330   // how often to sync hostdb to disk
331   REC_EstablishStaticConfigInt32(hostdb_sync_frequency, "proxy.config.cache.hostdb.sync_frequency");
332 
333   REC_EstablishStaticConfigInt32(hostdb_max_iobuf_index, "proxy.config.hostdb.io.max_buffer_index");
334 
335   if (hostdb_max_size == 0) {
336     Fatal("proxy.config.hostdb.max_size must be a non-zero number");
337   }
338 
339   // Setup the ref-counted cache (this must be done regardless of syncing or not).
340   this->refcountcache = new RefCountCache<HostDBInfo>(hostdb_partitions, hostdb_max_size, hostdb_max_count, HostDBInfo::version(),
341                                                       "proxy.process.hostdb.cache.");
342 
343   //
344   // Load and sync HostDB, if we've asked for it.
345   //
346   if (hostdb_sync_frequency > 0) {
347     // If proxy.config.hostdb.storage_path is not set, use the local state dir. If it is set to
348     // a relative path, make it relative to the prefix.
349     if (storage_path[0] == '\0') {
350       ats_scoped_str rundir(RecConfigReadRuntimeDir());
351       ink_strlcpy(storage_path, rundir, sizeof(storage_path));
352     } else if (storage_path[0] != '/') {
353       Layout::relative_to(storage_path, sizeof(storage_path), Layout::get()->prefix, storage_path);
354     }
355 
356     Debug("hostdb", "Storage path is %s", storage_path);
357 
358     if (access(storage_path, W_OK | R_OK) == -1) {
359       Warning("Unable to access() directory '%s': %d, %s", storage_path, errno, strerror(errno));
360       Warning("Please set 'proxy.config.hostdb.storage_path' or 'proxy.config.local_state_dir'");
361     }
362 
363     // Combine the path and name
364     char full_path[2 * PATH_NAME_MAX];
365     ink_filepath_make(full_path, 2 * PATH_NAME_MAX, storage_path, hostdb_filename);
366 
367     Debug("hostdb", "Opening %s, partitions=%d storage_size=%" PRIu64 " items=%d", full_path, hostdb_partitions, hostdb_max_size,
368           hostdb_max_count);
369     int load_ret = LoadRefCountCacheFromPath<HostDBInfo>(*this->refcountcache, storage_path, full_path, HostDBInfo::unmarshall);
370     if (load_ret != 0) {
371       Warning("Error loading cache from %s: %d", full_path, load_ret);
372     }
373 
374     eventProcessor.schedule_imm(new HostDBSync(hostdb_sync_frequency, storage_path, full_path), ET_TASK);
375   }
376 
377   this->pending_dns       = new Queue<HostDBContinuation, Continuation::Link_link>[hostdb_partitions];
378   this->remoteHostDBQueue = new Queue<HostDBContinuation, Continuation::Link_link>[hostdb_partitions];
379   return 0;
380 }
381 
382 // Start up the Host Database processor.
383 // Load configuration, register configuration and statistics and
384 // open the cache. This doesn't create any threads, so those
385 // parameters are ignored.
386 //
387 int
start(int,size_t)388 HostDBProcessor::start(int, size_t)
389 {
390   if (hostDB.start(0) < 0) {
391     return -1;
392   }
393 
394   if (auto_clear_hostdb_flag) {
395     hostDB.refcountcache->clear();
396   }
397 
398   statPagesManager.register_http("hostdb", register_ShowHostDB);
399 
400   //
401   // Register configuration callback, and establish configuration links
402   //
403   REC_EstablishStaticConfigInt32(hostdb_ttl_mode, "proxy.config.hostdb.ttl_mode");
404   REC_EstablishStaticConfigInt32(hostdb_disable_reverse_lookup, "proxy.config.cache.hostdb.disable_reverse_lookup");
405   REC_EstablishStaticConfigInt32(hostdb_re_dns_on_reload, "proxy.config.hostdb.re_dns_on_reload");
406   REC_EstablishStaticConfigInt32(hostdb_migrate_on_demand, "proxy.config.hostdb.migrate_on_demand");
407   REC_EstablishStaticConfigInt32(hostdb_strict_round_robin, "proxy.config.hostdb.strict_round_robin");
408   REC_EstablishStaticConfigInt32(hostdb_timed_round_robin, "proxy.config.hostdb.timed_round_robin");
409   REC_EstablishStaticConfigInt32(hostdb_lookup_timeout, "proxy.config.hostdb.lookup_timeout");
410   REC_EstablishStaticConfigInt32U(hostdb_ip_timeout_interval, "proxy.config.hostdb.timeout");
411   REC_EstablishStaticConfigInt32U(hostdb_ip_stale_interval, "proxy.config.hostdb.verify_after");
412   REC_EstablishStaticConfigInt32U(hostdb_ip_fail_timeout_interval, "proxy.config.hostdb.fail.timeout");
413   REC_EstablishStaticConfigInt32U(hostdb_serve_stale_but_revalidate, "proxy.config.hostdb.serve_stale_for");
414   REC_EstablishStaticConfigInt32U(hostdb_hostfile_check_interval, "proxy.config.hostdb.host_file.interval");
415   REC_EstablishStaticConfigInt32U(hostdb_round_robin_max_count, "proxy.config.hostdb.round_robin_max_count");
416 
417   //
418   // Set up hostdb_current_interval
419   //
420   hostdb_current_interval = ink_time();
421 
422   HostDBContinuation *b = hostDBContAllocator.alloc();
423   SET_CONTINUATION_HANDLER(b, (HostDBContHandler)&HostDBContinuation::backgroundEvent);
424   b->mutex = new_ProxyMutex();
425   eventProcessor.schedule_every(b, HRTIME_SECONDS(1), ET_DNS);
426 
427   return 0;
428 }
429 
430 void
init(HostDBHash const & the_hash,Options const & opt)431 HostDBContinuation::init(HostDBHash const &the_hash, Options const &opt)
432 {
433   hash = the_hash;
434   if (hash.host_name) {
435     // copy to backing store.
436     if (hash.host_len > static_cast<int>(sizeof(hash_host_name_store) - 1)) {
437       hash.host_len = sizeof(hash_host_name_store) - 1;
438     }
439     memcpy(hash_host_name_store, hash.host_name, hash.host_len);
440   } else {
441     hash.host_len = 0;
442   }
443   hash_host_name_store[hash.host_len] = 0;
444   hash.host_name                      = hash_host_name_store;
445 
446   host_res_style     = opt.host_res_style;
447   dns_lookup_timeout = opt.timeout;
448   mutex              = hostDB.refcountcache->lock_for_key(hash.hash.fold());
449   if (opt.cont) {
450     action = opt.cont;
451   } else {
452     // ink_assert(!"this sucks");
453     ink_zero(action);
454     action.mutex = mutex;
455   }
456 }
457 
458 void
refresh_hash()459 HostDBContinuation::refresh_hash()
460 {
461   Ptr<ProxyMutex> old_bucket_mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold());
462   // We're not pending DNS anymore.
463   remove_trigger_pending_dns();
464   hash.refresh();
465   // Update the mutex if it's from the bucket.
466   // Some call sites modify this after calling @c init so need to check.
467   if (mutex == old_bucket_mutex) {
468     mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold());
469   }
470 }
471 
472 static bool
reply_to_cont(Continuation * cont,HostDBInfo * r,bool is_srv=false)473 reply_to_cont(Continuation *cont, HostDBInfo *r, bool is_srv = false)
474 {
475   if (r == nullptr || r->is_srv != is_srv || r->is_failed()) {
476     cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr);
477     return false;
478   }
479 
480   if (r->reverse_dns) {
481     if (!r->hostname()) {
482       ink_assert(!"missing hostname");
483       cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr);
484       Warning("bogus entry deleted from HostDB: missing hostname");
485       hostDB.refcountcache->erase(r->key);
486       return false;
487     }
488     Debug("hostdb", "hostname = %s", r->hostname());
489   }
490 
491   if (!r->is_srv && r->round_robin) {
492     if (!r->rr()) {
493       ink_assert(!"missing round-robin");
494       cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr);
495       Warning("bogus entry deleted from HostDB: missing round-robin");
496       hostDB.refcountcache->erase(r->key);
497       return false;
498     }
499     ip_text_buffer ipb;
500     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));
501   }
502 
503   cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, r);
504 
505   return true;
506 }
507 
508 inline HostResStyle
host_res_style_for(sockaddr const * ip)509 host_res_style_for(sockaddr const *ip)
510 {
511   return ats_is_ip6(ip) ? HOST_RES_IPV6_ONLY : HOST_RES_IPV4_ONLY;
512 }
513 
514 inline HostResStyle
host_res_style_for(HostDBMark mark)515 host_res_style_for(HostDBMark mark)
516 {
517   return HOSTDB_MARK_IPV4 == mark ? HOST_RES_IPV4_ONLY : HOSTDB_MARK_IPV6 == mark ? HOST_RES_IPV6_ONLY : HOST_RES_NONE;
518 }
519 
520 inline HostDBMark
db_mark_for(HostResStyle style)521 db_mark_for(HostResStyle style)
522 {
523   HostDBMark zret = HOSTDB_MARK_GENERIC;
524   if (HOST_RES_IPV4 == style || HOST_RES_IPV4_ONLY == style) {
525     zret = HOSTDB_MARK_IPV4;
526   } else if (HOST_RES_IPV6 == style || HOST_RES_IPV6_ONLY == style) {
527     zret = HOSTDB_MARK_IPV6;
528   }
529   return zret;
530 }
531 
532 inline HostDBMark
db_mark_for(sockaddr const * ip)533 db_mark_for(sockaddr const *ip)
534 {
535   return ats_is_ip6(ip) ? HOSTDB_MARK_IPV6 : HOSTDB_MARK_IPV4;
536 }
537 
538 inline HostDBMark
db_mark_for(IpAddr const & ip)539 db_mark_for(IpAddr const &ip)
540 {
541   return ip.isIp6() ? HOSTDB_MARK_IPV6 : HOSTDB_MARK_IPV4;
542 }
543 
544 Ptr<HostDBInfo>
probe(const Ptr<ProxyMutex> & mutex,HostDBHash const & hash,bool ignore_timeout)545 probe(const Ptr<ProxyMutex> &mutex, HostDBHash const &hash, bool ignore_timeout)
546 {
547   // If hostdb is disabled, don't return anything
548   if (!hostdb_enable) {
549     return Ptr<HostDBInfo>();
550   }
551 
552   // Otherwise HostDB is enabled, so we'll do our thing
553   ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding);
554   uint64_t folded_hash = hash.hash.fold();
555 
556   // get the item from cache
557   Ptr<HostDBInfo> r = hostDB.refcountcache->get(folded_hash);
558   // If there was nothing in the cache-- this is a miss
559   if (r.get() == nullptr) {
560     return r;
561   }
562 
563   // If the dns response was failed, and we've hit the failed timeout, lets stop returning it
564   if (r->is_failed() && r->is_ip_fail_timeout()) {
565     return make_ptr((HostDBInfo *)nullptr);
566     // if we aren't ignoring timeouts, and we are past it-- then remove the item
567   } else if (!ignore_timeout && r->is_ip_timeout() && !r->serve_stale_but_revalidate()) {
568     HOSTDB_INCREMENT_DYN_STAT(hostdb_ttl_expires_stat);
569     return make_ptr((HostDBInfo *)nullptr);
570   }
571 
572   // If the record is stale, but we want to revalidate-- lets start that up
573   if ((!ignore_timeout && r->is_ip_stale() && !r->reverse_dns) || (r->is_ip_timeout() && r->serve_stale_but_revalidate())) {
574     if (hostDB.is_pending_dns_for_hash(hash.hash)) {
575       Debug("hostdb", "stale %u %u %u, using it and pending to refresh it", r->ip_interval(), r->ip_timestamp,
576             r->ip_timeout_interval);
577       return r;
578     }
579     Debug("hostdb", "stale %u %u %u, using it and refreshing it", r->ip_interval(), r->ip_timestamp, r->ip_timeout_interval);
580     HostDBContinuation *c = hostDBContAllocator.alloc();
581     HostDBContinuation::Options copt;
582     copt.host_res_style = host_res_style_for(r->ip());
583     c->init(hash, copt);
584     c->do_dns();
585   }
586   return r;
587 }
588 
589 //
590 // Insert a HostDBInfo into the database
591 // A null value indicates that the block is empty.
592 //
593 HostDBInfo *
insert(unsigned int attl)594 HostDBContinuation::insert(unsigned int attl)
595 {
596   uint64_t folded_hash = hash.hash.fold();
597 
598   ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(folded_hash)->thread_holding);
599 
600   HostDBInfo *r = HostDBInfo::alloc();
601   r->key        = folded_hash;
602 
603   r->ip_timestamp        = hostdb_current_interval;
604   r->ip_timeout_interval = std::clamp(attl, 1u, HOST_DB_MAX_TTL);
605 
606   Debug("hostdb", "inserting for: %.*s: (hash: %" PRIx64 ") now: %u timeout: %u ttl: %u", hash.host_len, hash.host_name,
607         folded_hash, r->ip_timestamp, r->ip_timeout_interval, attl);
608 
609   hostDB.refcountcache->put(folded_hash, r, 0, r->expiry_time());
610   return r;
611 }
612 
613 //
614 // Get an entry by either name or IP
615 //
616 Action *
getby(Continuation * cont,cb_process_result_pfn cb_process_result,HostDBHash & hash,Options const & opt)617 HostDBProcessor::getby(Continuation *cont, cb_process_result_pfn cb_process_result, HostDBHash &hash, Options const &opt)
618 {
619   bool force_dns        = false;
620   EThread *thread       = this_ethread();
621   Ptr<ProxyMutex> mutex = thread->mutex;
622   ip_text_buffer ipb;
623 
624   if (opt.flags & HOSTDB_FORCE_DNS_ALWAYS) {
625     force_dns = true;
626   } else if (opt.flags & HOSTDB_FORCE_DNS_RELOAD) {
627     force_dns = hostdb_re_dns_on_reload;
628     if (force_dns) {
629       HOSTDB_INCREMENT_DYN_STAT(hostdb_re_dns_on_reload_stat);
630     }
631   }
632 
633   HOSTDB_INCREMENT_DYN_STAT(hostdb_total_lookups_stat);
634 
635   if (!hostdb_enable ||                                       // if the HostDB is disabled,
636       (hash.host_name && !*hash.host_name) ||                 // or host_name is empty string
637       (hostdb_disable_reverse_lookup && hash.ip.isValid())) { // or try to lookup by ip address when the reverse lookup disabled
638     if (cb_process_result) {
639       (cont->*cb_process_result)(nullptr);
640     } else {
641       MUTEX_TRY_LOCK(lock, cont->mutex, thread);
642       if (!lock.is_locked()) {
643         goto Lretry;
644       }
645       cont->handleEvent(EVENT_HOST_DB_LOOKUP, nullptr);
646     }
647     return ACTION_RESULT_DONE;
648   }
649 
650   // Attempt to find the result in-line, for level 1 hits
651   if (!force_dns) {
652     MUTEX_TRY_LOCK(lock, cont->mutex, thread);
653     bool loop = lock.is_locked();
654     while (loop) {
655       loop = false; // Only loop on explicit set for retry.
656       // find the partition lock
657       Ptr<ProxyMutex> bucket_mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold());
658       MUTEX_TRY_LOCK(lock2, bucket_mutex, thread);
659       if (lock2.is_locked()) {
660         // If we can get the lock and a level 1 probe succeeds, return
661         Ptr<HostDBInfo> r = probe(bucket_mutex, hash, false);
662         if (r) {
663           // fail, see if we should retry with alternate
664           if (hash.db_mark != HOSTDB_MARK_SRV && r->is_failed() && hash.host_name) {
665             loop = check_for_retry(hash.db_mark, opt.host_res_style);
666           }
667           if (!loop) {
668             // No retry -> final result. Return it.
669             if (hash.db_mark == HOSTDB_MARK_SRV) {
670               Debug("hostdb", "immediate SRV answer for %.*s from hostdb", hash.host_len, hash.host_name);
671               Debug("dns_srv", "immediate SRV answer for %.*s from hostdb", hash.host_len, hash.host_name);
672             } else if (hash.host_name) {
673               Debug("hostdb", "immediate answer for %.*s", hash.host_len, hash.host_name);
674             } else {
675               Debug("hostdb", "immediate answer for %s", hash.ip.isValid() ? hash.ip.toString(ipb, sizeof ipb) : "<null>");
676             }
677             HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
678             if (cb_process_result) {
679               (cont->*cb_process_result)(r.get());
680             } else {
681               reply_to_cont(cont, r.get());
682             }
683             return ACTION_RESULT_DONE;
684           }
685           hash.refresh(); // only on reloop, because we've changed the family.
686         }
687       }
688     }
689   }
690   if (hash.db_mark == HOSTDB_MARK_SRV) {
691     Debug("hostdb", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, hash.host_len, hash.host_name,
692           opt.timeout);
693     Debug("dns_srv", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, hash.host_len, hash.host_name,
694           opt.timeout);
695   } else if (hash.host_name) {
696     Debug("hostdb", "delaying (force=%d) answer for %.*s [timeout %d]", force_dns, hash.host_len, hash.host_name, opt.timeout);
697   } else {
698     Debug("hostdb", "delaying (force=%d) answer for %s [timeout %d]", force_dns,
699           hash.ip.isValid() ? hash.ip.toString(ipb, sizeof ipb) : "<null>", opt.timeout);
700   }
701 
702 Lretry:
703   // Otherwise, create a continuation to do a deeper probe in the background
704   //
705   HostDBContinuation *c = hostDBContAllocator.alloc();
706   HostDBContinuation::Options copt;
707   copt.timeout        = opt.timeout;
708   copt.force_dns      = force_dns;
709   copt.cont           = cont;
710   copt.host_res_style = (hash.db_mark == HOSTDB_MARK_SRV) ? HOST_RES_NONE : opt.host_res_style;
711   c->init(hash, copt);
712   SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::probeEvent);
713 
714   thread->schedule_in(c, MUTEX_RETRY_DELAY);
715 
716   return &c->action;
717 }
718 
719 // Wrapper from getbyname to getby
720 //
721 Action *
getbyname_re(Continuation * cont,const char * ahostname,int len,Options const & opt)722 HostDBProcessor::getbyname_re(Continuation *cont, const char *ahostname, int len, Options const &opt)
723 {
724   HostDBHash hash;
725 
726   ink_assert(nullptr != ahostname);
727 
728   // Load the hash data.
729   hash.set_host(ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0);
730   // Leave hash.ip invalid
731   hash.port    = 0;
732   hash.db_mark = db_mark_for(opt.host_res_style);
733   hash.refresh();
734 
735   return getby(cont, nullptr, hash, opt);
736 }
737 
738 Action *
getbynameport_re(Continuation * cont,const char * ahostname,int len,Options const & opt)739 HostDBProcessor::getbynameport_re(Continuation *cont, const char *ahostname, int len, Options const &opt)
740 {
741   HostDBHash hash;
742 
743   ink_assert(nullptr != ahostname);
744 
745   // Load the hash data.
746   hash.set_host(ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0);
747   // Leave hash.ip invalid
748   hash.port    = opt.port;
749   hash.db_mark = db_mark_for(opt.host_res_style);
750   hash.refresh();
751 
752   return getby(cont, nullptr, hash, opt);
753 }
754 
755 // Lookup Hostinfo by addr
756 Action *
getbyaddr_re(Continuation * cont,sockaddr const * aip)757 HostDBProcessor::getbyaddr_re(Continuation *cont, sockaddr const *aip)
758 {
759   HostDBHash hash;
760 
761   ink_assert(nullptr != aip);
762 
763   HostDBProcessor::Options opt;
764   opt.host_res_style = HOST_RES_NONE;
765 
766   // Leave hash.host_name as nullptr
767   hash.ip.assign(aip);
768   hash.port    = ats_ip_port_host_order(aip);
769   hash.db_mark = db_mark_for(opt.host_res_style);
770   hash.refresh();
771 
772   return getby(cont, nullptr, hash, opt);
773 }
774 
775 /* Support SRV records */
776 Action *
getSRVbyname_imm(Continuation * cont,cb_process_result_pfn process_srv_info,const char * hostname,int len,Options const & opt)777 HostDBProcessor::getSRVbyname_imm(Continuation *cont, cb_process_result_pfn process_srv_info, const char *hostname, int len,
778                                   Options const &opt)
779 {
780   ink_assert(cont->mutex->thread_holding == this_ethread());
781   HostDBHash hash;
782 
783   ink_assert(nullptr != hostname);
784 
785   hash.set_host(hostname, len ? len : strlen(hostname));
786   // Leave hash.ip invalid
787   hash.port    = 0;
788   hash.db_mark = HOSTDB_MARK_SRV;
789   hash.refresh();
790 
791   return getby(cont, process_srv_info, hash, opt);
792 }
793 
794 // Wrapper from getbyname to getby
795 //
796 Action *
getbyname_imm(Continuation * cont,cb_process_result_pfn process_hostdb_info,const char * hostname,int len,Options const & opt)797 HostDBProcessor::getbyname_imm(Continuation *cont, cb_process_result_pfn process_hostdb_info, const char *hostname, int len,
798                                Options const &opt)
799 {
800   ink_assert(cont->mutex->thread_holding == this_ethread());
801   HostDBHash hash;
802 
803   ink_assert(nullptr != hostname);
804 
805   hash.set_host(hostname, len ? len : strlen(hostname));
806   // Leave hash.ip invalid
807   // TODO: May I rename the wrapper name to getbynameport_imm ? - oknet
808   //   By comparing getbyname_re and getbynameport_re, the hash.port should be 0 if only get hostinfo by name.
809   hash.port    = opt.port;
810   hash.db_mark = db_mark_for(opt.host_res_style);
811   hash.refresh();
812 
813   return getby(cont, process_hostdb_info, hash, opt);
814 }
815 
816 Action *
iterate(Continuation * cont)817 HostDBProcessor::iterate(Continuation *cont)
818 {
819   ink_assert(cont->mutex->thread_holding == this_ethread());
820   EThread *thread   = cont->mutex->thread_holding;
821   ProxyMutex *mutex = thread->mutex.get();
822 
823   HOSTDB_INCREMENT_DYN_STAT(hostdb_total_lookups_stat);
824 
825   HostDBContinuation *c = hostDBContAllocator.alloc();
826   HostDBContinuation::Options copt;
827   copt.cont           = cont;
828   copt.force_dns      = false;
829   copt.timeout        = 0;
830   copt.host_res_style = HOST_RES_NONE;
831   c->init(HostDBHash(), copt);
832   c->current_iterate_pos = 0;
833   SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::iterateEvent);
834 
835   thread->schedule_in(c, HOST_DB_RETRY_PERIOD);
836 
837   return &c->action;
838 }
839 
840 static void
do_setby(HostDBInfo * r,HostDBApplicationInfo * app,const char * hostname,IpAddr const & ip,bool is_srv=false)841 do_setby(HostDBInfo *r, HostDBApplicationInfo *app, const char *hostname, IpAddr const &ip, bool is_srv = false)
842 {
843   HostDBRoundRobin *rr = r->rr();
844 
845   if (is_srv && (!r->is_srv || !rr)) {
846     return;
847   }
848 
849   if (rr) {
850     if (is_srv) {
851       uint32_t key = makeHostHash(hostname);
852       for (int i = 0; i < rr->rrcount; i++) {
853         if (key == rr->info(i).data.srv.key && !strcmp(hostname, rr->info(i).srvname(rr))) {
854           Debug("hostdb", "immediate setby for %s", hostname);
855           rr->info(i).app.allotment.application1 = app->allotment.application1;
856           rr->info(i).app.allotment.application2 = app->allotment.application2;
857           return;
858         }
859       }
860     } else {
861       for (int i = 0; i < rr->rrcount; i++) {
862         if (rr->info(i).ip() == ip) {
863           Debug("hostdb", "immediate setby for %s", hostname ? hostname : "<addr>");
864           rr->info(i).app.allotment.application1 = app->allotment.application1;
865           rr->info(i).app.allotment.application2 = app->allotment.application2;
866           return;
867         }
868       }
869     }
870   } else {
871     if (r->reverse_dns || (!r->round_robin && ip == r->ip())) {
872       Debug("hostdb", "immediate setby for %s", hostname ? hostname : "<addr>");
873       r->app.allotment.application1 = app->allotment.application1;
874       r->app.allotment.application2 = app->allotment.application2;
875     }
876   }
877 }
878 
879 void
setby(const char * hostname,int len,sockaddr const * ip,HostDBApplicationInfo * app)880 HostDBProcessor::setby(const char *hostname, int len, sockaddr const *ip, HostDBApplicationInfo *app)
881 {
882   if (!hostdb_enable) {
883     return;
884   }
885 
886   HostDBHash hash;
887   hash.set_host(hostname, hostname ? (len ? len : strlen(hostname)) : 0);
888   hash.ip.assign(ip);
889   hash.port    = ip ? ats_ip_port_host_order(ip) : 0;
890   hash.db_mark = db_mark_for(ip);
891   hash.refresh();
892 
893   // Attempt to find the result in-line, for level 1 hits
894 
895   Ptr<ProxyMutex> mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold());
896   EThread *thread       = this_ethread();
897   MUTEX_TRY_LOCK(lock, mutex, thread);
898 
899   if (lock.is_locked()) {
900     Ptr<HostDBInfo> r = probe(mutex, hash, false);
901     if (r) {
902       do_setby(r.get(), app, hostname, hash.ip);
903     }
904     return;
905   }
906   // Create a continuation to do a deeper probe in the background
907 
908   HostDBContinuation *c = hostDBContAllocator.alloc();
909   c->init(hash);
910   c->app.allotment.application1 = app->allotment.application1;
911   c->app.allotment.application2 = app->allotment.application2;
912   SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::setbyEvent);
913   thread->schedule_in(c, MUTEX_RETRY_DELAY);
914 }
915 
916 void
setby_srv(const char * hostname,int len,const char * target,HostDBApplicationInfo * app)917 HostDBProcessor::setby_srv(const char *hostname, int len, const char *target, HostDBApplicationInfo *app)
918 {
919   if (!hostdb_enable || !hostname || !target) {
920     return;
921   }
922 
923   HostDBHash hash;
924   hash.set_host(hostname, len ? len : strlen(hostname));
925   hash.port    = 0;
926   hash.db_mark = HOSTDB_MARK_SRV;
927   hash.refresh();
928 
929   // Create a continuation to do a deeper probe in the background
930 
931   HostDBContinuation *c = hostDBContAllocator.alloc();
932   c->init(hash);
933   ink_strlcpy(c->srv_target_name, target, MAXDNAME);
934   c->app.allotment.application1 = app->allotment.application1;
935   c->app.allotment.application2 = app->allotment.application2;
936   SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::setbyEvent);
937   eventProcessor.schedule_imm(c);
938 }
939 int
setbyEvent(int,Event *)940 HostDBContinuation::setbyEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
941 {
942   Ptr<HostDBInfo> r = probe(mutex, hash, false);
943 
944   if (r) {
945     do_setby(r.get(), &app, hash.host_name, hash.ip, is_srv());
946   }
947 
948   hostdb_cont_free(this);
949   return EVENT_DONE;
950 }
951 
952 // Lookup done, insert into the local table, return data to the
953 // calling continuation.
954 // NOTE: if "i" exists it means we already allocated the space etc, just return
955 //
956 HostDBInfo *
lookup_done(IpAddr const & ip,const char * aname,bool around_robin,unsigned int ttl_seconds,SRVHosts * srv,HostDBInfo * r)957 HostDBContinuation::lookup_done(IpAddr const &ip, const char *aname, bool around_robin, unsigned int ttl_seconds, SRVHosts *srv,
958                                 HostDBInfo *r)
959 {
960   ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding);
961   if (!ip.isValid() || !aname || !aname[0]) {
962     if (is_byname()) {
963       Debug("hostdb", "lookup_done() failed for '%.*s'", hash.host_len, hash.host_name);
964     } else if (is_srv()) {
965       Debug("dns_srv", "SRV failed for '%.*s'", hash.host_len, hash.host_name);
966     } else {
967       ip_text_buffer b;
968       Debug("hostdb", "failed for %s", hash.ip.toString(b, sizeof b));
969     }
970     if (r == nullptr) {
971       r = insert(hostdb_ip_fail_timeout_interval);
972     } else {
973       r->ip_timestamp        = hostdb_current_interval;
974       r->ip_timeout_interval = std::clamp(hostdb_ip_fail_timeout_interval, 1u, HOST_DB_MAX_TTL);
975     }
976 
977     r->round_robin     = false;
978     r->round_robin_elt = false;
979     r->is_srv          = is_srv();
980     r->reverse_dns     = !is_byname() && !is_srv();
981 
982     r->set_failed();
983     return r;
984 
985   } else {
986     switch (hostdb_ttl_mode) {
987     default:
988       ink_assert(!"bad TTL mode");
989     case TTL_OBEY:
990       break;
991     case TTL_IGNORE:
992       ttl_seconds = hostdb_ip_timeout_interval;
993       break;
994     case TTL_MIN:
995       if (hostdb_ip_timeout_interval < ttl_seconds) {
996         ttl_seconds = hostdb_ip_timeout_interval;
997       }
998       break;
999     case TTL_MAX:
1000       if (hostdb_ip_timeout_interval > ttl_seconds) {
1001         ttl_seconds = hostdb_ip_timeout_interval;
1002       }
1003       break;
1004     }
1005     HOSTDB_SUM_DYN_STAT(hostdb_ttl_stat, ttl_seconds);
1006 
1007     if (r == nullptr) {
1008       r = insert(ttl_seconds);
1009     } else {
1010       // update the TTL
1011       r->ip_timestamp        = hostdb_current_interval;
1012       r->ip_timeout_interval = std::clamp(ttl_seconds, 1u, HOST_DB_MAX_TTL);
1013     }
1014 
1015     r->round_robin_elt = false; // only true for elements explicitly added as RR elements.
1016     if (is_byname()) {
1017       ip_text_buffer b;
1018       Debug("hostdb", "done %s TTL %d", ip.toString(b, sizeof b), ttl_seconds);
1019       ats_ip_set(r->ip(), ip);
1020       r->round_robin = around_robin;
1021       r->reverse_dns = false;
1022       if (hash.host_name != aname) {
1023         ink_strlcpy(hash_host_name_store, aname, sizeof(hash_host_name_store));
1024       }
1025       r->is_srv = false;
1026     } else if (is_srv()) {
1027       ink_assert(srv && srv->hosts.size() && srv->hosts.size() <= hostdb_round_robin_max_count && around_robin);
1028 
1029       r->data.srv.srv_offset = srv->hosts.size();
1030       r->reverse_dns         = false;
1031       r->is_srv              = true;
1032       r->round_robin         = around_robin;
1033 
1034       if (hash.host_name != aname) {
1035         ink_strlcpy(hash_host_name_store, aname, sizeof(hash_host_name_store));
1036       }
1037 
1038     } else {
1039       Debug("hostdb", "done '%s' TTL %d", aname, ttl_seconds);
1040       // TODO: check that this is right, it seems that the 2 hostnames are always the same
1041       r->data.hostname_offset = r->hostname_offset;
1042       // TODO: consolidate into a single "item type" field?
1043       r->round_robin = false;
1044       r->reverse_dns = true;
1045       r->is_srv      = false;
1046     }
1047   }
1048 
1049   ink_assert(!r->round_robin || !r->reverse_dns);
1050   return r;
1051 }
1052 
1053 int
dnsPendingEvent(int event,Event * e)1054 HostDBContinuation::dnsPendingEvent(int event, Event *e)
1055 {
1056   ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding);
1057   if (timeout) {
1058     timeout->cancel(this);
1059     timeout = nullptr;
1060   }
1061   if (event == EVENT_INTERVAL) {
1062     // we timed out, return a failure to the user
1063     MUTEX_TRY_LOCK(lock, action.mutex, ((Event *)e)->ethread);
1064     if (!lock.is_locked()) {
1065       timeout = eventProcessor.schedule_in(this, HOST_DB_RETRY_PERIOD);
1066       return EVENT_CONT;
1067     }
1068     if (!action.cancelled && action.continuation) {
1069       action.continuation->handleEvent(EVENT_HOST_DB_LOOKUP, nullptr);
1070     }
1071     hostDB.pending_dns_for_hash(hash.hash).remove(this);
1072     hostdb_cont_free(this);
1073     return EVENT_DONE;
1074   } else {
1075     SET_HANDLER((HostDBContHandler)&HostDBContinuation::probeEvent);
1076     return probeEvent(EVENT_INTERVAL, nullptr);
1077   }
1078 }
1079 
1080 // for a new HostDBInfo `r`, "inherit" from the old version of yourself if it exists in `old_rr_data`
1081 static int
restore_info(HostDBInfo * r,HostDBInfo * old_r,HostDBInfo & old_info,HostDBRoundRobin * old_rr_data)1082 restore_info(HostDBInfo *r, HostDBInfo *old_r, HostDBInfo &old_info, HostDBRoundRobin *old_rr_data)
1083 {
1084   if (old_rr_data) {
1085     for (int j = 0; j < old_rr_data->rrcount; j++) {
1086       if (ats_ip_addr_eq(old_rr_data->info(j).ip(), r->ip())) {
1087         r->app = old_rr_data->info(j).app;
1088         return true;
1089       }
1090     }
1091   } else if (old_r) {
1092     if (ats_ip_addr_eq(old_info.ip(), r->ip())) {
1093       r->app = old_info.app;
1094       return true;
1095     }
1096   }
1097   return false;
1098 }
1099 
1100 // DNS lookup result state
1101 //
1102 int
dnsEvent(int event,HostEnt * e)1103 HostDBContinuation::dnsEvent(int event, HostEnt *e)
1104 {
1105   ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding);
1106   if (timeout) {
1107     timeout->cancel(this);
1108     timeout = nullptr;
1109   }
1110   EThread *thread = mutex->thread_holding;
1111   if (event != DNS_EVENT_LOOKUP) {
1112     // This was an event_interval or an event_immediate
1113     // Either we timed out, or remove_trigger_pending gave up on us
1114     if (!action.continuation) {
1115       // give up on insert, it has been too long
1116       hostDB.pending_dns_for_hash(hash.hash).remove(this);
1117       hostdb_cont_free(this);
1118       return EVENT_DONE;
1119     }
1120     MUTEX_TRY_LOCK(lock, action.mutex, thread);
1121     if (!lock.is_locked()) {
1122       timeout = thread->schedule_in(this, HOST_DB_RETRY_PERIOD);
1123       return EVENT_CONT;
1124     }
1125     // [amc] Callback to client to indicate a failure due to timeout.
1126     // We don't try a different family here because a timeout indicates
1127     // a server issue that won't be fixed by asking for a different
1128     // address family.
1129     if (!action.cancelled && action.continuation) {
1130       action.continuation->handleEvent(EVENT_HOST_DB_LOOKUP, nullptr);
1131     }
1132     action = nullptr;
1133     return EVENT_DONE;
1134   } else {
1135     bool failed = !e || !e->good;
1136 
1137     bool is_rr     = false;
1138     pending_action = nullptr;
1139 
1140     if (is_srv()) {
1141       is_rr = !failed && (e->srv_hosts.hosts.size() > 0);
1142     } else if (!failed) {
1143       is_rr = nullptr != e->ent.h_addr_list[1];
1144     } else {
1145     }
1146 
1147     ttl             = failed ? 0 : e->ttl / 60;
1148     int ttl_seconds = failed ? 0 : e->ttl; // ebalsa: moving to second accuracy
1149 
1150     Ptr<HostDBInfo> old_r = probe(mutex, hash, false);
1151     // If the DNS lookup failed with NXDOMAIN, remove the old record
1152     if (e && e->isNameError() && old_r) {
1153       hostDB.refcountcache->erase(old_r->key);
1154       old_r = nullptr;
1155       Debug("hostdb", "Removing the old record when the DNS lookup failed with NXDOMAIN");
1156     }
1157     HostDBInfo old_info;
1158     if (old_r) {
1159       old_info = *old_r.get();
1160     }
1161     HostDBRoundRobin *old_rr_data = old_r ? old_r->rr() : nullptr;
1162     int valid_records             = 0;
1163     void *first_record            = nullptr;
1164     uint8_t af                    = e ? e->ent.h_addrtype : AF_UNSPEC; // address family
1165     // if this is an RR response, we need to find the first record, as well as the
1166     // total number of records
1167     if (is_rr) {
1168       if (is_srv() && !failed) {
1169         valid_records = e->srv_hosts.hosts.size();
1170       } else {
1171         void *ptr; // tmp for current entry.
1172         for (int total_records = 0;
1173              total_records < static_cast<int>(hostdb_round_robin_max_count) && nullptr != (ptr = e->ent.h_addr_list[total_records]);
1174              ++total_records) {
1175           if (is_addr_valid(af, ptr)) {
1176             if (!first_record) {
1177               first_record = ptr;
1178             }
1179             // If we have found some records which are invalid, lets just shuffle around them.
1180             // This way we'll end up with e->ent.h_addr_list with all the valid responses at
1181             // the first `valid_records` slots
1182             if (valid_records != total_records) {
1183               e->ent.h_addr_list[valid_records] = e->ent.h_addr_list[total_records];
1184             }
1185 
1186             ++valid_records;
1187           } else {
1188             Warning("Zero address removed from round-robin list for '%s'", hash.host_name);
1189           }
1190         }
1191         if (!first_record) {
1192           failed = true;
1193           is_rr  = false;
1194         }
1195       }
1196     } else if (!failed) {
1197       first_record = e->ent.h_addr_list[0];
1198     } // else first is 0.
1199 
1200     IpAddr tip; // temp storage if needed.
1201 
1202     // In the event that the lookup failed (SOA response-- for example) we want to use hash.host_name, since it'll be ""
1203     const char *aname = (failed || strlen(hash.host_name)) ? hash.host_name : e->ent.h_name;
1204 
1205     const size_t s_size = strlen(aname) + 1;
1206     const size_t rrsize = is_rr ? HostDBRoundRobin::size(valid_records, e->srv_hosts.srv_hosts_length) : 0;
1207     // where in our block of memory we are
1208     int offset = sizeof(HostDBInfo);
1209 
1210     int allocSize = s_size + rrsize; // The extra space we need for the rest of the things
1211 
1212     HostDBInfo *r = HostDBInfo::alloc(allocSize);
1213     Debug("hostdb", "allocating %d bytes for %s with %d RR records at [%p]", allocSize, aname, valid_records, r);
1214     // set up the record
1215     r->key = hash.hash.fold(); // always set the key
1216 
1217     r->hostname_offset = offset;
1218     ink_strlcpy(r->perm_hostname(), aname, s_size);
1219     offset += s_size;
1220 
1221     // If the DNS lookup failed (errors such as SERVFAIL, etc.) but we have an old record
1222     // which is okay with being served stale-- lets continue to serve the stale record as long as
1223     // the record is willing to be served.
1224     bool serve_stale = false;
1225     if (failed && old_r && old_r->serve_stale_but_revalidate()) {
1226       r->free();
1227       r           = old_r.get();
1228       serve_stale = true;
1229     } else if (is_byname()) {
1230       if (first_record) {
1231         ip_addr_set(tip, af, first_record);
1232       }
1233       r = lookup_done(tip, hash.host_name, is_rr, ttl_seconds, failed ? nullptr : &e->srv_hosts, r);
1234     } else if (is_srv()) {
1235       if (!failed) {
1236         tip._family = AF_INET; // force the tip valid, or else the srv will fail
1237       }
1238       r = lookup_done(tip,            /* junk: FIXME: is the code in lookup_done() wrong to NEED this? */
1239                       hash.host_name, /* hostname */
1240                       is_rr,          /* is round robin, doesnt matter for SRV since we recheck getCount() inside lookup_done() */
1241                       ttl_seconds,    /* ttl in seconds */
1242                       failed ? nullptr : &e->srv_hosts, r);
1243     } else if (failed) {
1244       r = lookup_done(tip, hash.host_name, false, ttl_seconds, nullptr, r);
1245     } else {
1246       r = lookup_done(hash.ip, e->ent.h_name, false, ttl_seconds, &e->srv_hosts, r);
1247     }
1248 
1249     // Conditionally make rr record entries
1250     if (is_rr) {
1251       r->app.rr.offset = offset;
1252       // This will only be set if is_rr
1253       HostDBRoundRobin *rr_data = static_cast<HostDBRoundRobin *>(r->rr());
1254       ;
1255       if (is_srv()) {
1256         int skip  = 0;
1257         char *pos = reinterpret_cast<char *>(rr_data) + sizeof(HostDBRoundRobin) + valid_records * sizeof(HostDBInfo);
1258         SRV *q[valid_records];
1259         ink_assert(valid_records <= (int)hostdb_round_robin_max_count);
1260         // sort
1261         for (int i = 0; i < valid_records; ++i) {
1262           q[i] = &e->srv_hosts.hosts[i];
1263         }
1264         for (int i = 0; i < valid_records; ++i) {
1265           for (int ii = i + 1; ii < valid_records; ++ii) {
1266             if (*q[ii] < *q[i]) {
1267               SRV *tmp = q[i];
1268               q[i]     = q[ii];
1269               q[ii]    = tmp;
1270             }
1271           }
1272         }
1273 
1274         rr_data->good = rr_data->rrcount = valid_records;
1275         rr_data->current                 = 0;
1276         for (int i = 0; i < valid_records; ++i) {
1277           SRV *t                     = q[i];
1278           HostDBInfo &item           = rr_data->info(i);
1279           item.round_robin           = 0;
1280           item.round_robin_elt       = 1;
1281           item.reverse_dns           = 0;
1282           item.is_srv                = 1;
1283           item.data.srv.srv_weight   = t->weight;
1284           item.data.srv.srv_priority = t->priority;
1285           item.data.srv.srv_port     = t->port;
1286           item.data.srv.key          = t->key;
1287 
1288           ink_assert((skip + t->host_len) <= e->srv_hosts.srv_hosts_length);
1289 
1290           memcpy(pos + skip, t->host, t->host_len);
1291           item.data.srv.srv_offset = (pos - reinterpret_cast<char *>(rr_data)) + skip;
1292 
1293           skip += t->host_len;
1294 
1295           item.app.allotment.application1 = 0;
1296           item.app.allotment.application2 = 0;
1297           Debug("dns_srv", "inserted SRV RR record [%s] into HostDB with TTL: %d seconds", t->host, ttl_seconds);
1298         }
1299 
1300         // restore
1301         if (old_rr_data) {
1302           for (int i = 0; i < rr_data->rrcount; ++i) {
1303             for (int ii = 0; ii < old_rr_data->rrcount; ++ii) {
1304               if (rr_data->info(i).data.srv.key == old_rr_data->info(ii).data.srv.key) {
1305                 char *new_host = rr_data->info(i).srvname(rr_data);
1306                 char *old_host = old_rr_data->info(ii).srvname(old_rr_data);
1307                 if (!strcmp(new_host, old_host)) {
1308                   rr_data->info(i).app = old_rr_data->info(ii).app;
1309                 }
1310               }
1311             }
1312           }
1313         }
1314       } else { // Otherwise this is a regular dns response
1315         rr_data->good = rr_data->rrcount = valid_records;
1316         rr_data->current                 = 0;
1317         for (int i = 0; i < valid_records; ++i) {
1318           HostDBInfo &item = rr_data->info(i);
1319           ip_addr_set(item.ip(), af, e->ent.h_addr_list[i]);
1320           item.round_robin     = 0;
1321           item.round_robin_elt = 1;
1322           item.reverse_dns     = 0;
1323           item.is_srv          = 0;
1324           if (!restore_info(&item, old_r.get(), old_info, old_rr_data)) {
1325             item.app.allotment.application1 = 0;
1326             item.app.allotment.application2 = 0;
1327           }
1328         }
1329       }
1330     }
1331 
1332     if (!failed && !is_rr && !is_srv()) {
1333       restore_info(r, old_r.get(), old_info, old_rr_data);
1334     }
1335     ink_assert(!r || !r->round_robin || !r->reverse_dns);
1336     ink_assert(failed || !r->round_robin || r->app.rr.offset);
1337 
1338     if (!serve_stale) {
1339       hostDB.refcountcache->put(hash.hash.fold(), r, allocSize, r->expiry_time());
1340     } else {
1341       Warning("Fallback to serving stale record, skip re-update of hostdb for %s", aname);
1342     }
1343 
1344     // try to callback the user
1345     //
1346     if (action.continuation) {
1347       // Check for IP family failover
1348       if (failed && check_for_retry(hash.db_mark, host_res_style)) {
1349         this->refresh_hash(); // family changed if we're doing a retry.
1350         SET_CONTINUATION_HANDLER(this, (HostDBContHandler)&HostDBContinuation::probeEvent);
1351         thread->schedule_in(this, MUTEX_RETRY_DELAY);
1352         return EVENT_CONT;
1353       }
1354 
1355       // We have seen cases were the action.mutex != action.continuation.mutex.  However, it seems that case
1356       // is likely a memory corruption... Thus the introduction of the assert.
1357       // Since reply_to_cont will call the handler on the action.continuation, it is important that we hold
1358       // that mutex.
1359       bool need_to_reschedule = true;
1360       MUTEX_TRY_LOCK(lock, action.mutex, thread);
1361       if (lock.is_locked()) {
1362         if (!action.cancelled) {
1363           if (action.continuation->mutex) {
1364             ink_release_assert(action.continuation->mutex == action.mutex);
1365           }
1366           reply_to_cont(action.continuation, r, is_srv());
1367         }
1368         need_to_reschedule = false;
1369       }
1370 
1371       if (need_to_reschedule) {
1372         SET_HANDLER((HostDBContHandler)&HostDBContinuation::probeEvent);
1373         // Will reschedule on affinity thread or current thread
1374         timeout = eventProcessor.schedule_in(this, HOST_DB_RETRY_PERIOD);
1375         return EVENT_CONT;
1376       }
1377     }
1378 
1379     // Clean ourselves up
1380     hostDB.pending_dns_for_hash(hash.hash).remove(this);
1381 
1382     // wake up everyone else who is waiting
1383     remove_trigger_pending_dns();
1384 
1385     hostdb_cont_free(this);
1386 
1387     // all done, or at least scheduled to be all done
1388     //
1389     return EVENT_DONE;
1390   }
1391 }
1392 
1393 int
iterateEvent(int event,Event * e)1394 HostDBContinuation::iterateEvent(int event, Event *e)
1395 {
1396   Debug("hostdb", "iterateEvent event=%d eventp=%p", event, e);
1397   ink_assert(!link.prev && !link.next);
1398   EThread *t = e ? e->ethread : this_ethread();
1399 
1400   MUTEX_TRY_LOCK(lock, action.mutex, t);
1401   if (!lock.is_locked()) {
1402     Debug("hostdb", "iterateEvent event=%d eventp=%p: reschedule due to not getting action mutex", event, e);
1403     mutex->thread_holding->schedule_in(this, HOST_DB_RETRY_PERIOD);
1404     return EVENT_CONT;
1405   }
1406 
1407   if (action.cancelled) {
1408     hostdb_cont_free(this);
1409     return EVENT_DONE;
1410   }
1411 
1412   // let's iterate through another record and then reschedule ourself.
1413   if (current_iterate_pos < hostDB.refcountcache->partition_count()) {
1414     // TODO: configurable number at a time?
1415     Ptr<ProxyMutex> bucket_mutex = hostDB.refcountcache->get_partition(current_iterate_pos).lock;
1416     MUTEX_TRY_LOCK(lock_bucket, bucket_mutex, t);
1417     if (!lock_bucket.is_locked()) {
1418       // we couldn't get the bucket lock, let's just reschedule and try later.
1419       Debug("hostdb", "iterateEvent event=%d eventp=%p: reschedule due to not getting bucket mutex", event, e);
1420       mutex->thread_holding->schedule_in(this, HOST_DB_RETRY_PERIOD);
1421       return EVENT_CONT;
1422     }
1423 
1424     IntrusiveHashMap<RefCountCacheLinkage> &partMap = hostDB.refcountcache->get_partition(current_iterate_pos).get_map();
1425     for (const auto &it : partMap) {
1426       HostDBInfo *r = static_cast<HostDBInfo *>(it.item.get());
1427       if (r && !r->is_failed()) {
1428         action.continuation->handleEvent(EVENT_INTERVAL, static_cast<void *>(r));
1429       }
1430     }
1431     current_iterate_pos++;
1432   }
1433 
1434   if (current_iterate_pos < hostDB.refcountcache->partition_count()) {
1435     // And reschedule ourselves to pickup the next bucket after HOST_DB_RETRY_PERIOD.
1436     Debug("hostdb", "iterateEvent event=%d eventp=%p: completed current iteration %ld of %ld", event, e, current_iterate_pos,
1437           hostDB.refcountcache->partition_count());
1438     mutex->thread_holding->schedule_in(this, HOST_DB_ITERATE_PERIOD);
1439     return EVENT_CONT;
1440   } else {
1441     Debug("hostdb", "iterateEvent event=%d eventp=%p: completed FINAL iteration %ld", event, e, current_iterate_pos);
1442     // if there are no more buckets, then we're done.
1443     action.continuation->handleEvent(EVENT_DONE, nullptr);
1444     hostdb_cont_free(this);
1445   }
1446 
1447   return EVENT_DONE;
1448 }
1449 
1450 //
1451 // Probe state
1452 //
1453 int
probeEvent(int,Event * e)1454 HostDBContinuation::probeEvent(int /* event ATS_UNUSED */, Event *e)
1455 {
1456   ink_assert(!link.prev && !link.next);
1457   EThread *t = e ? e->ethread : this_ethread();
1458 
1459   if (timeout) {
1460     timeout->cancel(this);
1461     timeout = nullptr;
1462   }
1463 
1464   MUTEX_TRY_LOCK(lock, action.mutex, t);
1465 
1466   // Separating lock checks here to make sure things don't break
1467   // when we check if the action is cancelled.
1468   if (!lock.is_locked()) {
1469     timeout = mutex->thread_holding->schedule_in(this, HOST_DB_RETRY_PERIOD);
1470     return EVENT_CONT;
1471   }
1472 
1473   if (action.cancelled) {
1474     hostdb_cont_free(this);
1475     return EVENT_DONE;
1476   }
1477 
1478   //  If the action.continuation->mutex != action.mutex, we have a use after free/realloc
1479   ink_release_assert(!action.continuation || action.continuation->mutex == action.mutex);
1480 
1481   if (!hostdb_enable || (!*hash.host_name && !hash.ip.isValid())) {
1482     if (action.continuation) {
1483       action.continuation->handleEvent(EVENT_HOST_DB_LOOKUP, nullptr);
1484     }
1485     hostdb_cont_free(this);
1486     return EVENT_DONE;
1487   }
1488 
1489   if (!force_dns) {
1490     // Do the probe
1491     //
1492     Ptr<HostDBInfo> r = probe(mutex, hash, false);
1493 
1494     if (r) {
1495       HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
1496     }
1497 
1498     if (action.continuation && r) {
1499       reply_to_cont(action.continuation, r.get(), is_srv());
1500     }
1501 
1502     // If it succeeds or it was a remote probe, we are done
1503     //
1504     if (r) {
1505       hostdb_cont_free(this);
1506       return EVENT_DONE;
1507     }
1508   }
1509   // If there are no remote nodes to probe, do a DNS lookup
1510   //
1511   do_dns();
1512   return EVENT_DONE;
1513 }
1514 
1515 int
set_check_pending_dns()1516 HostDBContinuation::set_check_pending_dns()
1517 {
1518   Queue<HostDBContinuation> &q = hostDB.pending_dns_for_hash(hash.hash);
1519   this->setThreadAffinity(this_ethread());
1520   if (q.in(this)) {
1521     HOSTDB_INCREMENT_DYN_STAT(hostdb_insert_duplicate_to_pending_dns_stat);
1522     Debug("hostdb", "Skip the insertion of the same continuation to pending dns");
1523     return false;
1524   }
1525   HostDBContinuation *c = q.head;
1526   for (; c; c = static_cast<HostDBContinuation *>(c->link.next)) {
1527     if (hash.hash == c->hash.hash) {
1528       Debug("hostdb", "enqueuing additional request");
1529       q.enqueue(this);
1530       return false;
1531     }
1532   }
1533   q.enqueue(this);
1534   return true;
1535 }
1536 
1537 void
remove_trigger_pending_dns()1538 HostDBContinuation::remove_trigger_pending_dns()
1539 {
1540   Queue<HostDBContinuation> &q = hostDB.pending_dns_for_hash(hash.hash);
1541   q.remove(this);
1542   HostDBContinuation *c = q.head;
1543   Queue<HostDBContinuation> qq;
1544   while (c) {
1545     HostDBContinuation *n = static_cast<HostDBContinuation *>(c->link.next);
1546     if (hash.hash == c->hash.hash) {
1547       Debug("hostdb", "dequeuing additional request");
1548       q.remove(c);
1549       qq.enqueue(c);
1550     }
1551     c = n;
1552   }
1553   EThread *thread = this_ethread();
1554   while ((c = qq.dequeue())) {
1555     // resume all queued HostDBCont in the thread associated with the netvc to avoid nethandler locking issues.
1556     EThread *affinity_thread = c->getThreadAffinity();
1557     if (!affinity_thread || affinity_thread == thread) {
1558       c->handleEvent(EVENT_IMMEDIATE, nullptr);
1559     } else {
1560       if (c->timeout) {
1561         c->timeout->cancel();
1562       }
1563       c->timeout = eventProcessor.schedule_imm(c);
1564     }
1565   }
1566 }
1567 
1568 //
1569 // Query the DNS processor
1570 //
1571 void
do_dns()1572 HostDBContinuation::do_dns()
1573 {
1574   ink_assert(!action.cancelled);
1575   if (is_byname()) {
1576     Debug("hostdb", "DNS %s", hash.host_name);
1577     IpAddr tip;
1578     if (0 == tip.load(hash.host_name)) {
1579       // check 127.0.0.1 format // What the heck does that mean? - AMC
1580       if (action.continuation) {
1581         HostDBInfo *r = lookup_done(tip, hash.host_name, false, HOST_DB_MAX_TTL, nullptr);
1582 
1583         reply_to_cont(action.continuation, r);
1584       }
1585       hostdb_cont_free(this);
1586       return;
1587     }
1588     ts::ConstBuffer hname(hash.host_name, hash.host_len);
1589     Ptr<RefCountedHostsFileMap> current_host_file_map = hostDB.hosts_file_ptr;
1590     HostsFileMap::iterator find_result                = current_host_file_map->hosts_file_map.find(hname);
1591     if (find_result != current_host_file_map->hosts_file_map.end()) {
1592       if (action.continuation) {
1593         // Set the TTL based on how often we stat() the host file
1594         HostDBInfo *r = lookup_done(IpAddr(find_result->second), hash.host_name, false, hostdb_hostfile_check_interval, nullptr);
1595         reply_to_cont(action.continuation, r);
1596       }
1597       hostdb_cont_free(this);
1598       return;
1599     }
1600   }
1601   if (hostdb_lookup_timeout) {
1602     timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout));
1603   } else {
1604     timeout = nullptr;
1605   }
1606   if (set_check_pending_dns()) {
1607     DNSProcessor::Options opt;
1608     opt.timeout        = dns_lookup_timeout;
1609     opt.host_res_style = host_res_style_for(hash.db_mark);
1610     SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsEvent);
1611     if (is_byname()) {
1612       if (hash.dns_server) {
1613         opt.handler = hash.dns_server->x_dnsH;
1614       }
1615       pending_action = dnsProcessor.gethostbyname(this, hash.host_name, opt);
1616     } else if (is_srv()) {
1617       Debug("dns_srv", "SRV lookup of %s", hash.host_name);
1618       pending_action = dnsProcessor.getSRVbyname(this, hash.host_name, opt);
1619     } else {
1620       ip_text_buffer ipb;
1621       Debug("hostdb", "DNS IP %s", hash.ip.toString(ipb, sizeof ipb));
1622       pending_action = dnsProcessor.gethostbyaddr(this, &hash.ip, opt);
1623     }
1624   } else {
1625     SET_HANDLER((HostDBContHandler)&HostDBContinuation::dnsPendingEvent);
1626   }
1627 }
1628 
1629 //
1630 // Background event
1631 // Just increment the current_interval.  Might do other stuff
1632 // here, like move records to the current position in the cluster.
1633 //
1634 int
backgroundEvent(int,Event *)1635 HostDBContinuation::backgroundEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
1636 {
1637   // No nothing if hosts file checking is not enabled.
1638   if (hostdb_hostfile_check_interval == 0) {
1639     return EVENT_CONT;
1640   }
1641 
1642   hostdb_current_interval = ink_time();
1643 
1644   if ((hostdb_current_interval - hostdb_last_interval) > hostdb_hostfile_check_interval) {
1645     bool update_p = false; // do we need to reparse the file and update?
1646     struct stat info;
1647     char path[sizeof(hostdb_hostfile_path)];
1648 
1649     REC_ReadConfigString(path, "proxy.config.hostdb.host_file.path", sizeof(path));
1650     if (0 != strcasecmp(hostdb_hostfile_path, path)) {
1651       Debug("hostdb", "Update host file '%s' -> '%s'", (*hostdb_hostfile_path ? hostdb_hostfile_path : "*-none-*"),
1652             (*path ? path : "*-none-*"));
1653       // path to hostfile changed
1654       hostdb_hostfile_update_timestamp = 0; // never updated from this file
1655       if ('\0' != *path) {
1656         memcpy(hostdb_hostfile_path, path, sizeof(hostdb_hostfile_path));
1657       } else {
1658         hostdb_hostfile_path[0] = 0; // mark as not there
1659       }
1660       update_p = true;
1661     } else {
1662       hostdb_last_interval = hostdb_current_interval;
1663       if (*hostdb_hostfile_path) {
1664         if (0 == stat(hostdb_hostfile_path, &info)) {
1665           if (info.st_mtime > static_cast<time_t>(hostdb_hostfile_update_timestamp)) {
1666             update_p = true; // same file but it's changed.
1667           }
1668         } else {
1669           Debug("hostdb", "Failed to stat host file '%s'", hostdb_hostfile_path);
1670         }
1671       }
1672     }
1673     if (update_p) {
1674       Debug("hostdb", "Updating from host file");
1675       ParseHostFile(hostdb_hostfile_path, hostdb_hostfile_check_interval);
1676     }
1677   }
1678 
1679   return EVENT_CONT;
1680 }
1681 
1682 char *
hostname() const1683 HostDBInfo::hostname() const
1684 {
1685   if (!reverse_dns) {
1686     return nullptr;
1687   }
1688 
1689   return (char *)this + data.hostname_offset;
1690 }
1691 
1692 /*
1693  * The perm_hostname exists for all records not just reverse dns records.
1694  */
1695 char *
perm_hostname() const1696 HostDBInfo::perm_hostname() const
1697 {
1698   if (hostname_offset == 0) {
1699     return nullptr;
1700   }
1701 
1702   return (char *)this + hostname_offset;
1703 }
1704 
1705 HostDBRoundRobin *
rr()1706 HostDBInfo::rr()
1707 {
1708   if (!round_robin) {
1709     return nullptr;
1710   }
1711 
1712   return reinterpret_cast<HostDBRoundRobin *>(reinterpret_cast<char *>(this) + this->app.rr.offset);
1713 }
1714 
1715 struct ShowHostDB;
1716 using ShowHostDBEventHandler = int (ShowHostDB::*)(int, Event *);
1717 struct ShowHostDB : public ShowCont {
1718   char *name;
1719   uint16_t port;
1720   IpEndpoint ip;
1721   bool force;
1722   bool output_json;
1723   int records_seen;
1724 
1725   int
showMainShowHostDB1726   showMain(int event, Event *e)
1727   {
1728     CHECK_SHOW(begin("HostDB"));
1729     CHECK_SHOW(show("<a href=\"./showall\">Show all HostDB records<a/><hr>"));
1730     CHECK_SHOW(show("<form method = GET action = \"./name\">\n"
1731                     "Lookup by name (e.g. trafficserver.apache.org):<br>\n"
1732                     "<input type=text name=name size=64 maxlength=256>\n"
1733                     "</form>\n"
1734                     "<form method = GET action = \"./ip\">\n"
1735                     "Lookup by IP (e.g. 127.0.0.1):<br>\n"
1736                     "<input type=text name=ip size=64 maxlength=256>\n"
1737                     "</form>\n"
1738                     "<form method = GET action = \"./nameforce\">\n"
1739                     "Force DNS by name (e.g. trafficserver.apache.org):<br>\n"
1740                     "<input type=text name=name size=64 maxlength=256>\n"
1741                     "</form>\n"));
1742     return complete(event, e);
1743   }
1744 
1745   int
showLookupShowHostDB1746   showLookup(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
1747   {
1748     SET_HANDLER(&ShowHostDB::showLookupDone);
1749     if (name) {
1750       HostDBProcessor::Options opts;
1751       opts.port  = port;
1752       opts.flags = HostDBProcessor::HOSTDB_DO_NOT_FORCE_DNS;
1753       hostDBProcessor.getbynameport_re(this, name, strlen(name), opts);
1754     } else {
1755       hostDBProcessor.getbyaddr_re(this, &ip.sa);
1756     }
1757     return EVENT_CONT;
1758   }
1759 
1760   int
showAllShowHostDB1761   showAll(int event, Event *e)
1762   {
1763     if (!output_json) {
1764       CHECK_SHOW(begin("HostDB All Records"));
1765       CHECK_SHOW(show("<hr>"));
1766     } else {
1767       CHECK_SHOW(show("["));
1768     }
1769     SET_HANDLER(&ShowHostDB::showAllEvent);
1770     hostDBProcessor.iterate(this);
1771     return EVENT_CONT;
1772   }
1773 
1774   int
showAllEventShowHostDB1775   showAllEvent(int event, Event *e)
1776   {
1777     if (event == EVENT_INTERVAL) {
1778       HostDBInfo *r = reinterpret_cast<HostDBInfo *>(e);
1779       if (output_json && records_seen++ > 0) {
1780         CHECK_SHOW(show(",")); // we need to separate records
1781       }
1782       showOne(r, false, event, e);
1783       if (r->round_robin) {
1784         HostDBRoundRobin *rr_data = r->rr();
1785         if (rr_data) {
1786           if (!output_json) {
1787             CHECK_SHOW(show("<table border=1>\n"));
1788             CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Total", rr_data->rrcount));
1789             CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Good", rr_data->good));
1790             CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Current", rr_data->current));
1791             CHECK_SHOW(show("</table>\n"));
1792           } else {
1793             CHECK_SHOW(show(",\"%s\":\"%d\",", "rr_total", rr_data->rrcount));
1794             CHECK_SHOW(show("\"%s\":\"%d\",", "rr_good", rr_data->good));
1795             CHECK_SHOW(show("\"%s\":\"%d\",", "rr_current", rr_data->current));
1796             CHECK_SHOW(show("\"rr_records\":["));
1797           }
1798 
1799           for (int i = 0; i < rr_data->rrcount; i++) {
1800             showOne(&rr_data->info(i), true, event, e, rr_data);
1801             if (output_json) {
1802               CHECK_SHOW(show("}")); // we need to separate records
1803               if (i < (rr_data->rrcount - 1))
1804                 CHECK_SHOW(show(","));
1805             }
1806           }
1807 
1808           if (!output_json) {
1809             CHECK_SHOW(show("<br />\n<br />\n"));
1810           } else {
1811             CHECK_SHOW(show("]"));
1812           }
1813         }
1814       }
1815 
1816       if (output_json) {
1817         CHECK_SHOW(show("}"));
1818       }
1819 
1820     } else if (event == EVENT_DONE) {
1821       if (output_json) {
1822         CHECK_SHOW(show("]"));
1823         return completeJson(event, e);
1824       } else {
1825         return complete(event, e);
1826       }
1827     } else {
1828       ink_assert(!"unexpected event");
1829     }
1830     return EVENT_CONT;
1831   }
1832 
1833   int
showOneShowHostDB1834   showOne(HostDBInfo *r, bool rr, int event, Event *e, HostDBRoundRobin *hostdb_rr = nullptr)
1835   {
1836     ip_text_buffer b;
1837     if (!output_json) {
1838       CHECK_SHOW(show("<table border=1>\n"));
1839       CHECK_SHOW(show("<tr><td>%s</td><td>%s%s %s</td></tr>\n", "Type", r->round_robin ? "Round-Robin" : "",
1840                       r->reverse_dns ? "Reverse DNS" : "", r->is_srv ? "SRV" : "DNS"));
1841 
1842       if (r->perm_hostname()) {
1843         CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Hostname", r->perm_hostname()));
1844       } else if (rr && r->is_srv && hostdb_rr) {
1845         CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Hostname", r->srvname(hostdb_rr)));
1846       }
1847 
1848       // Let's display the hash.
1849       CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "App1", r->app.allotment.application1));
1850       CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "App2", r->app.allotment.application2));
1851       CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "LastFailure", r->app.http_data.last_failure));
1852       if (!rr) {
1853         CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Stale", r->is_ip_stale() ? "Yes" : "No"));
1854         CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Timed-Out", r->is_ip_timeout() ? "Yes" : "No"));
1855         CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "TTL", r->ip_time_remaining()));
1856       }
1857 
1858       if (rr && r->is_srv) {
1859         CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Weight", r->data.srv.srv_weight));
1860         CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Priority", r->data.srv.srv_priority));
1861         CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Port", r->data.srv.srv_port));
1862         CHECK_SHOW(show("<tr><td>%s</td><td>%x</td></tr>\n", "Key", r->data.srv.key));
1863       } else if (!r->is_srv) {
1864         CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "IP", ats_ip_ntop(r->ip(), b, sizeof b)));
1865       }
1866 
1867       CHECK_SHOW(show("</table>\n"));
1868     } else {
1869       CHECK_SHOW(show("{"));
1870       CHECK_SHOW(show("\"%s\":\"%s%s%s\",", "type", (r->round_robin && !r->is_srv) ? "roundrobin" : "",
1871                       r->reverse_dns ? "reversedns" : "", r->is_srv ? "srv" : "dns"));
1872 
1873       if (r->perm_hostname()) {
1874         CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", r->perm_hostname()));
1875       } else if (rr && r->is_srv && hostdb_rr) {
1876         CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", r->srvname(hostdb_rr)));
1877       }
1878 
1879       CHECK_SHOW(show("\"%s\":\"%u\",", "app1", r->app.allotment.application1));
1880       CHECK_SHOW(show("\"%s\":\"%u\",", "app2", r->app.allotment.application2));
1881       CHECK_SHOW(show("\"%s\":\"%u\",", "lastfailure", r->app.http_data.last_failure));
1882       if (!rr) {
1883         CHECK_SHOW(show("\"%s\":\"%s\",", "stale", r->is_ip_stale() ? "yes" : "no"));
1884         CHECK_SHOW(show("\"%s\":\"%s\",", "timedout", r->is_ip_timeout() ? "yes" : "no"));
1885         CHECK_SHOW(show("\"%s\":\"%d\",", "ttl", r->ip_time_remaining()));
1886       }
1887 
1888       if (rr && r->is_srv) {
1889         CHECK_SHOW(show("\"%s\":\"%d\",", "weight", r->data.srv.srv_weight));
1890         CHECK_SHOW(show("\"%s\":\"%d\",", "priority", r->data.srv.srv_priority));
1891         CHECK_SHOW(show("\"%s\":\"%d\",", "port", r->data.srv.srv_port));
1892         CHECK_SHOW(show("\"%s\":\"%x\",", "key", r->data.srv.key));
1893       } else if (!r->is_srv) {
1894         CHECK_SHOW(show("\"%s\":\"%s\"", "ip", ats_ip_ntop(r->ip(), b, sizeof b)));
1895       }
1896     }
1897     return EVENT_CONT;
1898   }
1899 
1900   int
showLookupDoneShowHostDB1901   showLookupDone(int event, Event *e)
1902   {
1903     HostDBInfo *r = reinterpret_cast<HostDBInfo *>(e);
1904 
1905     CHECK_SHOW(begin("HostDB Lookup"));
1906     if (name) {
1907       CHECK_SHOW(show("<H2>%s</H2>\n", name));
1908     } else {
1909       CHECK_SHOW(show("<H2>%u.%u.%u.%u</H2>\n", PRINT_IP(ip)));
1910     }
1911     if (r) {
1912       showOne(r, false, event, e);
1913       if (r->round_robin) {
1914         HostDBRoundRobin *rr_data = r->rr();
1915         if (rr_data) {
1916           CHECK_SHOW(show("<table border=1>\n"));
1917           CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Total", rr_data->rrcount));
1918           CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Good", rr_data->good));
1919           CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Current", rr_data->current));
1920           CHECK_SHOW(show("</table>\n"));
1921 
1922           for (int i = 0; i < rr_data->rrcount; i++) {
1923             showOne(&rr_data->info(i), true, event, e, rr_data);
1924           }
1925         }
1926       }
1927     } else {
1928       if (!name) {
1929         ip_text_buffer b;
1930         CHECK_SHOW(show("<H2>%s Not Found</H2>\n", ats_ip_ntop(&ip.sa, b, sizeof b)));
1931       } else {
1932         CHECK_SHOW(show("<H2>%s Not Found</H2>\n", name));
1933       }
1934     }
1935     return complete(event, e);
1936   }
1937 
ShowHostDBShowHostDB1938   ShowHostDB(Continuation *c, HTTPHdr *h)
1939     : ShowCont(c, h), name(nullptr), port(0), force(false), output_json(false), records_seen(0)
1940   {
1941     ats_ip_invalidate(&ip);
1942     SET_HANDLER(&ShowHostDB::showMain);
1943   }
1944 };
1945 
1946 #define STR_LEN_EQ_PREFIX(_x, _l, _s) (!ptr_len_ncasecmp(_x, _l, _s, sizeof(_s) - 1))
1947 
1948 static Action *
register_ShowHostDB(Continuation * c,HTTPHdr * h)1949 register_ShowHostDB(Continuation *c, HTTPHdr *h)
1950 {
1951   ShowHostDB *s = new ShowHostDB(c, h);
1952   int path_len;
1953   const char *path = h->url_get()->path_get(&path_len);
1954 
1955   SET_CONTINUATION_HANDLER(s, &ShowHostDB::showMain);
1956   if (STR_LEN_EQ_PREFIX(path, path_len, "ip")) {
1957     s->force = !ptr_len_ncasecmp(path + 3, path_len - 3, "force", 5);
1958     int query_len;
1959     const char *query = h->url_get()->query_get(&query_len);
1960     s->sarg           = ats_strndup(query, query_len);
1961     char *gn          = nullptr;
1962     if (s->sarg) {
1963       gn = static_cast<char *>(memchr(s->sarg, '=', strlen(s->sarg)));
1964     }
1965     if (gn) {
1966       ats_ip_pton(gn + 1, &s->ip); // hope that's null terminated.
1967     }
1968     SET_CONTINUATION_HANDLER(s, &ShowHostDB::showLookup);
1969   } else if (STR_LEN_EQ_PREFIX(path, path_len, "name")) {
1970     s->force = !ptr_len_ncasecmp(path + 5, path_len - 5, "force", 5);
1971     int query_len;
1972     const char *query = h->url_get()->query_get(&query_len);
1973     s->sarg           = ats_strndup(query, query_len);
1974     char *gn          = nullptr;
1975     if (s->sarg) {
1976       gn = static_cast<char *>(memchr(s->sarg, '=', strlen(s->sarg)));
1977     }
1978     if (gn) {
1979       s->name   = gn + 1;
1980       char *pos = strstr(s->name, "%3A");
1981       if (pos != nullptr) {
1982         s->port = atoi(pos + 3);
1983         *pos    = '\0'; // Null terminate name
1984       } else {
1985         s->port = 0;
1986       }
1987     }
1988     SET_CONTINUATION_HANDLER(s, &ShowHostDB::showLookup);
1989   } else if (STR_LEN_EQ_PREFIX(path, path_len, "showall")) {
1990     int query_len     = 0;
1991     const char *query = h->url_get()->query_get(&query_len);
1992     if (query && query_len && strstr(query, "json")) {
1993       s->output_json = true;
1994     }
1995     Debug("hostdb", "dumping all hostdb records");
1996     SET_CONTINUATION_HANDLER(s, &ShowHostDB::showAll);
1997   }
1998   this_ethread()->schedule_imm(s);
1999   return &s->action;
2000 }
2001 
2002 static constexpr int HOSTDB_TEST_MAX_OUTSTANDING = 20;
2003 static constexpr int HOSTDB_TEST_LENGTH          = 200;
2004 
2005 struct HostDBTestReverse;
2006 using HostDBTestReverseHandler = int (HostDBTestReverse::*)(int, void *);
2007 struct HostDBTestReverse : public Continuation {
2008   RegressionTest *test;
2009   int type;
2010   int *status;
2011 
2012   int outstanding = 0;
2013   int total       = 0;
2014   std::ranlux48 randu;
2015 
2016   int
mainEventHostDBTestReverse2017   mainEvent(int event, Event *e)
2018   {
2019     if (event == EVENT_HOST_DB_LOOKUP) {
2020       HostDBInfo *i = reinterpret_cast<HostDBInfo *>(e);
2021       if (i) {
2022         rprintf(test, "HostDBTestReverse: reversed %s\n", i->hostname());
2023       }
2024       outstanding--;
2025     }
2026     while (outstanding < HOSTDB_TEST_MAX_OUTSTANDING && total < HOSTDB_TEST_LENGTH) {
2027       IpEndpoint ip;
2028       ip.assign(IpAddr(static_cast<in_addr_t>(randu())));
2029       outstanding++;
2030       total++;
2031       if (!(outstanding % 100)) {
2032         rprintf(test, "HostDBTestReverse: %d\n", total);
2033       }
2034       hostDBProcessor.getbyaddr_re(this, &ip.sa);
2035     }
2036     if (!outstanding) {
2037       rprintf(test, "HostDBTestReverse: done\n");
2038       *status = REGRESSION_TEST_PASSED; //  TODO: actually verify it passed
2039       delete this;
2040     }
2041     return EVENT_CONT;
2042   }
HostDBTestReverseHostDBTestReverse2043   HostDBTestReverse(RegressionTest *t, int atype, int *astatus)
2044     : Continuation(new_ProxyMutex()), test(t), type(atype), status(astatus)
2045   {
2046     SET_HANDLER((HostDBTestReverseHandler)&HostDBTestReverse::mainEvent);
2047     randu.seed(std::chrono::system_clock::now().time_since_epoch().count());
2048   }
2049 };
2050 
2051 #if TS_HAS_TESTS
REGRESSION_TEST(HostDBTests)2052 REGRESSION_TEST(HostDBTests)(RegressionTest *t, int atype, int *pstatus)
2053 {
2054   eventProcessor.schedule_imm(new HostDBTestReverse(t, atype, pstatus), ET_CACHE);
2055 }
2056 #endif
2057 
2058 RecRawStatBlock *hostdb_rsb;
2059 
2060 void
ink_hostdb_init(ts::ModuleVersion v)2061 ink_hostdb_init(ts::ModuleVersion v)
2062 {
2063   static int init_called = 0;
2064 
2065   ink_release_assert(v.check(HOSTDB_MODULE_INTERNAL_VERSION));
2066   if (init_called) {
2067     return;
2068   }
2069 
2070   init_called = 1;
2071   // do one time stuff
2072   // create a stat block for HostDBStats
2073   hostdb_rsb = RecAllocateRawStatBlock(static_cast<int>(HostDB_Stat_Count));
2074 
2075   //
2076   // Register stats
2077   //
2078 
2079   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS, "proxy.process.hostdb.total_lookups", RECD_INT, RECP_PERSISTENT,
2080                      (int)hostdb_total_lookups_stat, RecRawStatSyncSum);
2081 
2082   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS, "proxy.process.hostdb.total_hits", RECD_INT, RECP_PERSISTENT,
2083                      (int)hostdb_total_hits_stat, RecRawStatSyncSum);
2084 
2085   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS, "proxy.process.hostdb.ttl", RECD_FLOAT, RECP_PERSISTENT, (int)hostdb_ttl_stat,
2086                      RecRawStatSyncAvg);
2087 
2088   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS, "proxy.process.hostdb.ttl_expires", RECD_INT, RECP_PERSISTENT,
2089                      (int)hostdb_ttl_expires_stat, RecRawStatSyncSum);
2090 
2091   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS, "proxy.process.hostdb.re_dns_on_reload", RECD_INT, RECP_PERSISTENT,
2092                      (int)hostdb_re_dns_on_reload_stat, RecRawStatSyncSum);
2093 
2094   RecRegisterRawStat(hostdb_rsb, RECT_PROCESS, "proxy.process.hostdb.insert_duplicate_to_pending_dns", RECD_INT, RECP_PERSISTENT,
2095                      (int)hostdb_insert_duplicate_to_pending_dns_stat, RecRawStatSyncSum);
2096 
2097   ts_host_res_global_init();
2098 }
2099 
2100 /// Pair of IP address and host name from a host file.
2101 struct HostFilePair {
2102   using self = HostFilePair;
2103   IpAddr ip;
2104   const char *name;
2105 };
2106 
2107 struct HostDBFileContinuation : public Continuation {
2108   using self = HostDBFileContinuation;
2109   using Keys = std::vector<CryptoHash>;
2110 
2111   int idx          = 0;       ///< Working index.
2112   const char *name = nullptr; ///< Host name (just for debugging)
2113   Keys *keys       = nullptr; ///< Entries from file.
2114   CryptoHash hash;            ///< Key for entry.
2115   ats_scoped_str path;        ///< Used to keep the host file name around.
2116 
HostDBFileContinuationHostDBFileContinuation2117   HostDBFileContinuation() : Continuation(nullptr) {}
2118   /// Finish update
2119   static void finish(Keys *keys ///< Valid keys from update.
2120   );
2121   /// Clean up this instance.
2122   void destroy();
2123 };
2124 
2125 ClassAllocator<HostDBFileContinuation> hostDBFileContAllocator("hostDBFileContAllocator");
2126 
2127 void
destroy()2128 HostDBFileContinuation::destroy()
2129 {
2130   this->~HostDBFileContinuation();
2131   hostDBFileContAllocator.free(this);
2132 }
2133 
2134 // Host file processing globals.
2135 
2136 // We can't allow more than one update to be
2137 // proceeding at a time in any case so we might as well make these
2138 // globals.
2139 int HostDBFileUpdateActive = 0;
2140 
2141 static void
ParseHostLine(Ptr<RefCountedHostsFileMap> & map,char * l)2142 ParseHostLine(Ptr<RefCountedHostsFileMap> &map, char *l)
2143 {
2144   Tokenizer elts(" \t");
2145   int n_elts = elts.Initialize(l, SHARE_TOKS);
2146 
2147   // Elements should be the address then a list of host names.
2148   // Don't use RecHttpLoadIp because the address *must* be literal.
2149   IpAddr ip;
2150   if (n_elts > 1 && 0 == ip.load(elts[0])) {
2151     for (int i = 1; i < n_elts; ++i) {
2152       ts::ConstBuffer name(elts[i], strlen(elts[i]));
2153       // If we don't have an entry already (host files only support single IPs for a given name)
2154       if (map->hosts_file_map.find(name) == map->hosts_file_map.end()) {
2155         map->hosts_file_map[name] = ip;
2156       }
2157     }
2158   }
2159 }
2160 
2161 void
ParseHostFile(const char * path,unsigned int hostdb_hostfile_check_interval_parse)2162 ParseHostFile(const char *path, unsigned int hostdb_hostfile_check_interval_parse)
2163 {
2164   Ptr<RefCountedHostsFileMap> parsed_hosts_file_ptr;
2165 
2166   // Test and set for update in progress.
2167   if (0 != ink_atomic_swap(&HostDBFileUpdateActive, 1)) {
2168     Debug("hostdb", "Skipped load of host file because update already in progress");
2169     return;
2170   }
2171   Debug("hostdb", "Loading host file '%s'", path);
2172 
2173   if (*path) {
2174     ats_scoped_fd fd(open(path, O_RDONLY));
2175     if (fd >= 0) {
2176       struct stat info;
2177       if (0 == fstat(fd, &info)) {
2178         // +1 in case no terminating newline
2179         int64_t size = info.st_size + 1;
2180 
2181         parsed_hosts_file_ptr               = new RefCountedHostsFileMap;
2182         parsed_hosts_file_ptr->HostFileText = static_cast<char *>(ats_malloc(size));
2183         if (parsed_hosts_file_ptr->HostFileText) {
2184           char *base = parsed_hosts_file_ptr->HostFileText;
2185           char *limit;
2186 
2187           size   = read(fd, parsed_hosts_file_ptr->HostFileText, info.st_size);
2188           limit  = parsed_hosts_file_ptr->HostFileText + size;
2189           *limit = 0;
2190 
2191           // We need to get a list of all name/addr pairs so that we can
2192           // group names for round robin records. Also note that the
2193           // pairs have pointer back in to the text storage for the file
2194           // so we need to keep that until we're done with @a pairs.
2195           while (base < limit) {
2196             char *spot = strchr(base, '\n');
2197 
2198             // terminate the line.
2199             if (nullptr == spot) {
2200               spot = limit; // no trailing EOL, grab remaining
2201             } else {
2202               *spot = 0;
2203             }
2204 
2205             while (base < spot && isspace(*base)) {
2206               ++base; // skip leading ws
2207             }
2208             if (*base != '#' && base < spot) { // non-empty non-comment line
2209               ParseHostLine(parsed_hosts_file_ptr, base);
2210             }
2211             base = spot + 1;
2212           }
2213 
2214           hostdb_hostfile_update_timestamp = hostdb_current_interval;
2215         }
2216       }
2217     }
2218   }
2219 
2220   // Swap the pointer
2221   if (parsed_hosts_file_ptr != nullptr) {
2222     hostDB.hosts_file_ptr = parsed_hosts_file_ptr;
2223   }
2224   // Mark this one as completed, so we can allow another update to happen
2225   HostDBFileUpdateActive = 0;
2226 }
2227 
2228 //
2229 // Regression tests
2230 //
2231 // Take a started hostDB and fill it up and make sure it doesn't explode
2232 #if TS_HAS_TESTS
2233 struct HostDBRegressionContinuation;
2234 
2235 struct HostDBRegressionContinuation : public Continuation {
2236   int hosts;
2237   const char **hostnames;
2238   RegressionTest *test;
2239   int type;
2240   int *status;
2241 
2242   int success;
2243   int failure;
2244   int outstanding;
2245   int i;
2246 
2247   int
mainEventHostDBRegressionContinuation2248   mainEvent(int event, HostDBInfo *r)
2249   {
2250     (void)event;
2251 
2252     if (event == EVENT_INTERVAL) {
2253       rprintf(test, "hosts=%d success=%d failure=%d outstanding=%d i=%d\n", hosts, success, failure, outstanding, i);
2254     }
2255     if (event == EVENT_HOST_DB_LOOKUP) {
2256       --outstanding;
2257       // since this is a lookup done, data is either hostdbInfo or nullptr
2258       if (r) {
2259         rprintf(test, "hostdbinfo r=%x\n", r);
2260         char const *hname = r->perm_hostname();
2261         if (nullptr == hname) {
2262           hname = "(null)";
2263         }
2264         rprintf(test, "hostdbinfo hostname=%s\n", hname);
2265         rprintf(test, "hostdbinfo rr %x\n", r->rr());
2266         // If RR, print all of the enclosed records
2267         if (r->rr()) {
2268           rprintf(test, "hostdbinfo good=%d\n", r->rr()->good);
2269           for (int x = 0; x < r->rr()->good; x++) {
2270             ip_port_text_buffer ip_buf;
2271             ats_ip_ntop(r->rr()->info(x).ip(), ip_buf, sizeof(ip_buf));
2272             rprintf(test, "hostdbinfo RR%d ip=%s\n", x, ip_buf);
2273           }
2274         } else { // Otherwise, just the one will do
2275           ip_port_text_buffer ip_buf;
2276           ats_ip_ntop(r->ip(), ip_buf, sizeof(ip_buf));
2277           rprintf(test, "hostdbinfo A ip=%s\n", ip_buf);
2278         }
2279         ++success;
2280       } else {
2281         ++failure;
2282       }
2283     }
2284 
2285     if (i < hosts) {
2286       hostDBProcessor.getbyname_re(this, hostnames[i++], 0);
2287       return EVENT_CONT;
2288     } else {
2289       rprintf(test, "HostDBTestRR: %d outstanding %d success %d failure\n", outstanding, success, failure);
2290       if (success == hosts) {
2291         *status = REGRESSION_TEST_PASSED;
2292       } else {
2293         *status = REGRESSION_TEST_FAILED;
2294       }
2295       return EVENT_DONE;
2296     }
2297   }
2298 
HostDBRegressionContinuationHostDBRegressionContinuation2299   HostDBRegressionContinuation(int ahosts, const char **ahostnames, RegressionTest *t, int atype, int *astatus)
2300     : Continuation(new_ProxyMutex()),
2301       hosts(ahosts),
2302       hostnames(ahostnames),
2303       test(t),
2304       type(atype),
2305       status(astatus),
2306       success(0),
2307       failure(0),
2308       i(0)
2309   {
2310     outstanding = ahosts;
2311     SET_HANDLER(&HostDBRegressionContinuation::mainEvent);
2312   }
2313 };
2314 
2315 static const char *dns_test_hosts[] = {
2316   "www.apple.com", "www.ibm.com", "www.microsoft.com",
2317   "www.coke.com", // RR record
2318   "4.2.2.2",      // An IP-- since we don't expect resolution
2319   "127.0.0.1",    // loopback since it has some special handling
2320 };
2321 
REGRESSION_TEST(HostDBProcessor)2322 REGRESSION_TEST(HostDBProcessor)(RegressionTest *t, int atype, int *pstatus)
2323 {
2324   eventProcessor.schedule_in(new HostDBRegressionContinuation(6, dns_test_hosts, t, atype, pstatus), HRTIME_SECONDS(1));
2325 }
2326 
2327 #endif
2328