root/src/noit_check_resolver.c

Revision 0567273f79a651d42a6272b26389bc7b34290e84, 24.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 days ago)

switch to external libudns

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