root/src/noit_check_resolver.c

Revision 1a3ef9c2983848a08216da851e782499a7e7e6ce, 15.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 months ago)

Some debugging and fix the leaking of the udns result.

  • 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 60
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   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   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   noitL(noit_debug, "Resolved %s/%s -> %d records\n", n->target,
308         (rtype == DNS_T_AAAA ? "IPv6" : (rtype == DNS_T_A ? "IPv4" : "???")),
309         acnt);
310   if(result) free(result);
311   return;
312
313  blank:
314   if(rtype == DNS_T_A) blank_update_v4(n);
315   if(rtype == DNS_T_AAAA) blank_update_v6(n);
316   noitL(noit_debug, "Resolved %s/%s -> blank\n", n->target,
317         (rtype == DNS_T_AAAA ? "IPv6" : (rtype == DNS_T_A ? "IPv4" : "???")));
318   if(result) free(result);
319   return;
320 }
321 static void dns_cache_resolve_v4(struct dns_ctx *ctx, void *result, void *data) {
322   dns_cache_resolve(ctx, result, data, DNS_T_A);
323 }
324 static void dns_cache_resolve_v6(struct dns_ctx *ctx, void *result, void *data) {
325   dns_cache_resolve(ctx, result, data, DNS_T_AAAA);
326 }
327
328 void noit_check_resolver_maintain() {
329   time_t now;
330   noit_skiplist *tlist;
331   noit_skiplist_node *sn;
332
333   now = time(NULL);
334   sn = noit_skiplist_getlist(nc_dns_cache.index);
335   tlist = sn->data;
336   sn = noit_skiplist_getlist(tlist);
337   while(sn) {
338     dns_cache_node *n = sn->data;
339     noit_skiplist_next(tlist, &sn); /* move forward */
340     /* remove if needed */
341     if(n->last_updated + n->ttl > now) break;
342     if(n->last_needed + DEFAULT_PURGE_AGE < now &&
343        !(n->lookup_inflight_v4 || n->lookup_inflight_v6))
344       noit_skiplist_remove(&nc_dns_cache, n->target, dns_cache_node_free);
345     else {
346       int abs;
347       if(!dns_ptodn(n->target, strlen(n->target),
348                     n->dn, sizeof(n->dn), &abs)) {
349         blank_update(n);
350       }
351       else {
352         if(!n->lookup_inflight_v4) {
353           n->lookup_inflight_v4 = noit_true;
354           if(!dns_submit_dn(dns_ctx, n->dn, DNS_C_IN, DNS_T_A,
355                             abs | DNS_NOSRCH, NULL, dns_cache_resolve_v4, n))
356             blank_update_v4(n);
357           else
358             dns_timeouts(dns_ctx, -1, now);
359         }
360         if(!n->lookup_inflight_v6) {
361           n->lookup_inflight_v6 = noit_true;
362           if(!dns_submit_dn(dns_ctx, n->dn, DNS_C_IN, DNS_T_AAAA,
363                             abs | DNS_NOSRCH, NULL, dns_cache_resolve_v6, n))
364             blank_update_v6(n);
365           else
366             dns_timeouts(dns_ctx, -1, now);
367         }
368       }
369       noitL(noit_debug, "Firing lookup for '%s'\n", n->target);
370       continue;
371     }
372   }
373 }
374
375 int noit_check_resolver_loop(eventer_t e, int mask, void *c,
376                              struct timeval *now) {
377   noit_check_resolver_maintain();
378   eventer_add_in_s_us(noit_check_resolver_loop, NULL, 1, 0);
379   return 0;
380 }
381
382 static int
383 nc_print_dns_cache_node(noit_console_closure_t ncct,
384                         const char *target, dns_cache_node *n) {
385   nc_printf(ncct, "==== %s ====\n", target);
386   if(!n) nc_printf(ncct, "NOT FOUND\n");
387   else {
388     int i;
389     time_t now = time(NULL);
390     nc_printf(ncct, "%16s: %ds ago\n", "last needed", now - n->last_needed);
391     nc_printf(ncct, "%16s: %ds ago\n", "resolved", now - n->last_updated);
392     nc_printf(ncct, "%16s: %ds\n", "ttl", n->ttl);
393     if(n->lookup_inflight_v4) nc_printf(ncct, "actively resolving A RRs\n");
394     if(n->lookup_inflight_v6) nc_printf(ncct, "actively resolving AAAA RRs\n");
395     for(i=0;i<n->ip4_cnt;i++)
396       nc_printf(ncct, "%17s %s\n", i?"":"IPv4:", n->ip4[i]);
397     for(i=0;i<n->ip6_cnt;i++)
398       nc_printf(ncct, "%17s %s\n", i?"":"IPv6:", n->ip6[i]);
399   }
400   return 0;
401 }
402 static int
403 noit_console_show_dns_cache(noit_console_closure_t ncct,
404                             int argc, char **argv,
405                             noit_console_state_t *dstate,
406                             void *closure) {
407   int i;
408
409   if(argc == 0) {
410     noit_skiplist_node *sn;
411     for(sn = noit_skiplist_getlist(&nc_dns_cache); sn;
412         noit_skiplist_next(&nc_dns_cache, &sn)) {
413       dns_cache_node *n = (dns_cache_node *)sn->data;
414       nc_print_dns_cache_node(ncct, n->target, n);
415     }
416   }
417   for(i=0;i<argc;i++) {
418     dns_cache_node *n;
419     n = noit_skiplist_find(&nc_dns_cache, argv[i], NULL);
420     nc_print_dns_cache_node(ncct, argv[i], n);
421   }
422   return 0;
423 }
424 static int
425 noit_console_manip_dns_cache(noit_console_closure_t ncct,
426                              int argc, char **argv,
427                              noit_console_state_t *dstate,
428                              void *closure) {
429   int i;
430   if(argc == 0) {
431     nc_printf(ncct, "dns_cache what?\n");
432     return 0;
433   }
434   if(closure == NULL) {
435     /* adding */
436     for(i=0;i<argc;i++) {
437       dns_cache_node *n;
438       if(NULL != (n = noit_skiplist_find(&nc_dns_cache, argv[i], NULL))) {
439         nc_printf(ncct, " == Already in system ==\n");
440         nc_print_dns_cache_node(ncct, argv[i], n);
441       }
442       else {
443         nc_printf(ncct, "%s submitted.\n", argv[i]);
444         noit_check_resolver_remind(argv[i]);
445       }
446     }
447   }
448   else {
449     for(i=0;i<argc;i++) {
450       dns_cache_node *n;
451       if(NULL != (n = noit_skiplist_find(&nc_dns_cache, argv[i], NULL))) {
452         if(n->lookup_inflight_v4 || n->lookup_inflight_v6)
453           nc_printf(ncct, "%s is currently resolving and cannot be removed.\n");
454         else {
455           noit_skiplist_remove(&nc_dns_cache, argv[i], dns_cache_node_free);
456           nc_printf(ncct, "%s removed.\n", argv[i]);
457         }
458       }
459       else nc_printf(ncct, "%s not in system.\n", argv[i]);
460     }
461   }
462   return 0;
463 }
464
465 static void
466 register_console_dns_cache_commands() {
467   noit_console_state_t *tl;
468   cmd_info_t *showcmd, *nocmd;
469
470   tl = noit_console_state_initial();
471   showcmd = noit_console_state_get_cmd(tl, "show");
472   assert(showcmd && showcmd->dstate);
473
474   nocmd = noit_console_state_get_cmd(tl, "no");
475   assert(nocmd && nocmd->dstate);
476
477   noit_console_state_add_cmd(showcmd->dstate,
478     NCSCMD("dns_cache", noit_console_show_dns_cache, NULL, NULL, NULL));
479
480   noit_console_state_add_cmd(tl,
481     NCSCMD("dns_cache", noit_console_manip_dns_cache, NULL, NULL, NULL));
482
483   noit_console_state_add_cmd(nocmd->dstate,
484     NCSCMD("dns_cache", noit_console_manip_dns_cache, NULL, NULL, (void *)0x1));
485 }
486
487
488 void noit_check_resolver_init() {
489   eventer_t e;
490   if(dns_init(NULL, 0) < 0)
491     noitL(noit_error, "dns initialization failed.\n");
492   dns_ctx = dns_new(NULL);
493   if(dns_init(dns_ctx, 0) != 0 ||
494      dns_open(dns_ctx) < 0) {
495     noitL(noit_error, "dns initialization failed.\n");
496   }
497   eventer_name_callback("dns_cache_callback", dns_cache_callback);
498   dns_set_tmcbck(dns_ctx, dns_cache_utm_fn, dns_ctx);
499   e = eventer_alloc();
500   e->mask = EVENTER_READ | EVENTER_EXCEPTION;
501   e->closure = dns_ctx;
502   e->callback = dns_cache_callback;
503   e->fd = dns_sock(dns_ctx);
504   eventer_add(e);
505
506   noit_skiplist_init(&nc_dns_cache);
507   noit_skiplist_set_compare(&nc_dns_cache, name_lookup, name_lookup_k);
508   noit_skiplist_add_index(&nc_dns_cache, refresh_idx, refresh_idx_k);
509   noit_check_resolver_loop(NULL, 0, NULL, NULL);
510   register_console_dns_cache_commands();
511 }
Note: See TracBrowser for help on using the browser.