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_insert_duplicate_to_pending_dns_stat,
144 HostDB_Stat_Count
145 };
146
147 struct RecRawStatBlock;
148 extern RecRawStatBlock *hostdb_rsb;
149
150 // Stat Macros
151
152 #define HOSTDB_DEBUG_COUNT_DYN_STAT(_x, _y) RecIncrRawStatCount(hostdb_rsb, mutex->thread_holding, (int)_x, _y)
153
154 #define HOSTDB_INCREMENT_DYN_STAT(_x) RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, 1)
155
156 #define HOSTDB_DECREMENT_DYN_STAT(_x) RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, -1)
157
158 #define HOSTDB_SUM_DYN_STAT(_x, _r) RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, _r)
159
160 #define HOSTDB_READ_DYN_STAT(_x, _count, _sum) \
161 do { \
162 RecGetRawStatSum(hostdb_rsb, (int)_x, &_sum); \
163 RecGetRawStatCount(hostdb_rsb, (int)_x, &_count); \
164 } while (0)
165
166 #define HOSTDB_SET_DYN_COUNT(_x, _count) RecSetRawStatCount(hostdb_rsb, _x, _count);
167
168 #define HOSTDB_INCREMENT_THREAD_DYN_STAT(_s, _t) RecIncrRawStatSum(hostdb_rsb, _t, (int)_s, 1);
169
170 #define HOSTDB_DECREMENT_THREAD_DYN_STAT(_s, _t) RecIncrRawStatSum(hostdb_rsb, _t, (int)_s, -1);
171
172 struct CmpConstBuffferCaseInsensitive {
173 bool
operatorCmpConstBuffferCaseInsensitive174 operator()(ts::ConstBuffer a, ts::ConstBuffer b) const
175 {
176 return ptr_len_casecmp(a._ptr, a._size, b._ptr, b._size) < 0;
177 }
178 };
179
180 // Our own typedef for the host file mapping
181 typedef std::map<ts::ConstBuffer, IpAddr, CmpConstBuffferCaseInsensitive> HostsFileMap;
182 // A to hold a ref-counted map
183 struct RefCountedHostsFileMap : public RefCountObj {
184 HostsFileMap hosts_file_map;
185 ats_scoped_str HostFileText;
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 = infos[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 setbyEvent(int event, Event *e);
463
464 /// Recompute the hash and update ancillary values.
465 void refresh_hash();
466 void do_dns();
467 bool
is_bynameHostDBContinuation468 is_byname()
469 {
470 return hash.db_mark == HOSTDB_MARK_IPV4 || hash.db_mark == HOSTDB_MARK_IPV6;
471 }
472 bool
is_srvHostDBContinuation473 is_srv()
474 {
475 return hash.db_mark == HOSTDB_MARK_SRV;
476 }
477 HostDBInfo *lookup_done(IpAddr const &ip, const char *aname, bool round_robin, unsigned int attl, SRVHosts *s = nullptr,
478 HostDBInfo *r = nullptr);
479 int key_partition();
480 void remove_trigger_pending_dns();
481 int set_check_pending_dns();
482
483 HostDBInfo *insert(unsigned int attl);
484
485 /** Optional values for @c init.
486 */
487 struct Options {
488 typedef Options self; ///< Self reference type.
489
490 int timeout = 0; ///< Timeout value. Default 0
491 HostResStyle host_res_style = HOST_RES_NONE; ///< IP address family fallback. Default @c HOST_RES_NONE
492 bool force_dns = false; ///< Force DNS lookup. Default @c false
493 Continuation *cont = nullptr; ///< Continuation / action. Default @c nullptr (none)
494
OptionsHostDBContinuation::Options495 Options() {}
496 };
497 static const Options DEFAULT_OPTIONS; ///< Default defaults.
498 void init(HostDBHash const &hash, Options const &opt = DEFAULT_OPTIONS);
499 int make_get_message(char *buf, int len);
500 int make_put_message(HostDBInfo *r, Continuation *c, char *buf, int len);
501
HostDBContinuationHostDBContinuation502 HostDBContinuation() : missing(false), force_dns(DEFAULT_OPTIONS.force_dns), round_robin(false)
503 {
504 ink_zero(hash_host_name_store);
505 ink_zero(hash.hash);
506 SET_HANDLER((HostDBContHandler)&HostDBContinuation::probeEvent);
507 }
508 };
509
510 inline unsigned int
master_hash(CryptoHash const & hash)511 master_hash(CryptoHash const &hash)
512 {
513 return static_cast<int>(hash[1] >> 32);
514 }
515
516 inline bool
is_dotted_form_hostname(const char * c)517 is_dotted_form_hostname(const char *c)
518 {
519 return -1 != (int)ink_inet_addr(c);
520 }
521
522 inline Queue<HostDBContinuation> &
pending_dns_for_hash(const CryptoHash & hash)523 HostDBCache::pending_dns_for_hash(const CryptoHash &hash)
524 {
525 return pending_dns[this->refcountcache->partition_for_key(hash.fold())];
526 }
527
528 inline int
key_partition()529 HostDBContinuation::key_partition()
530 {
531 return hostDB.refcountcache->partition_for_key(hash.hash.fold());
532 }
533