root/src/modules/dns.c

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

refs #340

This is a rather major change. Targets can now be hostnames in addition
to IP addresses so long as //checks/@resolve_targets is not false.

If a target is entered that does not look like an IP (inet_pton fails)
then the check is marked as needing resolution NP_RESOLVE.

A passive look-aside dns cache has been implemented in noit_check_resolver.c
that is used to power the whole system and some reasonably simply console
command have been provided:

show dns_cache [fqdn1 [fqdn2]] -- shows the state
dns_cache <fqdn> [fqdn2 [fqdn3]] -- submits for lookup
no dns_cache <fqdn> [fqdn2 [fqdn3]] -- purges from cache

The big change is that modules that relied on check->target to be an IP
address are going to explode when names are provided. Instead, modules
should now use target for the provided target (possibly a FQDN) and use
target_ip (check->target_ip or check.target_ip) for a resolved IP address
and also check for the case of empty string: (check->target_ip[0] == '\0')
for the case that resolution has failed. In lua, the target_ip will be
presented as nil in the case of failed name resolution.

I believe I've updated all necessary components of the system for this to
"just work" but people that have implemented their own check should update
them before they elect to use non-IP addresses as targets.

The dns subsystem supports both IPv4 and IPv6, but currently prefers IPv4
addresses if any are present.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, 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 <unistd.h>
37 #include <netdb.h>
38 #include <errno.h>
39 #include <assert.h>
40 #include <arpa/inet.h>
41
42 #include "noit_module.h"
43 #include "noit_check.h"
44 #include "noit_check_tools.h"
45 #include "utils/noit_log.h"
46 #include "utils/noit_atomic.h"
47 #include "udns/udns.h"
48
49 #define MAX_RR 256
50
51 static void eventer_dns_utm_fn(struct dns_ctx *, int, void *);
52 static int dns_eventer_callback(eventer_t, int, void *, struct timeval *);
53
54 static noit_log_stream_t nlerr = NULL;
55 static noit_log_stream_t nldeb = NULL;
56
57 static noit_hash_table dns_rtypes = NOIT_HASH_EMPTY;
58 static noit_hash_table dns_ctypes = NOIT_HASH_EMPTY;
59
60 static noit_hash_table dns_ctx_store = NOIT_HASH_EMPTY;
61 static pthread_mutex_t dns_ctx_store_lock;
62 typedef struct dns_ctx_handle {
63   char *ns;
64   struct dns_ctx *ctx;
65   noit_atomic32_t refcnt;
66   eventer_t e; /* evetner handling UDP traffic */
67   eventer_t timeout; /* the timeout managed by libudns */
68 } dns_ctx_handle_t;
69
70 static int cstring_cmp(const void *a, const void *b) {
71   return strcmp(*((const char **)a), *((const char **)b));
72 }
73
74 static dns_ctx_handle_t *default_ctx_handle = NULL;
75 static void dns_ctx_handle_free(void *vh) {
76   dns_ctx_handle_t *h = vh;
77   assert(h->timeout == NULL);
78   free(h->ns);
79   dns_close(h->ctx);
80   dns_free(h->ctx);
81 }
82 static dns_ctx_handle_t *dns_ctx_alloc(const char *ns) {
83   void *vh;
84   dns_ctx_handle_t *h = NULL;
85   pthread_mutex_lock(&dns_ctx_store_lock);
86   if(ns == NULL && default_ctx_handle != NULL) {
87     /* special case -- default context */
88     h = default_ctx_handle;
89     noit_atomic_inc32(&h->refcnt);
90     goto bail;
91   }
92   if(ns &&
93      noit_hash_retrieve(&dns_ctx_store, ns, strlen(ns), &vh)) {
94     h = (dns_ctx_handle_t *)vh;
95     noit_atomic_inc32(&h->refcnt);
96   }
97   else {
98     int failed = 0;
99     h = calloc(1, sizeof(*h));
100     h->ns = ns ? strdup(ns) : NULL;
101     h->ctx = dns_new(NULL);
102     if(dns_init(h->ctx, 0) != 0) failed++;
103     if(ns) {
104       if(dns_add_serv(h->ctx, NULL) < 0) failed++;
105       if(dns_add_serv(h->ctx, ns) < 0) failed++;
106     }
107     if(dns_open(h->ctx) < 0) failed++;
108     if(failed) {
109       noitL(nlerr, "dns_open failed\n");
110       free(h->ns);
111       free(h);
112       h = NULL;
113       goto bail;
114     }
115     dns_set_tmcbck(h->ctx, eventer_dns_utm_fn, h);
116     h->e = eventer_alloc();
117     h->e->mask = EVENTER_READ | EVENTER_EXCEPTION;
118     h->e->closure = h;
119     h->e->callback = dns_eventer_callback;
120     h->e->fd = dns_sock(h->ctx);
121     eventer_add(h->e);
122     h->refcnt = 1;
123     if(!ns)
124       default_ctx_handle = h;
125     else
126       noit_hash_store(&dns_ctx_store, h->ns, strlen(h->ns), h);
127   }
128  bail:
129   pthread_mutex_unlock(&dns_ctx_store_lock);
130   return h;
131 }
132 static void dns_ctx_release(dns_ctx_handle_t *h) {
133   if(h->ns == NULL) {
134     /* Special case for the default */
135     noit_atomic_dec32(&h->refcnt);
136     return;
137   }
138   pthread_mutex_lock(&dns_ctx_store_lock);
139   if(noit_atomic_dec32(&h->refcnt) == 0) {
140     /* I was the last one */
141     assert(noit_hash_delete(&dns_ctx_store, h->ns, strlen(h->ns),
142                             NULL, dns_ctx_handle_free));
143   }
144   pthread_mutex_unlock(&dns_ctx_store_lock);
145 }
146
147 static noit_hash_table active_events = NOIT_HASH_EMPTY;
148 static pthread_mutex_t active_events_lock;
149
150 typedef struct dns_check_info {
151   stats_t current;
152   int timed_out;
153   noit_module_t *self;
154   noit_check_t *check;
155   eventer_t timeout_event;
156   dns_ctx_handle_t *h;
157   char *error;
158   int nrr;
159   int sort;
160
161   /* These make up the query itself */
162   unsigned char dn[DNS_MAXDN];
163   enum dns_class query_ctype;
164   enum dns_type query_rtype;
165 } dns_check_info_t;
166
167 static int __isactive_ci(struct dns_check_info *ci) {
168   void *u;
169   int exists = 0;
170   pthread_mutex_lock(&active_events_lock);
171   if(noit_hash_retrieve(&active_events, (void *)&ci, sizeof(ci), &u))
172     exists = 1;
173   pthread_mutex_unlock(&active_events_lock);
174   return exists;
175 }
176 static void __activate_ci(struct dns_check_info *ci) {
177   struct dns_check_info **holder;
178   holder = calloc(1, sizeof(*holder));
179   *holder = ci;
180   pthread_mutex_lock(&active_events_lock);
181   assert(noit_hash_store(&active_events, (void *)holder, sizeof(*holder), ci));
182   pthread_mutex_unlock(&active_events_lock);
183 }
184 static void __deactivate_ci(struct dns_check_info *ci) {
185   pthread_mutex_lock(&active_events_lock);
186   assert(noit_hash_delete(&active_events, (void *)&ci, sizeof(ci), free, NULL));
187   pthread_mutex_unlock(&active_events_lock);
188 }
189
190 static void dns_check_log_results(struct dns_check_info *ci) {
191   struct timeval duration;
192   double rtt;
193
194   gettimeofday(&ci->current.whence, NULL);
195   sub_timeval(ci->current.whence, ci->check->last_fire_time, &duration);
196   rtt = duration.tv_sec * 1000.0 + duration.tv_usec / 1000.0;
197   ci->current.duration = rtt;
198
199   ci->current.state = (ci->error || ci->nrr == 0) ? NP_BAD : NP_GOOD;
200   ci->current.available = ci->timed_out ? NP_UNAVAILABLE : NP_AVAILABLE;
201   if(ci->error) {
202     ci->current.status = strdup(ci->error);
203   }
204   else if(!ci->current.status) {
205     char buff[48];
206     snprintf(buff, sizeof(buff), "%d %s",
207              ci->nrr, ci->nrr == 1 ? "record" : "records");
208     ci->current.status = strdup(buff);
209     noit_stats_set_metric(&ci->current, "rtt", METRIC_DOUBLE,
210                           ci->timed_out ? NULL : &rtt);
211   }
212
213   noit_check_set_stats(ci->self, ci->check, &ci->current);
214   if(ci->error) free(ci->error);
215   if(ci->current.status) free(ci->current.status);
216   ci->error = NULL;
217   memset(&ci->current, 0, sizeof(ci->current));
218 }
219
220 static int dns_interpolate_inaddr_arpa(char *buff, int len, const char *ip) {
221   const char *b, *e;
222   char *o;
223   unsigned char dn[DNS_MAXDN];
224   int il;
225   struct {
226     struct in_addr addr;
227     struct in6_addr addr6;
228   } a;
229   /* This function takes a dot delimited string as input and
230    * reverses the parts split on dot.
231    */
232   if (dns_pton(AF_INET, ip, &a.addr) > 0) {
233     dns_a4todn(&a.addr, 0, dn, sizeof(dn));
234     dns_dntop(dn,buff,len);
235     return strlen(buff);
236   }
237   else if (dns_pton(AF_INET6, ip, &a.addr6) > 0) {
238     dns_a6todn(&a.addr6, 0, dn, sizeof(dn));
239     dns_dntop(dn,buff,len);
240     return strlen(buff);
241   }
242
243   o = buff;
244   il = strlen(ip);
245   if(len <= il) {
246     /* not enough room for ip and '\0' */
247     if(len > 0) buff[0] = '\0';
248     return 0;
249   }
250   e = ip + il;
251   b = e - 1;
252   while(b >= ip) {
253     const char *term;
254     while(b >= ip && *b != '.') b--;  /* Rewind to previous part */
255     term = b + 1; /* term is one ahead, we went past it */
256     if(term != e) memcpy(o, term, e - term); /* no sense in copying nothing */
257     o += e - term; /* advance the term length */
258     e = b;
259     b = e - 1;
260     if(e >= ip) *o++ = '.'; /* we must be at . */
261   }
262   *o = '\0';
263   assert((o - buff) == il);
264   return o - buff;
265 }
266 static int dns_interpolate_reverse_ip(char *buff, int len, const char *ip) {
267 #define IN4ADDRARPA_LEN 13 // strlen(".in-addr.arpa");
268 #define IN6ADDRARPA_LEN 9 // strlen(".ip6.arpa");
269   dns_interpolate_inaddr_arpa(buff,len,ip);
270   if(len > IN4ADDRARPA_LEN &&
271      !strcmp(buff+len-IN4ADDRARPA_LEN, ".in-addr.arpa"))
272     buff[len-IN4ADDRARPA_LEN] = '\0';
273   else if((len > IN6ADDRARPA_LEN) &&
274           !strcmp(buff+len-IN6ADDRARPA_LEN, ".ip6.arpa"))
275     buff[len-IN6ADDRARPA_LEN] = '\0';
276   return strlen(buff);
277 }
278
279 static int dns_module_init(noit_module_t *self) {
280   const struct dns_nameval *nv;
281   struct dns_ctx *pctx;
282   int i;
283   pthread_mutex_init(&dns_ctx_store_lock, NULL);
284   pthread_mutex_init(&active_events_lock, NULL);
285   /* HASH the rr types */
286   for(i=0, nv = dns_type_index(i); nv->name; nv = dns_type_index(++i))
287     noit_hash_store(&dns_rtypes,
288                     nv->name, strlen(nv->name),
289                     (void *)nv);
290   /* HASH the class types */
291   for(i=0, nv = dns_class_index(i); nv->name; nv = dns_class_index(++i))
292     noit_hash_store(&dns_ctypes,
293                     nv->name, strlen(nv->name),
294                     (void *)nv);
295
296   noit_check_interpolate_register_oper_fn("inaddrarpa",
297                                           dns_interpolate_inaddr_arpa);
298   noit_check_interpolate_register_oper_fn("reverseip",
299                                           dns_interpolate_reverse_ip);
300
301   if (dns_init(NULL, 0) < 0 || (pctx = dns_new(NULL)) == NULL) {
302     noitL(nlerr, "Unable to initialize dns subsystem\n");
303     return -1;
304   }
305   dns_free(pctx);
306   if(dns_ctx_alloc(NULL) == NULL) {
307     noitL(nlerr, "Error setting up default dns resolver context.\n");
308     return -1;
309   }
310   return 0;
311 }
312
313 static void dns_check_cleanup(noit_module_t *self, noit_check_t *check) {
314 }
315
316 static int dns_eventer_callback(eventer_t e, int mask, void *closure,
317                                 struct timeval *now) {
318   dns_ctx_handle_t *h = closure;
319   dns_ioevent(h->ctx, now->tv_sec);
320   return EVENTER_READ | EVENTER_EXCEPTION;
321 }
322
323 static int dns_check_timeout(eventer_t e, int mask, void *closure,
324                              struct timeval *now) {
325   struct dns_check_info *ci;
326   ci = closure;
327   ci->timeout_event = NULL;
328   ci->check->flags &= ~NP_RUNNING;
329   dns_check_log_results(ci);
330   __deactivate_ci(ci);
331   return 0;
332 }
333
334 static int dns_invoke_timeouts(eventer_t e, int mask, void *closure,
335                                struct timeval *now) {
336   dns_ctx_handle_t *h = closure;
337   dns_timeouts(h->ctx, 0, now->tv_sec);
338   return 0;
339 }
340 static void eventer_dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data) {
341   dns_ctx_handle_t *h = data;
342   eventer_t e = NULL, newe = NULL;
343   if(ctx == NULL) e = eventer_remove(h->timeout);
344   else {
345     assert(h->ctx == ctx);
346     if(timeout < 0) e = eventer_remove(h->timeout);
347     else {
348       newe = eventer_alloc();
349       newe->mask = EVENTER_TIMER;
350       newe->callback = dns_invoke_timeouts;
351       newe->closure = h;
352       gettimeofday(&newe->whence, NULL);
353       newe->whence.tv_sec += timeout;
354     }
355   }
356   if(e) eventer_free(e);
357   if(newe) eventer_add(newe);
358   h->timeout = newe;
359 }
360
361 static char *encode_txt(char *dst, const unsigned char *src, int len) {
362   int i;
363   for(i=0; i<len; i++) {
364     if(src[i] >= 127 || src[i] <= 31) {
365       snprintf(dst, 4, "\\%02x", src[i]);
366       dst += 3;
367     }
368     else if(src[i] == '\\') {
369       *dst++ = '\\';
370       *dst++ = '\\';
371     }
372     else {
373       *dst++ = (char)src[i];
374     }
375   }
376   *dst = '\0';
377   return dst;
378 }
379
380 static void decode_rr(struct dns_check_info *ci, struct dns_parse *p,
381                       struct dns_rr *rr, char **output) {
382   char buff[DNS_MAXDN], *txt_str, *c;
383   u_int32_t ttl, vu;
384   int32_t vs;
385   int totalsize;
386   const unsigned char *pkt = p->dnsp_pkt;
387   const unsigned char *end = p->dnsp_end;
388   const unsigned char *dptr = rr->dnsrr_dptr;
389   const unsigned char *dend = rr->dnsrr_dend;
390   unsigned char *dn = rr->dnsrr_dn;
391   const unsigned char *tmp;
392
393   /* Not interested unless it is the answer to my exact question */
394   if (!dns_dnequal(ci->dn, dn)) return;
395
396   if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) {
397     /* We don't handle EDNS0 OPT records */
398     goto decode_err;
399   }
400   noitL(nldeb, "%s. %u %s %s\n", dns_dntosp(dn), rr->dnsrr_ttl,
401         dns_classname(rr->dnsrr_cls),
402         dns_typename(rr->dnsrr_typ));
403
404   ttl = rr->dnsrr_ttl;
405   noit_stats_set_metric(&ci->current, "ttl", METRIC_UINT32, &ttl);
406
407   switch(rr->dnsrr_typ) {
408    case DNS_T_A:
409     if (rr->dnsrr_dsz != 4) goto decode_err;
410     snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
411              dptr[0], dptr[1], dptr[2], dptr[3]);
412     break;
413
414    case DNS_T_AAAA:
415     if (rr->dnsrr_dsz != 16) goto decode_err;
416     inet_ntop(AF_INET6, dptr, buff, sizeof(buff));
417     break;
418
419    case DNS_T_TXT:
420     totalsize = 0;
421     for(tmp = dptr; tmp < dend; totalsize += *tmp, tmp += *tmp + 1)
422       if(tmp + *tmp + 1 > dend) goto decode_err;
423     /* worst case: every character escaped + '\0' */
424     txt_str = alloca(totalsize * 3 + 1);
425     if(!txt_str) goto decode_err;
426     c = txt_str;
427     for(tmp = dptr; tmp < dend; tmp += *tmp + 1)
428       c = encode_txt(c, tmp+1, *tmp);
429     break;
430
431    case DNS_T_MX:
432     snprintf(buff, sizeof(buff), "%d ", dns_get16(dptr));
433     tmp = dptr + 2;
434     if(dns_getdn(pkt, &tmp, end, dn, DNS_MAXDN) <= 0 || tmp != dend)
435       goto decode_err;
436     dns_dntop(dn, buff + strlen(buff), sizeof(buff) - strlen(buff));
437     break;
438
439    case DNS_T_SOA:
440      if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err;
441      dns_dntop(dn, buff, sizeof(buff));
442      noit_stats_set_metric(&ci->current, "name-server", METRIC_STRING, buff);
443      if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err;
444      dns_dntop(dn, buff, sizeof(buff));
445      noit_stats_set_metric(&ci->current, "email-addr", METRIC_STRING, buff);
446      if(dptr + 5 * sizeof(u_int32_t) != dend) goto decode_err;
447      vu = dns_get32(dptr); dptr += sizeof(u_int32_t);
448      noit_stats_set_metric(&ci->current, "serial", METRIC_UINT32, &vu);
449      /* the serial is what we elect to store as the "answer" as text...
450       * because it rarely changes and that seems the most interesting thing
451       * to track change-log-style.
452       */
453      snprintf(buff, sizeof(buff), "%u", vu);
454      vs = dns_get32(dptr); dptr += sizeof(int32_t);
455      noit_stats_set_metric(&ci->current, "refresh", METRIC_UINT32, &vs);
456      vs = dns_get32(dptr); dptr += sizeof(int32_t);
457      noit_stats_set_metric(&ci->current, "retry", METRIC_UINT32, &vs);
458      vs = dns_get32(dptr); dptr += sizeof(int32_t);
459      noit_stats_set_metric(&ci->current, "expiry", METRIC_UINT32, &vs);
460      vs = dns_get32(dptr); dptr += sizeof(int32_t);
461      noit_stats_set_metric(&ci->current, "minimum", METRIC_UINT32, &vs);
462      break;
463
464    case DNS_T_CNAME:
465    case DNS_T_PTR:
466    case DNS_T_NS:
467    case DNS_T_MB:
468    case DNS_T_MD:
469    case DNS_T_MF:
470    case DNS_T_MG:
471    case DNS_T_MR:
472     if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err;
473     dns_dntop(dn, buff, sizeof(buff));
474     break;
475
476    default:
477     break;
478   }
479   if(*output) {
480     int newlen = strlen(*output) + strlen(", ") + strlen(buff) + 1;
481     char *newstr;
482     newstr = malloc(newlen);
483     snprintf(newstr, newlen, "%s, %s", *output, buff);
484     free(*output);
485     *output = newstr;
486   }
487   else
488     *output = strdup(buff);
489   ci->nrr++;
490   return;
491
492  decode_err:
493   ci->error = strdup("RR decode error");
494   return;
495 }
496
497 static void dns_cb(struct dns_ctx *ctx, void *result, void *data) {
498   int r = dns_status(ctx);
499   int len, i;
500   struct dns_check_info *ci = data;
501   struct dns_parse p;
502   struct dns_rr rr;
503   unsigned nrr;
504   unsigned char dn[DNS_MAXDN];
505   const unsigned char *pkt, *cur, *end;
506   char *result_str[MAX_RR] = { NULL };
507   char *result_combined = NULL;
508
509   /* If out ci isn't active, we must have timed out already */
510   if(!__isactive_ci(ci)) {
511     if(result) free(result);
512     return;
513   }
514
515   ci->timed_out = 0;
516   /* If we don't have a result, explode */
517   if (!result) {
518     ci->error = strdup(dns_strerror(r));
519     goto cleanup;
520   }
521
522   /* Process the packet */
523   pkt = result; end = pkt + r; cur = dns_payload(pkt);
524   dns_getdn(pkt, &cur, end, dn, sizeof(dn));
525   dns_initparse(&p, NULL, pkt, cur, end);
526   p.dnsp_qcls = 0;
527   p.dnsp_qtyp = 0;
528   nrr = 0;
529
530   while((r = dns_nextrr(&p, &rr)) > 0) {
531     if (!dns_dnequal(dn, rr.dnsrr_dn)) continue;
532     if ((ci->query_ctype == DNS_C_ANY || ci->query_ctype == rr.dnsrr_cls) &&
533         (ci->query_rtype == DNS_T_ANY || ci->query_rtype == rr.dnsrr_typ))
534       ++nrr;
535     else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
536       if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
537                     p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
538           rr.dnsrr_dptr != rr.dnsrr_dend) {
539         ci->error = strdup("protocol error");
540         break;
541       }
542       else {
543         int32_t on = 1;
544         /* This actually updates what we're looking for */
545         dns_dntodn(p.dnsp_dnbuf, ci->dn, sizeof(dn));
546         noit_stats_set_metric(&ci->current, "cname", METRIC_INT32, &on);
547
548         /* Now follow the leader */
549         noitL(nldeb, "%s. CNAME %s.\n", dns_dntosp(dn),
550               dns_dntosp(p.dnsp_dnbuf));
551         dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn));
552         noitL(nldeb, " ---> '%s'\n", dns_dntosp(dn));
553       }
554     }
555   }
556   if (!r && !nrr) {
557     ci->error = strdup("no data");
558   }
559
560   dns_rewind(&p, NULL);
561   p.dnsp_qtyp = ci->query_rtype == DNS_T_ANY ? 0 : ci->query_rtype;
562   p.dnsp_qcls = ci->query_ctype == DNS_C_ANY ? 0 : ci->query_ctype;
563   while(dns_nextrr(&p, &rr) && ci->nrr < MAX_RR)
564     decode_rr(ci, &p, &rr, &result_str[ci->nrr]);
565   if(ci->sort)
566     qsort(result_str, ci->nrr, sizeof(*result_str), cstring_cmp);
567   /* calculate the length and allocate on the stack */
568   len = 0;
569   for(i=0; i<ci->nrr; i++) len += strlen(result_str[i]) + 2;
570   result_combined = alloca(len);
571   result_combined[0] = '\0';
572   /* string it together */
573   len = 0;
574   for(i=0; i<ci->nrr; i++) {
575     int slen;
576     if(i) { memcpy(result_combined + len, ", ", 2); len += 2; }
577     slen = strlen(result_str[i]);
578     memcpy(result_combined + len, result_str[i], slen);
579     len += slen;
580     result_combined[len] = '\0';
581     free(result_str[i]); /* free as we go */
582   }
583   noit_stats_set_metric(&ci->current, "answer", METRIC_STRING, result_combined);
584
585  cleanup:
586   if(result) free(result);
587   if(ci->timeout_event) {
588     eventer_t e = eventer_remove(ci->timeout_event);
589     ci->timeout_event = NULL;
590     if(e) eventer_free(e);
591   }
592   ci->check->flags &= ~NP_RUNNING;
593   dns_check_log_results(ci);
594   __deactivate_ci(ci);
595 }
596
597 static int dns_check_send(noit_module_t *self, noit_check_t *check) {
598   void *vnv_pair = NULL;
599   struct dns_nameval *nv_pair;
600   eventer_t newe;
601   struct timeval p_int, now;
602   struct dns_check_info *ci = check->closure;
603   const char *config_val;
604   const char *rtype = NULL;
605   const char *nameserver = NULL;
606   const char *want_sort = NULL;
607   const char *ctype = "IN";
608   const char *query = NULL;
609   char interpolated_nameserver[1024];
610   char interpolated_query[1024];
611   noit_hash_table check_attrs_hash = NOIT_HASH_EMPTY;
612
613   gettimeofday(&now, NULL);
614   memcpy(&check->last_fire_time, &now, sizeof(now));
615   ci->current.state = NP_BAD;
616   ci->current.available = NP_UNAVAILABLE;
617   ci->timed_out = 1;
618   ci->nrr = 0;
619   ci->sort = 1;
620
621   if(!strcmp(check->name, "in-addr.arpa") ||
622      (strlen(check->name) >= sizeof("::in-addr.arpa") - 1 &&
623       !strcmp(check->name + strlen(check->name) - sizeof("::in-addr.arpa") + 1,
624               "::in-addr.arpa"))) {
625     /* in-addr.arpa defaults:
626      *   nameserver to NULL
627      *   rtype to PTR
628      *   query to %[:inaddrarpa:target]
629      */
630     nameserver = NULL;
631     rtype = "PTR";
632     query = "%[:inaddrarpa:target_ip]";
633   }
634   else {
635     nameserver = "%[target_ip]";
636     rtype = "A";
637     query = "%[name]";
638   }
639 #define CONFIG_OVERRIDE(a) \
640   if(noit_hash_retr_str(check->config, #a, strlen(#a), \
641                         &config_val) && \
642      strlen(config_val) > 0) \
643     a = config_val
644   CONFIG_OVERRIDE(ctype);
645   CONFIG_OVERRIDE(nameserver);
646   CONFIG_OVERRIDE(rtype);
647   CONFIG_OVERRIDE(query);
648   CONFIG_OVERRIDE(want_sort);
649   if(nameserver && !strcmp(nameserver, "default")) nameserver = NULL;
650   if(want_sort && strcasecmp(want_sort, "on") && strcasecmp(want_sort, "true"))
651     ci->sort = 0;
652
653   noit_check_make_attrs(check, &check_attrs_hash);
654
655   if(nameserver) {
656     noit_check_interpolate(interpolated_nameserver,
657                            sizeof(interpolated_nameserver),
658                            nameserver,
659                            &check_attrs_hash, check->config);
660     nameserver = interpolated_nameserver;
661   }
662   if(query) {
663     noit_check_interpolate(interpolated_query,
664                            sizeof(interpolated_query),
665                            query,
666                            &check_attrs_hash, check->config);
667     query = interpolated_query;
668   }
669   noit_hash_destroy(&check_attrs_hash, NULL, NULL);
670
671   check->flags |= NP_RUNNING;
672   noitL(nldeb, "dns_check_send(%p,%s,%s,%s,%s,%s)\n",
673         self, check->target, nameserver ? nameserver : "default",
674         query ? query : "null", ctype, rtype);
675
676   __activate_ci(ci);
677   /* If this ci has a handle and it isn't the one we need,
678    * we should release it
679    */
680   if(ci->h &&
681      ((ci->h->ns == NULL && nameserver != NULL) ||
682       (ci->h->ns != NULL && nameserver == NULL) ||
683       (ci->h->ns && strcmp(ci->h->ns, nameserver)))) {
684     dns_ctx_release(ci->h);
685     ci->h = NULL;
686   }
687   /* use the cached one, unless we don't have one */
688   if(!ci->h) ci->h = dns_ctx_alloc(nameserver);
689   if(!ci->h) ci->error = strdup("bad nameserver");
690
691   /* Lookup out class */
692   if(!noit_hash_retrieve(&dns_ctypes, ctype, strlen(ctype),
693                          &vnv_pair)) {
694     if(ci->error) free(ci->error);
695     ci->error = strdup("bad class");
696   }
697   else {
698     nv_pair = (struct dns_nameval *)vnv_pair;
699     ci->query_ctype = nv_pair->val;
700   }
701   /* Lookup out rr type */
702   if(!noit_hash_retrieve(&dns_rtypes, rtype, strlen(rtype),
703                          &vnv_pair)) {
704     if(ci->error) free(ci->error);
705     ci->error = strdup("bad rr type");
706   }
707   else {
708     nv_pair = (struct dns_nameval *)vnv_pair;
709     ci->query_rtype = nv_pair->val;
710   }
711
712   if(!ci->error) {
713     /* Submit the query */
714     int abs;
715     if(!dns_ptodn(query, strlen(query), ci->dn, sizeof(ci->dn), &abs) ||
716        !dns_submit_dn(ci->h->ctx, ci->dn, ci->query_ctype, ci->query_rtype,
717                       abs | DNS_NOSRCH, NULL, dns_cb, ci)) {
718       ci->error = strdup("submission error");
719     }
720     else {
721       dns_timeouts(ci->h->ctx, -1, now.tv_sec);
722     }
723   }
724
725   /* we could have completed by now... if so, we've nothing to do */
726
727   if(!__isactive_ci(ci)) return 0;
728
729   if(ci->error) {
730     /* Errors here are easy, fail and avoid scheduling a timeout */
731     ci->check->flags &= ~NP_RUNNING;
732     dns_check_log_results(ci);
733     __deactivate_ci(ci);
734     return 0;
735   }
736
737   newe = eventer_alloc();
738   newe->mask = EVENTER_TIMER;
739   gettimeofday(&now, NULL);
740   p_int.tv_sec = check->timeout / 1000;
741   p_int.tv_usec = (check->timeout % 1000) * 1000;
742   add_timeval(now, p_int, &newe->whence);
743   newe->closure = ci;
744   newe->callback = dns_check_timeout;
745   ci->timeout_event = newe;
746   eventer_add(newe);
747
748   return 0;
749 }
750
751 static int dns_initiate_check(noit_module_t *self, noit_check_t *check,
752                               int once, noit_check_t *cause) {
753   struct dns_check_info *ci;
754   if(!check->closure)
755     check->closure = calloc(1, sizeof(struct dns_check_info));
756   ci = check->closure;
757   ci->check = check;
758   ci->self = self;
759   INITIATE_CHECK(dns_check_send, self, check);
760   return 0;
761 }
762
763 static int dns_config(noit_module_t *self, noit_hash_table *options) {
764   return 0;
765 }
766
767 static int dns_onload(noit_image_t *self) {
768   nlerr = noit_log_stream_find("error/dns");
769   nldeb = noit_log_stream_find("debug/dns");
770   if(!nlerr) nlerr = noit_stderr;
771   if(!nldeb) nldeb = noit_debug;
772   eventer_name_callback("dns/dns_eventer_callback", dns_eventer_callback);
773   eventer_name_callback("dns/dns_check_timeout", dns_check_timeout);
774   eventer_name_callback("dns/dns_invoke_timeouts", dns_invoke_timeouts);
775   return 0;
776 }
777
778 #include "dns.xmlh"
779 noit_module_t dns = {
780   {
781     NOIT_MODULE_MAGIC,
782     NOIT_MODULE_ABI_VERSION,
783     "dns",
784     "DNS RR checker",
785     dns_xml_description,
786     dns_onload
787   },
788   dns_config,
789   dns_module_init,
790   dns_initiate_check,
791   dns_check_cleanup
792 };
793
Note: See TracBrowser for help on using the browser.