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 #pragma once
25 
26 #include "tscore/HashFNV.h"
27 #include "tscore/ink_time.h"
28 #include "tscore/CryptoHash.h"
29 #include "tscore/ink_align.h"
30 #include "tscore/ink_resolver.h"
31 #include "tscore/HTTPVersion.h"
32 #include "I_EventSystem.h"
33 #include "SRV.h"
34 #include "P_RefCountCache.h"
35 
36 // Event returned on a lookup
37 #define EVENT_HOST_DB_LOOKUP (HOSTDB_EVENT_EVENTS_START + 0)
38 #define EVENT_HOST_DB_IP_REMOVED (HOSTDB_EVENT_EVENTS_START + 1)
39 #define EVENT_HOST_DB_GET_RESPONSE (HOSTDB_EVENT_EVENTS_START + 2)
40 
41 #define EVENT_SRV_LOOKUP (SRV_EVENT_EVENTS_START + 0)
42 #define EVENT_SRV_IP_REMOVED (SRV_EVENT_EVENTS_START + 1)
43 #define EVENT_SRV_GET_RESPONSE (SRV_EVENT_EVENTS_START + 2)
44 
45 //
46 // Data
47 //
48 struct HostDBContinuation;
49 
50 //
51 // The host database stores host information, most notably the
52 // IP address.
53 //
54 // Since host information is relatively small, we can afford to have
55 // a reasonable size memory cache, and use a (relatively) sparse
56 // disk representation to decrease # of seeks.
57 //
58 extern int hostdb_enable;
59 extern ink_time_t hostdb_current_interval;
60 extern unsigned int hostdb_ip_stale_interval;
61 extern unsigned int hostdb_ip_timeout_interval;
62 extern unsigned int hostdb_ip_fail_timeout_interval;
63 extern unsigned int hostdb_serve_stale_but_revalidate;
64 extern unsigned int hostdb_round_robin_max_count;
65 
66 extern int hostdb_max_iobuf_index;
67 
68 static inline unsigned int
makeHostHash(const char * string)69 makeHostHash(const char *string)
70 {
71   ink_assert(string && *string);
72 
73   if (string && *string) {
74     ATSHash32FNV1a fnv;
75     fnv.update(string, strlen(string), ATSHash::nocase());
76     fnv.final();
77     return fnv.get();
78   }
79 
80   return 0;
81 }
82 
83 //
84 // Types
85 //
86 
87 /** Host information metadata used by various parts of HostDB.
88  * It is stored as generic data in the cache.
89  *
90  * As a @c union only one of the members is valid, Which one depends on context data in the
91  * @c HostDBInfo. This data is written literally to disk therefore if any change is made,
92  * the @c object_version for the cache must be updated by modifying @c HostDBInfo::version.
93  *
94  * @see HostDBInfo::version
95  */
96 union HostDBApplicationInfo {
97   /// Generic storage. This is verified to be the size of the union.
98   struct application_data_allotment {
99     unsigned int application1;
100     unsigned int application2;
101   } allotment;
102 
103   //////////////////////////////////////////////////////////
104   // http server attributes in the host database          //
105   //                                                      //
106   // http_version       - one of HTTPVersion              //
107   // last_failure       - UNIX time for the last time     //
108   //                      we tried the server & failed    //
109   // fail_count         - Number of times we tried and    //
110   //                       and failed to contact the host //
111   //////////////////////////////////////////////////////////
112   struct http_server_attr {
113     uint32_t last_failure;
114     HTTPVersion http_version;
115     uint8_t fail_count;
http_server_attrHostDBApplicationInfo::http_server_attr116     http_server_attr() : http_version() {}
117   } http_data;
118 
119   struct application_data_rr {
120     unsigned int offset;
121   } rr;
HostDBApplicationInfo()122   HostDBApplicationInfo() : http_data() {}
123 };
124 
125 struct HostDBRoundRobin;
126 
127 struct SRVInfo {
128   unsigned int srv_offset : 16;
129   unsigned int srv_weight : 16;
130   unsigned int srv_priority : 16;
131   unsigned int srv_port : 16;
132   unsigned int key;
133 };
134 
135 struct HostDBInfo : public RefCountObj {
136   /** Internal IP address data.
137       This is at least large enough to hold an IPv6 address.
138   */
139 
140   static HostDBInfo *
141   alloc(int size = 0)
142   {
143     size += sizeof(HostDBInfo);
144     int iobuffer_index = iobuffer_size_to_index(size, hostdb_max_iobuf_index);
145     ink_release_assert(iobuffer_index >= 0);
146     void *ptr = ioBufAllocator[iobuffer_index].alloc_void();
147     memset(ptr, 0, size);
148     HostDBInfo *ret      = new (ptr) HostDBInfo();
149     ret->_iobuffer_index = iobuffer_index;
150     return ret;
151   }
152 
153   void
freeHostDBInfo154   free() override
155   {
156     ink_release_assert(from_alloc());
157     Debug("hostdb", "freeing %d bytes at [%p]", (1 << (7 + _iobuffer_index)), this);
158     ioBufAllocator[_iobuffer_index].free_void((void *)(this));
159   }
160 
161   /// Effectively the @c object_version for cache data.
162   /// This is used to indicate incompatible changes in the binary layout of HostDB records.
163   /// It must be updated if any such change is made, even if it is functionally equivalent.
164   static ts::VersionNumber
versionHostDBInfo165   version()
166   {
167     /// - 1.0 Initial version.
168     /// - 1.1 tweak HostDBApplicationInfo::http_data.
169     return ts::VersionNumber(1, 1);
170   }
171 
172   static HostDBInfo *
unmarshallHostDBInfo173   unmarshall(char *buf, unsigned int size)
174   {
175     if (size < sizeof(HostDBInfo)) {
176       return nullptr;
177     }
178     HostDBInfo *ret = HostDBInfo::alloc(size - sizeof(HostDBInfo));
179     int buf_index   = ret->_iobuffer_index;
180     memcpy((void *)ret, buf, size);
181     // Reset the refcount back to 0, this is a bit ugly-- but I'm not sure we want to expose a method
182     // to mess with the refcount, since this is a fairly unique use case
183     ret                  = new (ret) HostDBInfo();
184     ret->_iobuffer_index = buf_index;
185     return ret;
186   }
187 
188   // return expiry time (in seconds since epoch)
189   ink_time_t
expiry_timeHostDBInfo190   expiry_time() const
191   {
192     return ip_timestamp + ip_timeout_interval + hostdb_serve_stale_but_revalidate;
193   }
194 
195   sockaddr *
ipHostDBInfo196   ip()
197   {
198     return &data.ip.sa;
199   }
200 
201   sockaddr const *
ipHostDBInfo202   ip() const
203   {
204     return &data.ip.sa;
205   }
206 
207   char *hostname() const;
208   char *perm_hostname() const;
209   char *srvname(HostDBRoundRobin *rr) const;
210 
211   /// Check if this entry is an element of a round robin entry.
212   /// If @c true then this entry is part of and was obtained from a round robin root. This is useful if the
213   /// address doesn't work - a retry can probably get a new address by doing another lookup and resolving to
214   /// a different element of the round robin.
215   bool
is_rr_eltHostDBInfo216   is_rr_elt() const
217   {
218     return 0 != round_robin_elt;
219   }
220 
221   HostDBRoundRobin *rr();
222 
223   unsigned int
ip_intervalHostDBInfo224   ip_interval() const
225   {
226     return (hostdb_current_interval - ip_timestamp) & 0x7FFFFFFF;
227   }
228 
229   int
ip_time_remainingHostDBInfo230   ip_time_remaining() const
231   {
232     return static_cast<int>(ip_timeout_interval) - static_cast<int>(this->ip_interval());
233   }
234 
235   bool
is_ip_staleHostDBInfo236   is_ip_stale() const
237   {
238     return ip_timeout_interval >= 2 * hostdb_ip_stale_interval && ip_interval() >= hostdb_ip_stale_interval;
239   }
240 
241   bool
is_ip_timeoutHostDBInfo242   is_ip_timeout() const
243   {
244     return ip_interval() >= ip_timeout_interval;
245   }
246 
247   bool
is_ip_fail_timeoutHostDBInfo248   is_ip_fail_timeout() const
249   {
250     return ip_interval() >= hostdb_ip_fail_timeout_interval;
251   }
252 
253   void
refresh_ipHostDBInfo254   refresh_ip()
255   {
256     ip_timestamp = hostdb_current_interval;
257   }
258 
259   bool
serve_stale_but_revalidateHostDBInfo260   serve_stale_but_revalidate() const
261   {
262     // the option is disabled
263     if (hostdb_serve_stale_but_revalidate <= 0) {
264       return false;
265     }
266 
267     // ip_timeout_interval == DNS TTL
268     // hostdb_serve_stale_but_revalidate == number of seconds
269     // ip_interval() is the number of seconds between now() and when the entry was inserted
270     if ((ip_timeout_interval + hostdb_serve_stale_but_revalidate) > ip_interval()) {
271       Debug("hostdb", "serving stale entry %d | %d | %d as requested by config", ip_timeout_interval,
272             hostdb_serve_stale_but_revalidate, ip_interval());
273       return true;
274     }
275 
276     // otherwise, the entry is too old
277     return false;
278   }
279 
280   /*
281    * Given the current time `now` and the fail_window, determine if this real is alive
282    */
283   bool
is_aliveHostDBInfo284   is_alive(ink_time_t now, int32_t fail_window)
285   {
286     unsigned int last_failure = app.http_data.last_failure;
287 
288     if (last_failure == 0 || (unsigned int)(now - fail_window) > last_failure) {
289       return true;
290     } else {
291       // Entry is marked down.  Make sure some nasty clock skew
292       //  did not occur.  Use the retry time to set an upper bound
293       //  as to how far in the future we should tolerate bogus last
294       //  failure times.  This sets the upper bound that we would ever
295       //  consider a server down to 2*down_server_timeout
296       if ((unsigned int)(now + fail_window) < last_failure) {
297         app.http_data.last_failure = 0;
298         return false;
299       }
300       return false;
301     }
302   }
303 
304   bool
is_failedHostDBInfo305   is_failed() const
306   {
307     return !((is_srv && data.srv.srv_offset) || (reverse_dns && data.hostname_offset) || ats_is_ip(ip()));
308   }
309 
310   void
set_failedHostDBInfo311   set_failed()
312   {
313     if (is_srv) {
314       data.srv.srv_offset = 0;
315     } else if (reverse_dns) {
316       data.hostname_offset = 0;
317     } else {
318       ats_ip_invalidate(ip());
319     }
320   }
321 
322   uint64_t key{0};
323 
324   // Application specific data. NOTE: We need an integral number of
325   // these per block. This structure is 32 bytes. (at 200k hosts =
326   // 8 Meg). Which gives us 7 bytes of application information.
327   HostDBApplicationInfo app;
328 
329   union {
330     IpEndpoint ip;                ///< IP address / port data.
331     unsigned int hostname_offset; ///< Some hostname thing.
332     SRVInfo srv;
333   } data;
334 
335   unsigned int hostname_offset{0}; // always maintain a permanent copy of the hostname for non-rev dns records.
336 
337   unsigned int ip_timestamp{0};
338 
339   unsigned int ip_timeout_interval{0}; // bounded between 1 and HOST_DB_MAX_TTL (0x1FFFFF, 24 days)
340 
341   unsigned int is_srv : 1;
342   unsigned int reverse_dns : 1;
343 
344   unsigned int round_robin : 1;     // This is the root of a round robin block
345   unsigned int round_robin_elt : 1; // This is an address in a round robin block
346 
HostDBInfoHostDBInfo347   HostDBInfo() : _iobuffer_index{-1} {}
348 
HostDBInfoHostDBInfo349   HostDBInfo(HostDBInfo const &src) : RefCountObj()
350   {
351     memcpy(static_cast<void *>(this), static_cast<const void *>(&src), sizeof(*this));
352     _iobuffer_index = -1;
353   }
354 
355   HostDBInfo &
356   operator=(HostDBInfo const &src)
357   {
358     if (this != &src) {
359       int iob_idx = _iobuffer_index;
360       memcpy(static_cast<void *>(this), static_cast<const void *>(&src), sizeof(*this));
361       _iobuffer_index = iob_idx;
362     }
363     return *this;
364   }
365 
366   bool
from_allocHostDBInfo367   from_alloc() const
368   {
369     return _iobuffer_index >= 0;
370   }
371 
372 private:
373   // The value of this will be -1 for objects that are not created by the alloc() static member function.
374   int _iobuffer_index;
375 };
376 
377 struct HostDBRoundRobin {
378   /** Total number (to compute space used). */
379   short rrcount = 0;
380 
381   /** Number which have not failed a connect. */
382   short good = 0;
383 
384   unsigned short current    = 0;
385   ink_time_t timed_rr_ctime = 0;
386 
387   // This is the equivalent of a variable length array, we can't use a VLA because
388   // HostDBInfo is a non-POD type-- so this is the best we can do.
389   HostDBInfo &
infoHostDBRoundRobin390   info(short n)
391   {
392     ink_assert(n < rrcount && n >= 0);
393     return *((HostDBInfo *)((char *)this + sizeof(HostDBRoundRobin)) + n);
394   }
395 
396   // Return the allocation size of a HostDBRoundRobin struct suitable for storing
397   // "count" HostDBInfo records.
398   static unsigned
399   size(unsigned count, unsigned srv_len = 0)
400   {
401     ink_assert(count > 0);
402     return INK_ALIGN((sizeof(HostDBRoundRobin) + (count * sizeof(HostDBInfo)) + srv_len), 8);
403   }
404 
405   /** Find the index of @a addr in member @a info.
406       @return The index if found, -1 if not found.
407   */
408   int index_of(sockaddr const *addr);
409   HostDBInfo *find_ip(sockaddr const *addr);
410   // Find the srv target
411   HostDBInfo *find_target(const char *target);
412   /** Select the next entry after @a addr.
413       @note If @a addr isn't an address in the round robin nothing is updated.
414       @return The selected entry or @c nullptr if @a addr wasn't present.
415    */
416   HostDBInfo *select_next(sockaddr const *addr);
417   HostDBInfo *select_best_http(sockaddr const *client_ip, ink_time_t now, int32_t fail_window);
418   HostDBInfo *select_best_srv(char *target, InkRand *rand, ink_time_t now, int32_t fail_window);
HostDBRoundRobinHostDBRoundRobin419   HostDBRoundRobin() {}
420 };
421 
422 struct HostDBCache;
423 struct HostDBHash;
424 
425 // Prototype for inline completion function or
426 //  getbyname_imm()
427 typedef void (Continuation::*cb_process_result_pfn)(HostDBInfo *r);
428 
429 Action *iterate(Continuation *cont);
430 
431 /** The Host Database access interface. */
432 struct HostDBProcessor : public Processor {
433   friend struct HostDBSync;
434   // Public Interface
435 
436   // Lookup Hostinfo by name
437   //    cont->handleEvent( EVENT_HOST_DB_LOOKUP, HostDBInfo * ); on success
438   //    cont->handleEVent( EVENT_HOST_DB_LOOKUP, 0); on failure
439   // Failure occurs when the host cannot be DNS-ed
440   // NOTE: Will call the continuation back before returning if data is in the
441   //       cache.  The HostDBInfo * becomes invalid when the callback returns.
442   //       The HostDBInfo may be changed during the callback.
443 
444   enum {
445     HOSTDB_DO_NOT_FORCE_DNS   = 0,
446     HOSTDB_ROUND_ROBIN        = 0,
447     HOSTDB_FORCE_DNS_RELOAD   = 1,
448     HOSTDB_FORCE_DNS_ALWAYS   = 2,
449     HOSTDB_DO_NOT_ROUND_ROBIN = 4
450   };
451 
452   /// Optional parameters for getby...
453   struct Options {
454     typedef Options self;                                  ///< Self reference type.
455     int port                    = 0;                       ///< Target service port (default 0 -> don't care)
456     int flags                   = HOSTDB_DO_NOT_FORCE_DNS; ///< Processing flags (default HOSTDB_DO_NOT_FORCE_DNS)
457     int timeout                 = 0;                       ///< Timeout value (default 0 -> default timeout)
458     HostResStyle host_res_style = HOST_RES_IPV4;           ///< How to query host (default HOST_RES_IPV4)
459 
OptionsHostDBProcessor::Options460     Options() {}
461     /// Set the flags.
462     self &
setFlagsHostDBProcessor::Options463     setFlags(int f)
464     {
465       flags = f;
466       return *this;
467     }
468   };
469 
470   /// Default options.
471   static Options const DEFAULT_OPTIONS;
472 
HostDBProcessorHostDBProcessor473   HostDBProcessor() {}
474   inkcoreapi Action *getbyname_re(Continuation *cont, const char *hostname, int len, Options const &opt = DEFAULT_OPTIONS);
475 
476   Action *getbynameport_re(Continuation *cont, const char *hostname, int len, Options const &opt = DEFAULT_OPTIONS);
477 
478   Action *getSRVbyname_imm(Continuation *cont, cb_process_result_pfn process_srv_info, const char *hostname, int len,
479                            Options const &opt = DEFAULT_OPTIONS);
480 
481   Action *getbyname_imm(Continuation *cont, cb_process_result_pfn process_hostdb_info, const char *hostname, int len,
482                         Options const &opt = DEFAULT_OPTIONS);
483 
484   Action *iterate(Continuation *cont);
485 
486   /** Lookup Hostinfo by addr */
487   Action *getbyaddr_re(Continuation *cont, sockaddr const *aip);
488 
489   /** Set the application information (fire-and-forget). */
490   void
setbyname_appinfoHostDBProcessor491   setbyname_appinfo(char *hostname, int len, int port, HostDBApplicationInfo *app)
492   {
493     sockaddr_in addr;
494     ats_ip4_set(&addr, INADDR_ANY, port);
495     setby(hostname, len, ats_ip_sa_cast(&addr), app);
496   }
497 
498   void
setbyaddr_appinfoHostDBProcessor499   setbyaddr_appinfo(sockaddr const *addr, HostDBApplicationInfo *app)
500   {
501     this->setby(nullptr, 0, addr, app);
502   }
503 
504   void
setbyaddr_appinfoHostDBProcessor505   setbyaddr_appinfo(in_addr_t ip, HostDBApplicationInfo *app)
506   {
507     sockaddr_in addr;
508     ats_ip4_set(&addr, ip);
509     this->setby(nullptr, 0, ats_ip_sa_cast(&addr), app);
510   }
511 
512   /** Configuration. */
513   static int hostdb_strict_round_robin;
514   static int hostdb_timed_round_robin;
515 
516   // Processor Interface
517   /* hostdb does not use any dedicated event threads
518    * currently. Dont pass any value to start
519    */
520   int start(int no_of_additional_event_threads = 0, size_t stacksize = DEFAULT_STACKSIZE) override;
521 
522   // Private
523   HostDBCache *cache();
524 
525 private:
526   Action *getby(Continuation *cont, cb_process_result_pfn cb_process_result, HostDBHash &hash, Options const &opt);
527 
528 public:
529   /** Set something.
530       @a aip can carry address and / or port information. If setting just
531       by a port value, the address should be set to INADDR_ANY which is of
532       type IPv4.
533    */
534   void setby(const char *hostname,      ///< Hostname.
535              int len,                   ///< Length of hostname.
536              sockaddr const *aip,       ///< Address and/or port.
537              HostDBApplicationInfo *app ///< I don't know.
538   );
539 
540   void setby_srv(const char *hostname, int len, const char *target, HostDBApplicationInfo *app);
541 };
542 
543 void run_HostDBTest();
544 
545 extern inkcoreapi HostDBProcessor hostDBProcessor;
546 
547 void ink_hostdb_init(ts::ModuleVersion version);
548