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 /****************************************************************************
25   P_HostDBProcessor.h
26  ****************************************************************************/
27 
28 #pragma once
29 
30 #include "I_HostDBProcessor.h"
31 #include "tscore/TsBuffer.h"
32 
33 //
34 // Data
35 //
36 
37 extern int hostdb_enable;
38 extern int hostdb_migrate_on_demand;
39 extern int hostdb_lookup_timeout;
40 extern int hostdb_re_dns_on_reload;
41 
42 // 0 = obey, 1 = ignore, 2 = min(X,ttl), 3 = max(X,ttl)
43 enum {
44   TTL_OBEY,
45   TTL_IGNORE,
46   TTL_MIN,
47   TTL_MAX,
48 };
49 
50 extern int hostdb_ttl_mode;
51 extern int hostdb_srv_enabled;
52 
53 // extern int hostdb_timestamp;
54 extern int hostdb_sync_frequency;
55 extern int hostdb_disable_reverse_lookup;
56 
57 // Static configuration information
58 extern HostDBCache hostDB;
59 
60 /** Host DB record mark.
61 
62     The records in the host DB are de facto segregated by roughly the
63     DNS query type. We use an intermediate type to provide a little flexibility
64     although the type is presumed to be a single byte.
65  */
66 enum HostDBMark {
67   HOSTDB_MARK_GENERIC, ///< Anything that's not one of the other types.
68   HOSTDB_MARK_IPV4,    ///< IPv4 / T_A
69   HOSTDB_MARK_IPV6,    ///< IPv6 / T_AAAA
70   HOSTDB_MARK_SRV,     ///< Service / T_SRV
71 };
72 /** Convert a HostDB @a mark to a string.
73     @return A static string.
74  */
75 extern const char *string_for(HostDBMark mark);
76 
77 inline unsigned int
HOSTDB_CLIENT_IP_HASH(sockaddr const * lhs,sockaddr const * rhs)78 HOSTDB_CLIENT_IP_HASH(sockaddr const *lhs, sockaddr const *rhs)
79 {
80   unsigned int zret = ~static_cast<unsigned int>(0);
81   if (ats_ip_are_compatible(lhs, rhs)) {
82     if (ats_is_ip4(lhs)) {
83       in_addr_t ip1 = ats_ip4_addr_cast(lhs);
84       in_addr_t ip2 = ats_ip4_addr_cast(rhs);
85       zret          = (ip1 >> 16) ^ ip1 ^ ip2 ^ (ip2 >> 16);
86     } else if (ats_is_ip6(lhs)) {
87       uint32_t const *ip1 = ats_ip_addr32_cast(lhs);
88       uint32_t const *ip2 = ats_ip_addr32_cast(rhs);
89       for (int i = 0; i < 4; ++i, ++ip1, ++ip2) {
90         zret ^= (*ip1 >> 16) ^ *ip1 ^ *ip2 ^ (*ip2 >> 16);
91       }
92     }
93   }
94   return zret & 0xFFFF;
95 }
96 
97 //
98 // Constants
99 //
100 
101 #define HOST_DB_HITS_BITS 3
102 #define HOST_DB_TAG_BITS 56
103 
104 #define CONFIGURATION_HISTORY_PROBE_DEPTH 1
105 
106 // Bump this any time hostdb format is changed
107 #define HOST_DB_CACHE_MAJOR_VERSION 3
108 #define HOST_DB_CACHE_MINOR_VERSION 0
109 // 2.2: IP family split 2.1 : IPv6
110 
111 #define DEFAULT_HOST_DB_FILENAME "host.db"
112 #define DEFAULT_HOST_DB_SIZE (1 << 14)
113 // Timeout DNS every 24 hours by default if ttl_mode is enabled
114 #define HOST_DB_IP_TIMEOUT (24 * 60 * 60)
115 // DNS entries should be revalidated every 12 hours
116 #define HOST_DB_IP_STALE (12 * 60 * 60)
117 // DNS entries which failed lookup, should be revalidated every hour
118 #define HOST_DB_IP_FAIL_TIMEOUT (60 * 60)
119 
120 //#define HOST_DB_MAX_INTERVAL                 (0x7FFFFFFF)
121 const unsigned int HOST_DB_MAX_TTL = (0x1FFFFF); // 24 days
122 
123 //
124 // Constants
125 //
126 
127 // period to wait for a remote probe...
128 #define HOST_DB_RETRY_PERIOD HRTIME_MSECONDS(20)
129 #define HOST_DB_ITERATE_PERIOD HRTIME_MSECONDS(5)
130 
131 //#define TEST(_x) _x
132 #define TEST(_x)
133 
134 struct HostEnt;
135 
136 // Stats
137 enum HostDB_Stats {
138   hostdb_total_lookups_stat,
139   hostdb_total_hits_stat,  // D == total hits
140   hostdb_ttl_stat,         // D average TTL
141   hostdb_ttl_expires_stat, // D == TTL Expires
142   hostdb_re_dns_on_reload_stat,
143   HostDB_Stat_Count
144 };
145 
146 struct RecRawStatBlock;
147 extern RecRawStatBlock *hostdb_rsb;
148 
149 // Stat Macros
150 
151 #define HOSTDB_DEBUG_COUNT_DYN_STAT(_x, _y) RecIncrRawStatCount(hostdb_rsb, mutex->thread_holding, (int)_x, _y)
152 
153 #define HOSTDB_INCREMENT_DYN_STAT(_x) RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, 1)
154 
155 #define HOSTDB_DECREMENT_DYN_STAT(_x) RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, -1)
156 
157 #define HOSTDB_SUM_DYN_STAT(_x, _r) RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, _r)
158 
159 #define HOSTDB_READ_DYN_STAT(_x, _count, _sum)        \
160   do {                                                \
161     RecGetRawStatSum(hostdb_rsb, (int)_x, &_sum);     \
162     RecGetRawStatCount(hostdb_rsb, (int)_x, &_count); \
163   } while (0)
164 
165 #define HOSTDB_SET_DYN_COUNT(_x, _count) RecSetRawStatCount(hostdb_rsb, _x, _count);
166 
167 #define HOSTDB_INCREMENT_THREAD_DYN_STAT(_s, _t) RecIncrRawStatSum(hostdb_rsb, _t, (int)_s, 1);
168 
169 #define HOSTDB_DECREMENT_THREAD_DYN_STAT(_s, _t) RecIncrRawStatSum(hostdb_rsb, _t, (int)_s, -1);
170 
171 struct CmpConstBuffferCaseInsensitive {
172   bool
operator ()CmpConstBuffferCaseInsensitive173   operator()(ts::ConstBuffer a, ts::ConstBuffer b) const
174   {
175     return ptr_len_casecmp(a._ptr, a._size, b._ptr, b._size) < 0;
176   }
177 };
178 
179 // Our own typedef for the host file mapping
180 typedef std::map<ts::ConstBuffer, IpAddr, CmpConstBuffferCaseInsensitive> HostsFileMap;
181 // A to hold a ref-counted map
182 struct RefCountedHostsFileMap : public RefCountObj {
183   HostsFileMap hosts_file_map;
184   ats_scoped_str HostFileText;
185   ink_time_t next_sync_time; // time of the next sync
186 };
187 
188 //
189 // HostDBCache (Private)
190 //
191 struct HostDBCache {
192   int start(int flags = 0);
193   // Map to contain all of the host file overrides, initialize it to empty
194   Ptr<RefCountedHostsFileMap> hosts_file_ptr;
195   // TODO: make ATS call a close() method or something on shutdown (it does nothing of the sort today)
196   RefCountCache<HostDBInfo> *refcountcache = nullptr;
197 
198   // TODO configurable number of items in the cache
199   Queue<HostDBContinuation, Continuation::Link_link> *pending_dns = nullptr;
200   Queue<HostDBContinuation, Continuation::Link_link> &pending_dns_for_hash(const CryptoHash &hash);
201   Queue<HostDBContinuation, Continuation::Link_link> *remoteHostDBQueue = nullptr;
202   HostDBCache();
203   bool is_pending_dns_for_hash(const CryptoHash &hash);
204 };
205 
206 inline int
index_of(sockaddr const * ip)207 HostDBRoundRobin::index_of(sockaddr const *ip)
208 {
209   bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
210               (unsigned int)good > hostdb_round_robin_max_count);
211   if (bad) {
212     ink_assert(!"bad round robin size");
213     return -1;
214   }
215 
216   for (int i = 0; i < good; i++) {
217     if (ats_ip_addr_eq(ip, info(i).ip())) {
218       return i;
219     }
220   }
221 
222   return -1;
223 }
224 
225 inline HostDBInfo *
find_ip(sockaddr const * ip)226 HostDBRoundRobin::find_ip(sockaddr const *ip)
227 {
228   int idx = this->index_of(ip);
229   return idx < 0 ? nullptr : &info(idx);
230 }
231 
232 inline HostDBInfo *
select_next(sockaddr const * ip)233 HostDBRoundRobin::select_next(sockaddr const *ip)
234 {
235   HostDBInfo *zret = nullptr;
236   if (good > 1) {
237     int idx = this->index_of(ip);
238     if (idx >= 0) {
239       idx  = (idx + 1) % good;
240       zret = &info(idx);
241     }
242   }
243   return zret;
244 }
245 
246 inline HostDBInfo *
find_target(const char * target)247 HostDBRoundRobin::find_target(const char *target)
248 {
249   bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
250               (unsigned int)good > hostdb_round_robin_max_count);
251   if (bad) {
252     ink_assert(!"bad round robin size");
253     return nullptr;
254   }
255 
256   uint32_t key = makeHostHash(target);
257   for (int i = 0; i < good; i++) {
258     if (info(i).data.srv.key == key && !strcmp(target, info(i).srvname(this)))
259       return &info(i);
260   }
261   return nullptr;
262 }
263 
264 inline HostDBInfo *
select_best_http(sockaddr const * client_ip,ink_time_t now,int32_t fail_window)265 HostDBRoundRobin::select_best_http(sockaddr const *client_ip, ink_time_t now, int32_t fail_window)
266 {
267   bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
268               (unsigned int)good > hostdb_round_robin_max_count);
269 
270   if (bad) {
271     ink_assert(!"bad round robin size");
272     return nullptr;
273   }
274 
275   int best_any = 0;
276   int best_up  = -1;
277 
278   // Basic round robin, increment current and mod with how many we have
279   if (HostDBProcessor::hostdb_strict_round_robin) {
280     Debug("hostdb", "Using strict round robin");
281     // Check that the host we selected is alive
282     for (int i = 0; i < good; i++) {
283       best_any = current++ % good;
284       if (info(best_any).is_alive(now, fail_window)) {
285         best_up = best_any;
286         break;
287       }
288     }
289   } else if (HostDBProcessor::hostdb_timed_round_robin > 0) {
290     Debug("hostdb", "Using timed round-robin for HTTP");
291     if ((now - timed_rr_ctime) > HostDBProcessor::hostdb_timed_round_robin) {
292       Debug("hostdb", "Timed interval expired.. rotating");
293       ++current;
294       timed_rr_ctime = now;
295     }
296     for (int i = 0; i < good; i++) {
297       best_any = (current + i) % good;
298       if (info(best_any).is_alive(now, fail_window)) {
299         best_up = best_any;
300         break;
301       }
302     }
303     Debug("hostdb", "Using %d for best_up", best_up);
304   } else {
305     Debug("hostdb", "Using default round robin");
306     unsigned int best_hash_any = 0;
307     unsigned int best_hash_up  = 0;
308     for (int i = 0; i < good; i++) {
309       sockaddr const *ip = info(i).ip();
310       unsigned int h     = HOSTDB_CLIENT_IP_HASH(client_ip, ip);
311       if (best_hash_any <= h) {
312         best_any      = i;
313         best_hash_any = h;
314       }
315       if (info(i).is_alive(now, fail_window)) {
316         if (best_hash_up <= h) {
317           best_up      = i;
318           best_hash_up = h;
319         }
320       }
321     }
322   }
323 
324   if (best_up != -1) {
325     ink_assert(best_up >= 0 && best_up < good);
326     return &info(best_up);
327   } else {
328     ink_assert(best_any >= 0 && best_any < good);
329     return &info(best_any);
330   }
331 }
332 
333 inline HostDBInfo *
select_best_srv(char * target,InkRand * rand,ink_time_t now,int32_t fail_window)334 HostDBRoundRobin::select_best_srv(char *target, InkRand *rand, ink_time_t now, int32_t fail_window)
335 {
336   bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
337               (unsigned int)good > hostdb_round_robin_max_count);
338 
339   if (bad) {
340     ink_assert(!"bad round robin size");
341     return nullptr;
342   }
343 
344 #ifdef DEBUG
345   for (int i = 1; i < good; ++i) {
346     ink_assert(info(i).data.srv.srv_priority >= info(i - 1).data.srv.srv_priority);
347   }
348 #endif
349 
350   int i           = 0;
351   int len         = 0;
352   uint32_t weight = 0, p = INT32_MAX;
353   HostDBInfo *result = nullptr;
354   HostDBInfo *infos[good];
355   do {
356     // if the real isn't alive-- exclude it from selection
357     if (!info(i).is_alive(now, fail_window)) {
358       continue;
359     }
360 
361     if (info(i).data.srv.srv_priority <= p) {
362       p = info(i).data.srv.srv_priority;
363       weight += info(i).data.srv.srv_weight;
364       infos[len++] = &info(i);
365     } else
366       break;
367   } while (++i < good);
368 
369   if (len == 0) { // all failed
370     result = &info(current++ % good);
371   } else if (weight == 0) { // srv weight is 0
372     result = &info(current++ % len);
373   } else {
374     uint32_t xx = rand->random() % weight;
375     for (i = 0; i < len - 1 && xx >= infos[i]->data.srv.srv_weight; ++i)
376       xx -= infos[i]->data.srv.srv_weight;
377 
378     result = infos[i];
379   }
380 
381   if (result) {
382     ink_strlcpy(target, result->srvname(this), MAXDNAME);
383     return result;
384   }
385   return nullptr;
386 }
387 
388 //
389 // Types
390 //
391 
392 /** Container for a hash and its dependent data.
393     This handles both the host name and raw address cases.
394 */
395 struct HostDBHash {
396   typedef HostDBHash self; ///< Self reference type.
397 
398   CryptoHash hash; ///< The hash value.
399 
400   const char *host_name = nullptr; ///< Host name.
401   int host_len          = 0;       ///< Length of @a _host_name
402   IpAddr ip;                       ///< IP address.
403   in_port_t port = 0;              ///< IP port (host order).
404   /// DNS server. Not strictly part of the hash data but
405   /// it's both used by @c HostDBContinuation and provides access to
406   /// hash data. It's just handier to store it here for both uses.
407   DNSServer *dns_server = nullptr;
408   SplitDNS *pSD         = nullptr;             ///< Hold the container for @a dns_server.
409   HostDBMark db_mark    = HOSTDB_MARK_GENERIC; ///< Mark / type of record.
410 
411   /// Default constructor.
412   HostDBHash();
413   /// Destructor.
414   ~HostDBHash();
415   /// Recompute and update the hash.
416   void refresh();
417   /** Assign a hostname.
418       This updates the split DNS data as well.
419   */
420   self &set_host(const char *name, int len);
421 };
422 
423 //
424 // Handles a HostDB lookup request
425 //
426 struct HostDBContinuation;
427 typedef int (HostDBContinuation::*HostDBContHandler)(int, void *);
428 
429 struct HostDBContinuation : public Continuation {
430   Action action;
431   HostDBHash hash;
432   //  IpEndpoint ip;
433   unsigned int ttl = 0;
434   //  HostDBMark db_mark; ///< Target type.
435   /// Original IP address family style. Note this will disagree with
436   /// @a hash.db_mark when doing a retry on an alternate family. The retry
437   /// logic depends on it to avoid looping.
438   HostResStyle host_res_style = DEFAULT_OPTIONS.host_res_style; ///< Address family priority.
439   int dns_lookup_timeout      = DEFAULT_OPTIONS.timeout;
440   Event *timeout              = nullptr;
441   Continuation *from_cont     = nullptr;
442   HostDBApplicationInfo app;
443   int probe_depth            = 0;
444   size_t current_iterate_pos = 0;
445   //  char name[MAXDNAME];
446   //  int namelen;
447   char hash_host_name_store[MAXDNAME + 1]; // used as backing store for @a hash
448   char srv_target_name[MAXDNAME];
449   //  void *m_pDS;
450   Action *pending_action = nullptr;
451 
452   unsigned int missing : 1;
453   unsigned int force_dns : 1;
454   unsigned int round_robin : 1;
455 
456   int probeEvent(int event, Event *e);
457   int iterateEvent(int event, Event *e);
458   int dnsEvent(int event, HostEnt *e);
459   int dnsPendingEvent(int event, Event *e);
460   int backgroundEvent(int event, Event *e);
461   int retryEvent(int event, Event *e);
462   int removeEvent(int event, Event *e);
463   int setbyEvent(int event, Event *e);
464 
465   /// Recompute the hash and update ancillary values.
466   void refresh_hash();
467   void do_dns();
468   bool
is_bynameHostDBContinuation469   is_byname()
470   {
471     return hash.db_mark == HOSTDB_MARK_IPV4 || hash.db_mark == HOSTDB_MARK_IPV6;
472   }
473   bool
is_srvHostDBContinuation474   is_srv()
475   {
476     return hash.db_mark == HOSTDB_MARK_SRV;
477   }
478   HostDBInfo *lookup_done(IpAddr const &ip, const char *aname, bool round_robin, unsigned int attl, SRVHosts *s = nullptr,
479                           HostDBInfo *r = nullptr);
480   int key_partition();
481   void remove_trigger_pending_dns();
482   int set_check_pending_dns();
483 
484   HostDBInfo *insert(unsigned int attl);
485 
486   /** Optional values for @c init.
487    */
488   struct Options {
489     typedef Options self; ///< Self reference type.
490 
491     int timeout                 = 0;             ///< Timeout value. Default 0
492     HostResStyle host_res_style = HOST_RES_NONE; ///< IP address family fallback. Default @c HOST_RES_NONE
493     bool force_dns              = false;         ///< Force DNS lookup. Default @c false
494     Continuation *cont          = nullptr;       ///< Continuation / action. Default @c nullptr (none)
495 
OptionsHostDBContinuation::Options496     Options() {}
497   };
498   static const Options DEFAULT_OPTIONS; ///< Default defaults.
499   void init(HostDBHash const &hash, Options const &opt = DEFAULT_OPTIONS);
500   int make_get_message(char *buf, int len);
501   int make_put_message(HostDBInfo *r, Continuation *c, char *buf, int len);
502 
HostDBContinuationHostDBContinuation503   HostDBContinuation() : missing(false), force_dns(DEFAULT_OPTIONS.force_dns), round_robin(false)
504   {
505     ink_zero(hash_host_name_store);
506     ink_zero(hash.hash);
507     SET_HANDLER((HostDBContHandler)&HostDBContinuation::probeEvent);
508   }
509 };
510 
511 inline unsigned int
master_hash(CryptoHash const & hash)512 master_hash(CryptoHash const &hash)
513 {
514   return static_cast<int>(hash[1] >> 32);
515 }
516 
517 inline bool
is_dotted_form_hostname(const char * c)518 is_dotted_form_hostname(const char *c)
519 {
520   return -1 != (int)ink_inet_addr(c);
521 }
522 
523 inline Queue<HostDBContinuation> &
pending_dns_for_hash(const CryptoHash & hash)524 HostDBCache::pending_dns_for_hash(const CryptoHash &hash)
525 {
526   return pending_dns[this->refcountcache->partition_for_key(hash.fold())];
527 }
528 
529 inline int
key_partition()530 HostDBContinuation::key_partition()
531 {
532   return hostDB.refcountcache->partition_for_key(hash.hash.fold());
533 }
534