root/src/noit_check_resolver.c

Revision 590663b0b72cf0548c8748144ff426dac89b04f3, 25.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 months ago)

recursive lock attempt causes deadlock

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