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