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