root/src/noit_check_resolver.c

Revision 98b736868c51ee7fc36428c7a07cf188f50daef7, 25.1 kB (checked in by Philip Maddox <philip.maddox@circonus.com>, 1 month ago)

Revert "Merge pull request #282 from circonus-labs/use_mtev_hash_init"

This reverts commit 271b2db2ee3b5efbb9bc7dc6e4c070c8af3a547d, reversing
changes made to fc5266aeabae0bb1cc227e1374f151869d3d57da.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2011, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  * Copyright (c) 2015, Circonus, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
17  *       of its contributors may be used to endorse or promote products
18  *       derived from this software without specific prior written
19  *       permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "noit_config.h"
35 #include <mtev_defines.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <time.h>
41 #include <ctype.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <netdb.h>
45 #include <udns.h>
46
47 #include <eventer/eventer.h>
48 #include <mtev_log.h>
49 #include <mtev_skiplist.h>
50 #include <mtev_hash.h>
51 #include <mtev_hooks.h>
52 #include <mtev_conf.h>
53 #include <mtev_console.h>
54 #include "noit_mtev_bridge.h"
55
56 #define MAX_RR 256
57 #define DEFAULT_FAILED_TTL 60
58 #define DEFAULT_PURGE_AGE  1200 /* 20 minutes */
59
60 static struct dns_ctx *dns_ctx;
61 static pthread_mutex_t nc_dns_cache_lock = PTHREAD_MUTEX_INITIALIZER;
62 static mtev_skiplist nc_dns_cache;
63 static eventer_t dns_cache_timeout = NULL;
64 static mtev_hash_table etc_hosts_cache;
65 static int dns_search_flag = DNS_NOSRCH;
66
67 MTEV_HOOK_IMPL(noit_resolver_cache_store,
68                (const char *key, const void *data, int len),
69                void *, closure,
70                (void *closure, const char *key, const void *data, int len),
71                (closure,key,data,len))
72
73 MTEV_HOOK_IMPL(noit_resolver_cache_load,
74                (char **key, void **data, int *len),
75                void *, closure,
76                (void *closure, char **key, void **data, int *len),
77                (closure,key,data,len))
78
79 #define DCLOCK() pthread_mutex_lock(&nc_dns_cache_lock)
80 #define DCUNLOCK() pthread_mutex_unlock(&nc_dns_cache_lock)
81
82 #define dns_cache_SHARED \
83   time_t last_needed; \
84   time_t last_updated; \
85   time_t ttl; \
86   int ip4_cnt; \
87   int ip6_cnt; \
88   unsigned char dn[DNS_MAXDN]
89
90 typedef struct {
91   dns_cache_SHARED;
92   char *target;
93   mtev_boolean lookup_inflight_v4;
94   mtev_boolean lookup_inflight_v6;
95   struct in_addr *ip4;
96   struct in6_addr *ip6;
97 } dns_cache_node;
98
99 typedef struct {
100   dns_cache_SHARED;
101 } dns_cache_serial_t;
102
103 static int dns_cache_node_serialize(void *b, int blen, dns_cache_node *n) {
104   int needed_len, ip4len, ip6len;
105   ip4len = n->ip4_cnt * sizeof(struct in_addr);
106   ip6len = n->ip6_cnt * sizeof(struct in6_addr);
107   needed_len = sizeof(dns_cache_serial_t) + ip4len + ip6len;
108
109   if(needed_len > blen) return -1;
110   memcpy(b, n, sizeof(dns_cache_serial_t));
111   memcpy(b + sizeof(dns_cache_serial_t), n->ip4, ip4len);
112   memcpy(b + sizeof(dns_cache_serial_t) + ip4len, n->ip6, ip6len);
113   return needed_len;
114 }
115
116 static int dns_cache_node_deserialize(dns_cache_node *n, void *b, int blen) {
117   int ip4len, ip6len;
118   if(n->ip4) free(n->ip4);
119   n->ip4 = NULL;
120   if(n->ip6) free(n->ip6);
121   n->ip6 = NULL;
122   if(blen < sizeof(dns_cache_serial_t)) return -1;
123   memcpy(n, b, sizeof(dns_cache_serial_t));
124   ip4len = n->ip4_cnt * sizeof(struct in_addr);
125   ip6len = n->ip6_cnt * sizeof(struct in6_addr);
126   if(blen != (sizeof(dns_cache_serial_t) + ip4len + ip6len)) {
127     n->ip4_cnt = 0;
128     n->ip6_cnt = 0;
129     return -1;
130   }
131   if(ip4len) {
132     n->ip4 = malloc(ip4len);
133     memcpy(n->ip4, b + sizeof(dns_cache_serial_t), ip4len);
134   }
135   if(ip6len) {
136     n->ip6 = malloc(ip6len);
137     memcpy(n->ip6, b + sizeof(dns_cache_serial_t) + ip4len, ip6len);
138   }
139   return sizeof(dns_cache_serial_t) + ip4len + ip6len;
140 }
141
142 typedef struct {
143   char *target;
144   struct in_addr ip4;
145   struct in6_addr ip6;
146   int has_ip4:1;
147   int has_ip6:1;
148 } static_host_node;
149
150 void dns_cache_node_free(void *vn) {
151   dns_cache_node *n = vn;
152   if(!n) return;
153   if(n->target) free(n->target);
154   if(n->ip4) free(n->ip4);
155   if(n->ip6) free(n->ip6);
156   free(n);
157 }
158
159 static int name_lookup(const void *av, const void *bv) {
160   const dns_cache_node *a = av;
161   const dns_cache_node *b = bv;
162   return strcmp(a->target, b->target);
163 }
164 static int name_lookup_k(const void *akv, const void *bv) {
165   const char *ak = akv;
166   const dns_cache_node *b = bv;
167   return strcmp(ak, b->target);
168 }
169 static int refresh_idx(const void *av, const void *bv) {
170   const dns_cache_node *a = av;
171   const dns_cache_node *b = bv;
172   if((a->last_updated + a->ttl) < (b->last_updated + b->ttl)) return -1;
173   return 1;
174 }
175 static int refresh_idx_k(const void *akv, const void *bv) {
176   time_t f = (time_t) akv;
177   const dns_cache_node *b = bv;
178   if(f < (b->last_updated + b->ttl)) return -1;
179   return 1;
180 }
181
182 static void
183 noit_check_resolver_remind_internal(const char *target,
184                                     mtev_boolean needs_lock) {
185   dns_cache_node *n;
186   if(!target) return;
187   if(needs_lock) DCLOCK();
188   n = mtev_skiplist_find(&nc_dns_cache, target, NULL);
189   if(n != NULL) {
190     n->last_needed = time(NULL);
191     if(needs_lock) DCUNLOCK();
192     return;
193   }
194   n = calloc(1, sizeof(*n));
195   n->target = strdup(target);
196   n->last_needed = time(NULL);
197   mtev_skiplist_insert(&nc_dns_cache, n);
198   if(needs_lock) DCUNLOCK();
199 }
200
201 void noit_check_resolver_remind(const char *target) {
202   noit_check_resolver_remind_internal(target, mtev_true);
203 }
204
205 int noit_check_resolver_fetch(const char *target, char *buff, int len,
206                               uint8_t prefer_family) {
207   int i, rv;
208   uint8_t progression[2];
209   dns_cache_node *n;
210   void *vnode;
211
212   buff[0] = '\0';
213   if(!target) return -1;
214   progression[0] = prefer_family;
215   progression[1] = (prefer_family == AF_INET) ? AF_INET6 : AF_INET;
216
217   if(mtev_hash_retrieve(&etc_hosts_cache, target, strlen(target), &vnode)) {
218     static_host_node *node = vnode;
219     for(i=0; i<2; i++) {
220       switch(progression[i]) {
221         case AF_INET:
222           if(node->has_ip4) {
223             inet_ntop(AF_INET, &node->ip4, buff, len);
224             return 1;
225           }
226           break;
227         case AF_INET6:
228           if(node->has_ip6) {
229             inet_ntop(AF_INET6, &node->ip6, buff, len);
230             return 1;
231           }
232           break;
233       }
234     }
235   }
236
237   rv = -1;
238   DCLOCK();
239   n = mtev_skiplist_find(&nc_dns_cache, target, NULL);
240   if(n != NULL) {
241     if(n->last_updated == 0) goto leave; /* not resolved yet */
242     rv = n->ip4_cnt + n->ip6_cnt;
243     for(i=0; i<2; i++) {
244       switch(progression[i]) {
245         case AF_INET:
246           if(n->ip4_cnt > 0) {
247             inet_ntop(AF_INET, &n->ip4[0], buff, len);
248             goto leave;
249           }
250           break;
251         case AF_INET6:
252           if(n->ip6_cnt > 0) {
253             inet_ntop(AF_INET6, &n->ip6[0], buff, len);
254             goto leave;
255           }
256           break;
257       }
258     }
259   }
260  leave:
261   DCUNLOCK();
262   return rv;
263 }
264
265 /* You are assumed to be holding the lock when in these blanking functions */
266 static void blank_update_v4(dns_cache_node *n) {
267   if(n->ip4) free(n->ip4);
268   n->ip4 = NULL;
269   n->ip4_cnt = 0;
270   mtev_skiplist_remove(&nc_dns_cache, n->target, NULL);
271   n->last_updated = time(NULL);
272   if (n->lookup_inflight_v6) {
273     n->ttl = DEFAULT_FAILED_TTL;
274   }
275   n->lookup_inflight_v4 = mtev_false;
276   mtev_skiplist_insert(&nc_dns_cache, n);
277 }
278 static void blank_update_v6(dns_cache_node *n) {
279   if(n->ip6) free(n->ip6);
280   n->ip6 = NULL;
281   n->ip6_cnt = 0;
282   mtev_skiplist_remove(&nc_dns_cache, n->target, NULL);
283   n->last_updated = time(NULL);
284   if (n->lookup_inflight_v4) {
285     n->ttl = DEFAULT_FAILED_TTL;
286   }
287   n->lookup_inflight_v6 = mtev_false;
288   mtev_skiplist_insert(&nc_dns_cache, n);
289 }
290 static void blank_update(dns_cache_node *n) {
291   blank_update_v4(n);
292   blank_update_v6(n);
293 }
294
295 static int dns_cache_callback(eventer_t e, int mask, void *closure,
296                               struct timeval *now) {
297   struct dns_ctx *ctx = closure;
298   dns_ioevent(ctx, now->tv_sec);
299   return EVENTER_READ | EVENTER_EXCEPTION;
300 }
301
302 static int dns_invoke_timeouts(eventer_t e, int mask, void *closure,
303                                struct timeval *now) {
304   struct dns_ctx *ctx = closure;
305   dns_timeouts(ctx, 0, now->tv_sec);
306   return 0;
307 }
308
309 static void dns_cache_utm_fn(struct dns_ctx *ctx, int timeout, void *data) {
310   eventer_t e = NULL, newe = NULL;
311   if(ctx == NULL) e = eventer_remove(dns_cache_timeout);
312   else {
313     if(timeout < 0) e = eventer_remove(dns_cache_timeout);
314     else {
315       newe = eventer_alloc();
316       newe->mask = EVENTER_TIMER;
317       newe->callback = dns_invoke_timeouts;
318       newe->closure = dns_ctx;
319       gettimeofday(&newe->whence, NULL);
320       newe->whence.tv_sec += timeout;
321     }
322   }
323   if(e) eventer_free(e);
324   if(newe) eventer_add(newe);
325   dns_cache_timeout = newe;
326 }
327
328 static void dns_cache_resolve(struct dns_ctx *ctx, void *result, void *data,
329                               enum dns_type rtype) {
330   int ttl, acnt, r = dns_status(ctx), idnlen;
331   dns_cache_node *n = data;
332   unsigned char idn[DNS_MAXDN], dn[DNS_MAXDN];
333   struct dns_parse p;
334   struct dns_rr rr;
335   unsigned nrr;
336   struct in_addr *answers4;
337   struct in6_addr *answers6;
338   const unsigned char *pkt, *cur, *end;
339
340   if(!result) goto blank;
341
342   dns_dntodn(n->dn, idn, sizeof(idn));
343   idnlen = dns_dnlen(idn);
344
345   pkt = result; end = pkt + r; cur = dns_payload(pkt);
346   dns_getdn(pkt, &cur, end, dn, sizeof(dn));
347   dns_initparse(&p, NULL, pkt, cur, end);
348   p.dnsp_qcls = 0;
349   p.dnsp_qtyp = 0;
350   nrr = 0;
351   ttl = -1;
352
353   while((r = dns_nextrr(&p, &rr)) > 0) {
354     int dnlen = dns_dnlen(rr.dnsrr_dn);
355     /* if we aren't searching and the don't match...
356      * or if we are searching and the prefixes don't match...
357      */
358     if ((dns_search_flag && !dns_dnequal(idn, rr.dnsrr_dn)) ||
359         (dns_search_flag == 0 &&
360          (idnlen > dnlen || memcmp(idn, rr.dnsrr_dn, idnlen-1)))) continue;
361     if (DNS_C_IN == rr.dnsrr_cls && rtype == rr.dnsrr_typ) ++nrr;
362     else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
363       if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
364                     p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
365           rr.dnsrr_dptr != rr.dnsrr_dend) {
366         break;
367       }
368       else {
369         if(ttl < 0 || (int)rr.dnsrr_ttl < ttl)
370           ttl = rr.dnsrr_ttl;
371         dns_dntodn(p.dnsp_dnbuf, idn, sizeof(idn));
372         idnlen = dns_dnlen(idn);
373       }
374     }
375   }
376   if(!r && !nrr) goto blank;
377
378   dns_rewind(&p, NULL);
379   p.dnsp_qcls = DNS_C_IN;
380   p.dnsp_qtyp = rtype;
381   if(rtype == DNS_T_A)
382     answers4 = calloc(nrr, sizeof(*answers4));
383   else if(rtype == DNS_T_AAAA)
384     answers6 = calloc(nrr, sizeof(*answers6));
385   acnt = 0;
386   while(dns_nextrr(&p, &rr) && nrr < MAX_RR) {
387     int dnlen = dns_dnlen(rr.dnsrr_dn);
388     if ((dns_search_flag && !dns_dnequal(idn, rr.dnsrr_dn)) ||
389         (dns_search_flag == 0 &&
390          (idnlen > dnlen || memcmp(idn, rr.dnsrr_dn, idnlen-1)))) continue;
391     if (p.dnsp_rrl && !rr.dnsrr_dn[0] && rr.dnsrr_typ == DNS_T_OPT) continue;
392     if (rtype == rr.dnsrr_typ) {
393       if(ttl < 0 || (int)rr.dnsrr_ttl < ttl)
394         ttl = rr.dnsrr_ttl;
395       switch(rr.dnsrr_typ) {
396         case DNS_T_A:
397           if(rr.dnsrr_dsz != 4) continue;
398           memcpy(&answers4[acnt++], rr.dnsrr_dptr, rr.dnsrr_dsz);
399           break;
400         case DNS_T_AAAA:
401           if(rr.dnsrr_dsz != 16) continue;
402           memcpy(&answers6[acnt++], rr.dnsrr_dptr, rr.dnsrr_dsz);
403           break;
404         default:
405           break;
406       }
407     }
408   }
409
410   if(ttl < 0)
411     ttl = 0;
412   n->ttl = ttl;
413   if(rtype == DNS_T_A) {
414     if(n->ip4) free(n->ip4);
415     n->ip4_cnt = acnt;
416     n->ip4 = answers4;
417     n->lookup_inflight_v4 = mtev_false;
418   }
419   else if(rtype == DNS_T_AAAA) {
420     if(n->ip6) free(n->ip6);
421     n->ip6_cnt = acnt;
422     n->ip6 = answers6;
423     n->lookup_inflight_v6 = mtev_false;
424   }
425   else {
426     if(result) free(result);
427     return;
428   }
429   DCLOCK();
430   mtev_skiplist_remove(&nc_dns_cache, n->target, NULL);
431   n->last_updated = time(NULL);
432   mtev_skiplist_insert(&nc_dns_cache, n);
433   DCUNLOCK();
434   mtevL(noit_debug, "Resolved %s/%s -> %d records\n", n->target,
435         (rtype == DNS_T_AAAA ? "IPv6" : (rtype == DNS_T_A ? "IPv4" : "???")),
436         acnt);
437   if(result) free(result);
438   return;
439
440  blank:
441   DCLOCK();
442   if(rtype == DNS_T_A) blank_update_v4(n);
443   if(rtype == DNS_T_AAAA) blank_update_v6(n);
444   DCUNLOCK();
445   mtevL(noit_debug, "Resolved %s/%s -> blank\n", n->target,
446         (rtype == DNS_T_AAAA ? "IPv6" : (rtype == DNS_T_A ? "IPv4" : "???")));
447   if(result) free(result);
448   return;
449 }
450 static void dns_cache_resolve_v4(struct dns_ctx *ctx, void *result, void *data) {
451   dns_cache_resolve(ctx, result, data, DNS_T_A);
452 }
453 static void dns_cache_resolve_v6(struct dns_ctx *ctx, void *result, void *data) {
454   dns_cache_resolve(ctx, result, data, DNS_T_AAAA);
455 }
456
457 void noit_check_resolver_maintain() {
458   time_t now;
459   mtev_skiplist *tlist;
460   mtev_skiplist_node *sn;
461
462   now = time(NULL);
463   sn = mtev_skiplist_getlist(nc_dns_cache.index);
464   mtevAssert(sn);
465   tlist = sn->data;
466   mtevAssert(tlist);
467   sn = mtev_skiplist_getlist(tlist);
468   while(sn) {
469     dns_cache_node *n = sn->data;
470     mtev_skiplist_next(tlist, &sn); /* move forward */
471     /* remove if needed */
472     if(n->last_updated + n->ttl > now) break;
473     if(n->last_needed + DEFAULT_PURGE_AGE < now &&
474        !(n->lookup_inflight_v4 || n->lookup_inflight_v6))
475       mtev_skiplist_remove(&nc_dns_cache, n->target, dns_cache_node_free);
476     else {
477       int abs;
478       if(!dns_ptodn(n->target, strlen(n->target),
479                     n->dn, sizeof(n->dn), &abs)) {
480         blank_update(n);
481       }
482       else {
483         if(!n->lookup_inflight_v4) {
484           n->lookup_inflight_v4 = mtev_true;
485           if(!dns_submit_dn(dns_ctx, n->dn, DNS_C_IN, DNS_T_A,
486                             abs | dns_search_flag, NULL, dns_cache_resolve_v4, n))
487             blank_update_v4(n);
488           else
489             dns_timeouts(dns_ctx, -1, now);
490         }
491         if(!n->lookup_inflight_v6) {
492           n->lookup_inflight_v6 = mtev_true;
493           if(!dns_submit_dn(dns_ctx, n->dn, DNS_C_IN, DNS_T_AAAA,
494                             abs | dns_search_flag, NULL, dns_cache_resolve_v6, n))
495             blank_update_v6(n);
496           else
497             dns_timeouts(dns_ctx, -1, now);
498         }
499       }
500       mtevL(noit_debug, "Firing lookup for '%s'\n", n->target);
501       continue;
502     }
503   }
504
505   /* If we have a cache implementation */
506   if(noit_resolver_cache_store_hook_exists()) {
507     /* And that implementation is interested in getting a dump... */
508     if(noit_resolver_cache_store_hook_invoke(NULL, NULL, 0) == MTEV_HOOK_CONTINUE) {
509       mtev_skiplist_node *sn;
510       /* dump it all */
511       DCLOCK();
512       for(sn = mtev_skiplist_getlist(&nc_dns_cache); sn;
513           mtev_skiplist_next(&nc_dns_cache, &sn)) {
514         int sbuffsize;
515         char sbuff[1024];
516         dns_cache_node *n = (dns_cache_node *)sn->data;
517         sbuffsize = dns_cache_node_serialize(sbuff, sizeof(sbuff), n);
518         if(sbuffsize > 0)
519           noit_resolver_cache_store_hook_invoke(n->target, sbuff, sbuffsize);
520       }
521       DCUNLOCK();
522     }
523   }
524 }
525
526 int noit_check_resolver_loop(eventer_t e, int mask, void *c,
527                              struct timeval *now) {
528   noit_check_resolver_maintain();
529   eventer_add_in_s_us(noit_check_resolver_loop, NULL, 1, 0);
530   return 0;
531 }
532
533 static int
534 nc_print_dns_cache_node(mtev_console_closure_t ncct,
535                         const char *target, dns_cache_node *n) {
536   nc_printf(ncct, "==== %s ====\n", target);
537   if(!n) nc_printf(ncct, "NOT FOUND\n");
538   else {
539     int i;
540     char buff[INET6_ADDRSTRLEN];
541     time_t now = time(NULL);
542     nc_printf(ncct, "%16s: %ds ago\n", "last needed", now - n->last_needed);
543     nc_printf(ncct, "%16s: %ds ago\n", "resolved", now - n->last_updated);
544     nc_printf(ncct, "%16s: %ds\n", "ttl", n->ttl);
545     if(n->lookup_inflight_v4) nc_printf(ncct, "actively resolving A RRs\n");
546     if(n->lookup_inflight_v6) nc_printf(ncct, "actively resolving AAAA RRs\n");
547     for(i=0;i<n->ip4_cnt;i++) {
548       inet_ntop(AF_INET, &n->ip4[i], buff, sizeof(buff));
549       nc_printf(ncct, "%17s %s\n", i?"":"IPv4:", buff);
550     }
551     for(i=0;i<n->ip6_cnt;i++) {
552       inet_ntop(AF_INET6, &n->ip6[i], buff, sizeof(buff));
553       nc_printf(ncct, "%17s %s\n", i?"":"IPv6:", buff);
554     }
555   }
556   return 0;
557 }
558 static int
559 noit_console_show_dns_cache(mtev_console_closure_t ncct,
560                             int argc, char **argv,
561                             mtev_console_state_t *dstate,
562                             void *closure) {
563   int i;
564
565   DCLOCK();
566   if(argc == 0) {
567     mtev_skiplist_node *sn;
568     for(sn = mtev_skiplist_getlist(&nc_dns_cache); sn;
569         mtev_skiplist_next(&nc_dns_cache, &sn)) {
570       dns_cache_node *n = (dns_cache_node *)sn->data;
571       nc_print_dns_cache_node(ncct, n->target, n);
572     }
573   }
574   for(i=0;i<argc;i++) {
575     dns_cache_node *n;
576     n = mtev_skiplist_find(&nc_dns_cache, argv[i], NULL);
577     nc_print_dns_cache_node(ncct, argv[i], n);
578   }
579   DCUNLOCK();
580   return 0;
581 }
582 static int
583 noit_console_manip_dns_cache(mtev_console_closure_t ncct,
584                              int argc, char **argv,
585                              mtev_console_state_t *dstate,
586                              void *closure) {
587   int i;
588   if(argc == 0) {
589     nc_printf(ncct, "dns_cache what?\n");
590     return 0;
591   }
592   DCLOCK();
593   if(closure == NULL) {
594     /* adding */
595     for(i=0;i<argc;i++) {
596       dns_cache_node *n;
597       n = mtev_skiplist_find(&nc_dns_cache, argv[i], NULL);
598       if(NULL != n) {
599         nc_printf(ncct, " == Already in system ==\n");
600         nc_print_dns_cache_node(ncct, argv[i], n);
601       }
602       else {
603         nc_printf(ncct, "%s submitted.\n", argv[i]);
604         noit_check_resolver_remind_internal(argv[i], mtev_false);
605       }
606     }
607   }
608   else {
609     for(i=0;i<argc;i++) {
610       dns_cache_node *n;
611       n = mtev_skiplist_find(&nc_dns_cache, argv[i], NULL);
612       if(NULL != n) {
613         if(n->lookup_inflight_v4 || n->lookup_inflight_v6)
614           nc_printf(ncct, "%s is currently resolving and cannot be removed.\n");
615         else {
616           mtev_skiplist_remove(&nc_dns_cache, argv[i], dns_cache_node_free);
617           nc_printf(ncct, "%s removed.\n", argv[i]);
618         }
619       }
620       else nc_printf(ncct, "%s not in system.\n", argv[i]);
621     }
622   }
623   DCUNLOCK();
624   return 0;
625 }
626
627 static void
628 register_console_dns_cache_commands() {
629   mtev_console_state_t *tl;
630   cmd_info_t *showcmd, *nocmd;
631
632   tl = mtev_console_state_initial();
633   showcmd = mtev_console_state_get_cmd(tl, "show");
634   mtevAssert(showcmd && showcmd->dstate);
635
636   nocmd = mtev_console_state_get_cmd(tl, "no");
637   mtevAssert(nocmd && nocmd->dstate);
638
639   mtev_console_state_add_cmd(showcmd->dstate,
640     NCSCMD("dns_cache", noit_console_show_dns_cache, NULL, NULL, NULL));
641
642   mtev_console_state_add_cmd(tl,
643     NCSCMD("dns_cache", noit_console_manip_dns_cache, NULL, NULL, NULL));
644
645   mtev_console_state_add_cmd(nocmd->dstate,
646     NCSCMD("dns_cache", noit_console_manip_dns_cache, NULL, NULL, (void *)0x1));
647 }
648
649 int
650 noit_check_etc_hosts_cache_refresh(eventer_t e, int mask, void *closure,
651                                    struct timeval *now) {
652   static struct stat last_stat;
653   struct stat sb;
654   struct hostent *ent;
655   int reload = 0;
656
657   memset(&sb, 0, sizeof(sb));
658   stat("/etc/hosts", &sb);
659 #define CSTAT(f) (sb.f == last_stat.f)
660   reload = ! (CSTAT(st_dev) && CSTAT(st_ino) && CSTAT(st_mode) && CSTAT(st_uid) &&
661               CSTAT(st_gid) && CSTAT(st_size) && CSTAT(st_mtime));
662   memcpy(&last_stat, &sb, sizeof(sb));
663
664   if(reload) {
665     mtev_hash_delete_all(&etc_hosts_cache, free, free);
666     while(NULL != (ent = gethostent())) {
667       int i = 0;
668       char *name = ent->h_name;
669       while(name) {
670         void *vnode;
671         static_host_node *node;
672         if(!mtev_hash_retrieve(&etc_hosts_cache, name, strlen(name), &vnode)) {
673           vnode = node = calloc(1, sizeof(*node));
674           node->target = strdup(name);
675           mtev_hash_store(&etc_hosts_cache, node->target, strlen(node->target), node);
676         }
677         node = vnode;
678  
679         if(ent->h_addrtype == AF_INET) {
680           node->has_ip4 = 1;
681           memcpy(&node->ip4, ent->h_addr_list[0], ent->h_length);
682         }
683         if(ent->h_addrtype == AF_INET6) {
684           node->has_ip6 = 1;
685           memcpy(&node->ip6, ent->h_addr_list[0], ent->h_length);
686         }
687        
688         name = ent->h_aliases[i++];
689       }
690     }
691     endhostent();
692     mtevL(noit_debug, "reloaded %d /etc/hosts targets\n", mtev_hash_size(&etc_hosts_cache));
693   }
694
695   eventer_add_in_s_us(noit_check_etc_hosts_cache_refresh, NULL, 1, 0);
696   return 0;
697 }
698
699 void noit_check_resolver_init() {
700   int cnt;
701   mtev_conf_section_t *servers, *searchdomains;
702   eventer_t e;
703   if(dns_init(NULL, 0) < 0)
704     mtevL(noit_error, "dns initialization failed.\n");
705   dns_ctx = dns_new(NULL);
706   if(dns_init(dns_ctx, 0) != 0) {
707     mtevL(noit_error, "dns initialization failed.\n");
708     exit(-1);
709   }
710
711   /* Optional servers */
712   servers = mtev_conf_get_sections(NULL, "//resolver//server", &cnt);
713   if(cnt) {
714     int i;
715     char server[128];
716     dns_add_serv(dns_ctx, NULL); /* reset */
717     for(i=0;i<cnt;i++) {
718       if(mtev_conf_get_stringbuf(servers[i], "self::node()",
719                                  server, sizeof(server))) {
720         if(dns_add_serv(dns_ctx, server) < 0) {
721           mtevL(noit_error, "Failed adding DNS server: %s\n", server);
722         }
723       }
724     }
725     free(servers);
726   }
727   searchdomains = mtev_conf_get_sections(NULL, "//resolver//search", &cnt);
728   if(cnt) {
729     int i;
730     char search[128];
731     dns_add_srch(dns_ctx, NULL); /* reset */
732     for(i=0;i<cnt;i++) {
733       if(mtev_conf_get_stringbuf(searchdomains[i], "self::node()",
734                                  search, sizeof(search))) {
735         if(dns_add_srch(dns_ctx, search) < 0) {
736           mtevL(noit_error, "Failed adding DNS search path: %s\n", search);
737         }
738         else if(dns_search_flag) dns_search_flag = 0; /* enable search */
739       }
740     }
741     free(searchdomains);
742   }
743
744   if(mtev_conf_get_int(NULL, "//resolver/@ndots", &cnt))
745     dns_set_opt(dns_ctx, DNS_OPT_NDOTS, cnt);
746
747   if(mtev_conf_get_int(NULL, "//resolver/@ntries", &cnt))
748     dns_set_opt(dns_ctx, DNS_OPT_NTRIES, cnt);
749
750   if(mtev_conf_get_int(NULL, "//resolver/@timeout", &cnt))
751     dns_set_opt(dns_ctx, DNS_OPT_TIMEOUT, cnt);
752
753   if(dns_open(dns_ctx) < 0) {
754     mtevL(noit_error, "dns open failed.\n");
755     exit(-1);
756   }
757   eventer_name_callback("dns_cache_callback", dns_cache_callback);
758   dns_set_tmcbck(dns_ctx, dns_cache_utm_fn, dns_ctx);
759   e = eventer_alloc();
760   e->mask = EVENTER_READ | EVENTER_EXCEPTION;
761   e->closure = dns_ctx;
762   e->callback = dns_cache_callback;
763   e->fd = dns_sock(dns_ctx);
764   eventer_add(e);
765
766   mtev_skiplist_init(&nc_dns_cache);
767   mtev_skiplist_set_compare(&nc_dns_cache, name_lookup, name_lookup_k);
768   mtev_skiplist_add_index(&nc_dns_cache, refresh_idx, refresh_idx_k);
769
770   /* maybe load it from cache */
771   if(noit_resolver_cache_load_hook_exists()) {
772     struct timeval now;
773     char *key;
774     void *data;
775     int len;
776     gettimeofday(&now, NULL);
777     while(noit_resolver_cache_load_hook_invoke(&key, &data, &len) == MTEV_HOOK_CONTINUE) {
778       dns_cache_node *n;
779       n = calloc(1, sizeof(*n));
780       if(dns_cache_node_deserialize(n, data, len) >= 0) {
781         n->target = strdup(key);
782         /* if the TTL indicates that it will expire in less than 60 seconds
783          * (including stuff that should have already expired), then fudge
784          * the last_updated time to make it expire some random time within
785          * the next 60 seconds.
786          */
787         if(n->last_needed > now.tv_sec || n->last_updated > now.tv_sec)
788           break; /* impossible */
789
790         n->last_needed = now.tv_sec;
791         if(n->last_updated + n->ttl < now.tv_sec + 60) {
792           int fudge = MIN(60, n->ttl) + 1;
793           n->last_updated = now.tv_sec - n->ttl + (lrand48() % fudge);
794         }
795         DCLOCK();
796         mtev_skiplist_insert(&nc_dns_cache, n);
797         DCUNLOCK();
798         n = NULL;
799       }
800       else {
801         mtevL(noit_error, "Failed to deserialize resolver cache record.\n");
802       }
803       if(n) dns_cache_node_free(n);
804       if(key) free(key);
805       if(data) free(data);
806     }
807   }
808
809   noit_check_resolver_loop(NULL, 0, NULL, NULL);
810   register_console_dns_cache_commands();
811
812   mtev_hash_init(&etc_hosts_cache);
813   noit_check_etc_hosts_cache_refresh(NULL, 0, NULL, NULL);
814 }
815
816 int noit_check_should_resolve_targets(mtev_boolean *should_resolve) {
817   static int inited = 0, cached_rv;;
818   static mtev_boolean cached_should_resolve;
819   if(!inited) {
820     cached_rv = mtev_conf_get_boolean(NULL, "//checks/@resolve_targets",
821                                       &cached_should_resolve);
822     inited = 1;
823   }
824   *should_resolve = cached_should_resolve;
825   return cached_rv;
826 }
Note: See TracBrowser for help on using the browser.