root/src/noit_check_resolver.c

Revision db037b88b1a8392d998a758523e8d0bd66113c89, 15.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

if we get multiple TTLs in the response, we should fudge it to the lowest one

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2011, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <time.h>
39 #include <ctype.h>
40 #include <assert.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43
44 #include "eventer/eventer.h"
45 #include "utils/noit_log.h"
46 #include "utils/noit_skiplist.h"
47 #include "udns/udns.h"
48 #include "noit_console.h"
49
50 #define MAX_RR 256
51 #define DEFAULT_FAILED_TTL 300
52 #define DEFAULT_PURGE_AGE  1200 /* 20 minutes */
53
54 static struct dns_ctx *dns_ctx;
55 static noit_skiplist nc_dns_cache;
56 static eventer_t dns_cache_timeout = NULL;
57
58 typedef struct {
59   time_t last_needed;
60   time_t last_updated;
61   noit_boolean lookup_inflight_v4;
62   noit_boolean lookup_inflight_v6;
63   char *target;
64   unsigned char dn[DNS_MAXDN];
65   time_t ttl;
66   int ip4_cnt;
67   char **ip4;
68   int ip6_cnt;
69   char **ip6;
70 } dns_cache_node;
71
72 void dns_cache_node_free(void *vn) {
73   dns_cache_node *n = vn;
74   int i;
75   if(!n) return;
76   if(n->target) free(n->target);
77   for(i=0;i<n->ip4_cnt;i++) if(n->ip4[i]) free(n->ip4[i]);
78   for(i=0;i<n->ip6_cnt;i++) if(n->ip6[i]) free(n->ip6[i]);
79   if(n->ip4) free(n->ip4);
80   if(n->ip6) free(n->ip6);
81   free(n);
82 }
83
84 static int name_lookup(const void *av, const void *bv) {
85   const dns_cache_node *a = av;
86   const dns_cache_node *b = bv;
87   return strcmp(a->target, b->target);
88 }
89 static int name_lookup_k(const void *akv, const void *bv) {
90   const char *ak = akv;
91   const dns_cache_node *b = bv;
92   return strcmp(ak, b->target);
93 }
94 static int refresh_idx(const void *av, const void *bv) {
95   const dns_cache_node *a = av;
96   const dns_cache_node *b = bv;
97   if((a->last_updated + a->ttl) < (b->last_updated + b->ttl)) return -1;
98   return 1;
99 }
100 static int refresh_idx_k(const void *akv, const void *bv) {
101   time_t f = (time_t) akv;
102   const dns_cache_node *b = bv;
103   if(f < (b->last_updated + b->ttl)) return -1;
104   return 1;
105 }
106
107 void noit_check_resolver_remind(const char *target) {
108   dns_cache_node *n;
109   if(!target) return;
110   n = noit_skiplist_find(&nc_dns_cache, target, NULL);
111   if(n != NULL) {
112     n->last_needed = time(NULL);
113     return;
114   }
115   n = calloc(1, sizeof(*n));
116   n->target = strdup(target);
117   n->last_needed = time(NULL);
118   noit_skiplist_insert(&nc_dns_cache, n);
119 }
120
121
122 int noit_check_resolver_fetch(const char *target, char *buff, int len,
123                               uint8_t prefer_family) {
124   int i;
125   uint8_t progression[2];
126   dns_cache_node *n;
127
128   buff[0] = '\0';
129   if(!target) return -1;
130   progression[0] = prefer_family;
131   progression[1] = (prefer_family == AF_INET) ? AF_INET6 : AF_INET;
132
133   n = noit_skiplist_find(&nc_dns_cache, target, NULL);
134   if(n != NULL) {
135     int rv;
136     if(n->last_updated == 0) return -1; /* not resolved yet */
137     rv = n->ip4_cnt + n->ip6_cnt;
138     for(i=0; i<2; i++) {
139       switch(progression[i]) {
140         case AF_INET:
141           if(n->ip4_cnt > 0) {
142             strlcpy(buff, n->ip4[0], len);
143             return rv;
144           }
145         case AF_INET6:
146           if(n->ip6_cnt > 0) {
147             strlcpy(buff, n->ip6[0], len);
148             return rv;
149           }
150       }
151     }
152     return rv;
153   }
154   return -1;
155 }
156
157 static void blank_update_v4(dns_cache_node *n) {
158   int i;
159   for(i=0;i<n->ip4_cnt;i++) if(n->ip4[i]) free(n->ip4[i]);
160   if(n->ip4) free(n->ip4);
161   n->ip4 = NULL;
162   n->ip4_cnt = 0;
163   noit_skiplist_remove(&nc_dns_cache, n->target, NULL);
164   n->last_updated = time(NULL);
165   if(n->ttl == 0) n->ttl = DEFAULT_FAILED_TTL;
166   n->lookup_inflight_v4 = noit_false;
167   noit_skiplist_insert(&nc_dns_cache, n);
168 }
169 static void blank_update_v6(dns_cache_node *n) {
170   int i;
171   for(i=0;i<n->ip6_cnt;i++) if(n->ip6[i]) free(n->ip6[i]);
172   if(n->ip6) free(n->ip6);
173   n->ip6 = NULL;
174   n->ip6_cnt = 0;
175   noit_skiplist_remove(&nc_dns_cache, n->target, NULL);
176   n->last_updated = time(NULL);
177   if(n->ttl == 0) n->ttl = DEFAULT_FAILED_TTL;
178   n->lookup_inflight_v6 = noit_false;
179   noit_skiplist_insert(&nc_dns_cache, n);
180 }
181 static void blank_update(dns_cache_node *n) {
182   blank_update_v4(n);
183   blank_update_v6(n);
184 }
185
186 static int dns_cache_callback(eventer_t e, int mask, void *closure,
187                               struct timeval *now) {
188   struct dns_ctx *ctx = closure;
189   dns_ioevent(ctx, now->tv_sec);
190   return EVENTER_READ | EVENTER_EXCEPTION;
191 }
192
193 static int dns_invoke_timeouts(eventer_t e, int mask, void *closure,
194                                struct timeval *now) {
195   struct dns_ctx *ctx = closure;
196   dns_timeouts(ctx, 0, now->tv_sec);
197   return 0;
198 }
199
200 static void dns_cache_utm_fn(struct dns_ctx *ctx, int timeout, void *data) {
201   eventer_t e = NULL, newe = NULL;
202   if(ctx == NULL) e = eventer_remove(dns_cache_timeout);
203   else {
204     if(timeout < 0) e = eventer_remove(dns_cache_timeout);
205     else {
206       newe = eventer_alloc();
207       newe->mask = EVENTER_TIMER;
208       newe->callback = dns_invoke_timeouts;
209       newe->closure = dns_ctx;
210       gettimeofday(&newe->whence, NULL);
211       newe->whence.tv_sec += timeout;
212     }
213   }
214   if(e) eventer_free(e);
215   if(newe) eventer_add(newe);
216   dns_cache_timeout = newe;
217 }
218
219 static void dns_cache_resolve(struct dns_ctx *ctx, void *result, void *data,
220                               enum dns_type rtype) {
221   int i, ttl, acnt, r = dns_status(ctx);
222   dns_cache_node *n = data;
223   unsigned char idn[DNS_MAXDN], dn[DNS_MAXDN];
224   struct dns_parse p;
225   struct dns_rr rr;
226   unsigned nrr;
227   char **answers;
228   const unsigned char *pkt, *cur, *end;
229
230   if(!result) goto blank;
231
232   dns_dntodn(n->dn, idn, sizeof(idn));
233
234   pkt = result; end = pkt + r; cur = dns_payload(pkt);
235   dns_getdn(pkt, &cur, end, dn, sizeof(dn));
236   dns_initparse(&p, NULL, pkt, cur, end);
237   p.dnsp_qcls = 0;
238   p.dnsp_qtyp = 0;
239   nrr = 0;
240   ttl = 0;
241
242   while((r = dns_nextrr(&p, &rr)) > 0) {
243     if (!dns_dnequal(idn, rr.dnsrr_dn)) continue;
244     if (DNS_C_IN == rr.dnsrr_cls && rtype == rr.dnsrr_typ) ++nrr;
245     else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
246       if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
247                     p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
248           rr.dnsrr_dptr != rr.dnsrr_dend) {
249         break;
250       }
251       else {
252         if(rr.dnsrr_ttl > 0 && (ttl == 0 || rr.dnsrr_ttl < ttl))
253           ttl = rr.dnsrr_ttl;
254         dns_dntodn(p.dnsp_dnbuf, idn, sizeof(idn));
255       }
256     }
257   }
258   if(!r && !nrr) goto blank;
259
260   dns_rewind(&p, NULL);
261   p.dnsp_qcls = DNS_C_IN;
262   p.dnsp_qtyp = rtype;
263   answers = calloc(nrr, sizeof(*answers));
264   acnt = 0;
265   while(dns_nextrr(&p, &rr) && nrr < MAX_RR) {
266     char buff[INET6_ADDRSTRLEN];
267     if (!dns_dnequal(idn, rr.dnsrr_dn)) continue;
268     if (p.dnsp_rrl && !rr.dnsrr_dn[0] && rr.dnsrr_typ == DNS_T_OPT) continue;
269     if (rtype == rr.dnsrr_typ) {
270       if(rr.dnsrr_ttl > 0 && (ttl == 0 || rr.dnsrr_ttl < ttl))
271         ttl = rr.dnsrr_ttl;
272       switch(rr.dnsrr_typ) {
273         case DNS_T_A:
274           if(rr.dnsrr_dsz != 4) continue;
275           inet_ntop(AF_INET, rr.dnsrr_dptr, buff, sizeof(buff));
276           answers[acnt++] = strdup(buff);
277           break;
278         case DNS_T_AAAA:
279           if(rr.dnsrr_dsz != 16) continue;
280           inet_ntop(AF_INET6, rr.dnsrr_dptr, buff, sizeof(buff));
281           answers[acnt++] = strdup(buff);
282           break;
283         default:
284           break;
285       }
286     }
287   }
288
289   n->ttl = ttl;
290   if(rtype == DNS_T_A) {
291     for(i=0;i<n->ip4_cnt;i++) if(n->ip4[i]) free(n->ip4[i]);
292     if(n->ip4) free(n->ip4);
293     n->ip4_cnt = acnt;
294     n->ip4 = answers;
295     n->lookup_inflight_v4 = noit_false;
296   }
297   else if(rtype == DNS_T_AAAA) {
298     for(i=0;i<n->ip6_cnt;i++) if(n->ip6[i]) free(n->ip6[i]);
299     if(n->ip6) free(n->ip6);
300     n->ip6_cnt = acnt;
301     n->ip6 = answers;
302     n->lookup_inflight_v6 = noit_false;
303   }
304   noit_skiplist_remove(&nc_dns_cache, n->target, NULL);
305   n->last_updated = time(NULL);
306   noit_skiplist_insert(&nc_dns_cache, n);
307   return;
308
309  blank:
310   if(rtype == DNS_T_A) blank_update_v4(n);
311   if(rtype == DNS_T_AAAA) blank_update_v6(n);
312   return;
313 }
314 static void dns_cache_resolve_v4(struct dns_ctx *ctx, void *result, void *data) {
315   dns_cache_resolve(ctx, result, data, DNS_T_A);
316 }
317 static void dns_cache_resolve_v6(struct dns_ctx *ctx, void *result, void *data) {
318   dns_cache_resolve(ctx, result, data, DNS_T_AAAA);
319 }
320
321 void noit_check_resolver_maintain() {
322   time_t now;
323   noit_skiplist *tlist;
324   noit_skiplist_node *sn;
325
326   now = time(NULL);
327   sn = noit_skiplist_getlist(nc_dns_cache.index);
328   tlist = sn->data;
329   sn = noit_skiplist_getlist(tlist);
330   while(sn) {
331     dns_cache_node *n = sn->data;
332     noit_skiplist_next(tlist, &sn); /* move forward */
333     /* remove if needed */
334     if(n->last_updated + n->ttl > now) break;
335     if(n->last_needed + DEFAULT_PURGE_AGE < now &&
336        !(n->lookup_inflight_v4 || n->lookup_inflight_v6))
337       noit_skiplist_remove(&nc_dns_cache, n->target, dns_cache_node_free);
338     else {
339       int abs;
340       if(!dns_ptodn(n->target, strlen(n->target),
341                     n->dn, sizeof(n->dn), &abs)) {
342         blank_update(n);
343       }
344       else {
345         if(!n->lookup_inflight_v4) {
346           n->lookup_inflight_v4 = noit_true;
347           if(!dns_submit_dn(dns_ctx, n->dn, DNS_C_IN, DNS_T_A,
348                             abs | DNS_NOSRCH, NULL, dns_cache_resolve_v4, n))
349             blank_update_v4(n);
350           else
351             dns_timeouts(dns_ctx, -1, now);
352         }
353         if(!n->lookup_inflight_v6) {
354           n->lookup_inflight_v6 = noit_true;
355           if(!dns_submit_dn(dns_ctx, n->dn, DNS_C_IN, DNS_T_AAAA,
356                             abs | DNS_NOSRCH, NULL, dns_cache_resolve_v6, n))
357             blank_update_v6(n);
358           else
359             dns_timeouts(dns_ctx, -1, now);
360         }
361       }
362       noitL(noit_debug, "Firing lookup for '%s'\n", n->target);
363       continue;
364     }
365   }
366 }
367
368 int noit_check_resolver_loop(eventer_t e, int mask, void *c,
369                              struct timeval *now) {
370   noit_check_resolver_maintain();
371   eventer_add_in_s_us(noit_check_resolver_loop, NULL, 1, 0);
372   return 0;
373 }
374
375 static int
376 nc_print_dns_cache_node(noit_console_closure_t ncct,
377                         const char *target, dns_cache_node *n) {
378   nc_printf(ncct, "==== %s ====\n", target);
379   if(!n) nc_printf(ncct, "NOT FOUND\n");
380   else {
381     int i;
382     time_t now = time(NULL);
383     nc_printf(ncct, "%16s: %ds ago\n", "last needed", now - n->last_needed);
384     nc_printf(ncct, "%16s: %ds ago\n", "resolved", now - n->last_updated);
385     nc_printf(ncct, "%16s: %ds\n", "ttl", n->ttl);
386     if(n->lookup_inflight_v4) nc_printf(ncct, "actively resolving A RRs\n");
387     if(n->lookup_inflight_v6) nc_printf(ncct, "actively resolving AAAA RRs\n");
388     for(i=0;i<n->ip4_cnt;i++)
389       nc_printf(ncct, "%17s %s\n", i?"":"IPv4:", n->ip4[i]);
390     for(i=0;i<n->ip6_cnt;i++)
391       nc_printf(ncct, "%17s %s\n", i?"":"IPv6:", n->ip6[i]);
392   }
393   return 0;
394 }
395 static int
396 noit_console_show_dns_cache(noit_console_closure_t ncct,
397                             int argc, char **argv,
398                             noit_console_state_t *dstate,
399                             void *closure) {
400   int i;
401
402   if(argc == 0) {
403     noit_skiplist_node *sn;
404     for(sn = noit_skiplist_getlist(&nc_dns_cache); sn;
405         noit_skiplist_next(&nc_dns_cache, &sn)) {
406       dns_cache_node *n = (dns_cache_node *)sn->data;
407       nc_print_dns_cache_node(ncct, n->target, n);
408     }
409   }
410   for(i=0;i<argc;i++) {
411     dns_cache_node *n;
412     n = noit_skiplist_find(&nc_dns_cache, argv[i], NULL);
413     nc_print_dns_cache_node(ncct, argv[i], n);
414   }
415   return 0;
416 }
417 static int
418 noit_console_manip_dns_cache(noit_console_closure_t ncct,
419                              int argc, char **argv,
420                              noit_console_state_t *dstate,
421                              void *closure) {
422   int i;
423   if(argc == 0) {
424     nc_printf(ncct, "dns_cache what?\n");
425     return 0;
426   }
427   if(closure == NULL) {
428     /* adding */
429     for(i=0;i<argc;i++) {
430       dns_cache_node *n;
431       if(NULL != (n = noit_skiplist_find(&nc_dns_cache, argv[i], NULL))) {
432         nc_printf(ncct, " == Already in system ==\n");
433         nc_print_dns_cache_node(ncct, argv[i], n);
434       }
435       else {
436         nc_printf(ncct, "%s submitted.\n", argv[i]);
437         noit_check_resolver_remind(argv[i]);
438       }
439     }
440   }
441   else {
442     for(i=0;i<argc;i++) {
443       dns_cache_node *n;
444       if(NULL != (n = noit_skiplist_find(&nc_dns_cache, argv[i], NULL))) {
445         if(n->lookup_inflight_v4 || n->lookup_inflight_v6)
446           nc_printf(ncct, "%s is currently resolving and cannot be removed.\n");
447         else {
448           noit_skiplist_remove(&nc_dns_cache, argv[i], dns_cache_node_free);
449           nc_printf(ncct, "%s removed.\n", argv[i]);
450         }
451       }
452       else nc_printf(ncct, "%s not in system.\n", argv[i]);
453     }
454   }
455   return 0;
456 }
457
458 static void
459 register_console_dns_cache_commands() {
460   noit_console_state_t *tl;
461   cmd_info_t *showcmd, *nocmd;
462
463   tl = noit_console_state_initial();
464   showcmd = noit_console_state_get_cmd(tl, "show");
465   assert(showcmd && showcmd->dstate);
466
467   nocmd = noit_console_state_get_cmd(tl, "no");
468   assert(nocmd && nocmd->dstate);
469
470   noit_console_state_add_cmd(showcmd->dstate,
471     NCSCMD("dns_cache", noit_console_show_dns_cache, NULL, NULL, NULL));
472
473   noit_console_state_add_cmd(tl,
474     NCSCMD("dns_cache", noit_console_manip_dns_cache, NULL, NULL, NULL));
475
476   noit_console_state_add_cmd(nocmd->dstate,
477     NCSCMD("dns_cache", noit_console_manip_dns_cache, NULL, NULL, (void *)0x1));
478 }
479
480
481 void noit_check_resolver_init() {
482   eventer_t e;
483   if(dns_init(NULL, 0) < 0)
484     noitL(noit_error, "dns initialization failed.\n");
485   dns_ctx = dns_new(NULL);
486   if(dns_init(dns_ctx, 0) != 0 ||
487      dns_open(dns_ctx) < 0) {
488     noitL(noit_error, "dns initialization failed.\n");
489   }
490   dns_set_tmcbck(dns_ctx, dns_cache_utm_fn, dns_ctx);
491   e = eventer_alloc();
492   e->mask = EVENTER_READ | EVENTER_EXCEPTION;
493   e->closure = dns_ctx;
494   e->callback = dns_cache_callback;
495   e->fd = dns_sock(dns_ctx);
496   eventer_add(e);
497
498   noit_skiplist_init(&nc_dns_cache);
499   noit_skiplist_set_compare(&nc_dns_cache, name_lookup, name_lookup_k);
500   noit_skiplist_add_index(&nc_dns_cache, refresh_idx, refresh_idx_k);
501   noit_check_resolver_loop(NULL, 0, NULL, NULL);
502   register_console_dns_cache_commands();
503 }
Note: See TracBrowser for help on using the browser.