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