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