root/src/noit_check_resolver.c

Revision 549630e8dfdd110460af2a2f0818a3cc85677a00, 24.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 weeks ago)

off-by-one error in fudge of ttl expiry results int modulo 0

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