root/src/modules/dns.c

Revision 16e8c85a9d0e6b2e2bf74a7d159f6352253c50a9, 22.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

fixes #315

  • 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 = buff;
223   int il;
224   /* This function takes a dot delimited string as input and
225    * reverses the parts split on dot.
226    */
227   il = strlen(ip);
228   if(len <= il) {
229     /* not enough room for ip and '\0' */
230     if(len > 0) buff[0] = '\0';
231     return 0;
232   }
233   e = ip + il;
234   b = e - 1;
235   while(b >= ip) {
236     const char *term;
237     while(b >= ip && *b != '.') b--;  /* Rewind to previous part */
238     term = b + 1; /* term is one ahead, we went past it */
239     if(term != e) memcpy(o, term, e - term); /* no sense in copying nothing */
240     o += e - term; /* advance the term length */
241     e = b;
242     b = e - 1;
243     if(e >= ip) *o++ = '.'; /* we must be at . */
244   }
245   *o = '\0';
246   assert((o - buff) == il);
247   return o - buff;
248 }
249
250 static int dns_module_init(noit_module_t *self) {
251   const struct dns_nameval *nv;
252   struct dns_ctx *pctx;
253   int i;
254   pthread_mutex_init(&dns_ctx_store_lock, NULL);
255   pthread_mutex_init(&active_events_lock, NULL);
256   /* HASH the rr types */
257   for(i=0, nv = dns_type_index(i); nv->name; nv = dns_type_index(++i))
258     noit_hash_store(&dns_rtypes,
259                     nv->name, strlen(nv->name),
260                     (void *)nv);
261   /* HASH the class types */
262   for(i=0, nv = dns_class_index(i); nv->name; nv = dns_class_index(++i))
263     noit_hash_store(&dns_ctypes,
264                     nv->name, strlen(nv->name),
265                     (void *)nv);
266
267   noit_check_interpolate_register_oper_fn("inaddrarpa",
268                                           dns_interpolate_inaddr_arpa);
269
270   if (dns_init(NULL, 0) < 0 || (pctx = dns_new(NULL)) == NULL) {
271     noitL(nlerr, "Unable to initialize dns subsystem\n");
272     return -1;
273   }
274   dns_free(pctx);
275   if(dns_ctx_alloc(NULL) == NULL) {
276     noitL(nlerr, "Error setting up default dns resolver context.\n");
277     return -1;
278   }
279   return 0;
280 }
281
282 static void dns_check_cleanup(noit_module_t *self, noit_check_t *check) {
283 }
284
285 static int dns_eventer_callback(eventer_t e, int mask, void *closure,
286                                 struct timeval *now) {
287   dns_ctx_handle_t *h = closure;
288   dns_ioevent(h->ctx, now->tv_sec);
289   return EVENTER_READ | EVENTER_EXCEPTION;
290 }
291
292 static int dns_check_timeout(eventer_t e, int mask, void *closure,
293                              struct timeval *now) {
294   struct dns_check_info *ci;
295   ci = closure;
296   ci->timeout_event = NULL;
297   ci->check->flags &= ~NP_RUNNING;
298   dns_check_log_results(ci);
299   __deactivate_ci(ci);
300   return 0;
301 }
302
303 static int dns_invoke_timeouts(eventer_t e, int mask, void *closure,
304                                struct timeval *now) {
305   dns_ctx_handle_t *h = closure;
306   dns_timeouts(h->ctx, 0, now->tv_sec);
307   return 0;
308 }
309 static void eventer_dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data) {
310   dns_ctx_handle_t *h = data;
311   eventer_t e = NULL, newe = NULL;
312   if(ctx == NULL) e = eventer_remove(h->timeout);
313   else {
314     assert(h->ctx == ctx);
315     if(timeout < 0) e = eventer_remove(h->timeout);
316     else {
317       newe = eventer_alloc();
318       newe->mask = EVENTER_TIMER;
319       newe->callback = dns_invoke_timeouts;
320       newe->closure = h;
321       gettimeofday(&newe->whence, NULL);
322       newe->whence.tv_sec += timeout;
323     }
324   }
325   if(e) eventer_free(e);
326   if(newe) eventer_add(newe);
327   h->timeout = newe;
328 }
329
330 static char *encode_txt(char *dst, const unsigned char *src, int len) {
331   int i;
332   for(i=0; i<len; i++) {
333     if(src[i] >= 127 || src[i] <= 31) {
334       snprintf(dst, 4, "\\%02x", src[i]);
335       dst += 3;
336     }
337     else if(src[i] == '\\') {
338       *dst++ = '\\';
339       *dst++ = '\\';
340     }
341     else {
342       *dst++ = (char)src[i];
343     }
344   }
345   *dst = '\0';
346   return dst;
347 }
348
349 static void decode_rr(struct dns_check_info *ci, struct dns_parse *p,
350                       struct dns_rr *rr, char **output) {
351   char buff[DNS_MAXDN], *txt_str, *c;
352   u_int32_t ttl, vu;
353   int32_t vs;
354   int totalsize;
355   const unsigned char *pkt = p->dnsp_pkt;
356   const unsigned char *end = p->dnsp_end;
357   const unsigned char *dptr = rr->dnsrr_dptr;
358   const unsigned char *dend = rr->dnsrr_dend;
359   unsigned char *dn = rr->dnsrr_dn;
360   const unsigned char *tmp;
361
362   /* Not interested unless it is the answer to my exact question */
363   if (!dns_dnequal(ci->dn, dn)) return;
364
365   if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) {
366     /* We don't handle EDNS0 OPT records */
367     goto decode_err;
368   }
369   noitL(nldeb, "%s. %u %s %s\n", dns_dntosp(dn), rr->dnsrr_ttl,
370         dns_classname(rr->dnsrr_cls),
371         dns_typename(rr->dnsrr_typ));
372
373   ttl = rr->dnsrr_ttl;
374   noit_stats_set_metric(&ci->current, "ttl", METRIC_UINT32, &ttl);
375
376   switch(rr->dnsrr_typ) {
377    case DNS_T_A:
378     if (rr->dnsrr_dsz != 4) goto decode_err;
379     snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
380              dptr[0], dptr[1], dptr[2], dptr[3]);
381     break;
382
383    case DNS_T_AAAA:
384     if (rr->dnsrr_dsz != 16) goto decode_err;
385     inet_ntop(AF_INET6, dptr, buff, 16);
386     break;
387
388    case DNS_T_TXT:
389     totalsize = 0;
390     for(tmp = dptr; tmp < dend; totalsize += *tmp, tmp += *tmp + 1)
391       if(tmp + *tmp + 1 > dend) goto decode_err;
392     /* worst case: every character escaped + '\0' */
393     txt_str = alloca(totalsize * 3 + 1);
394     if(!txt_str) goto decode_err;
395     c = txt_str;
396     for(tmp = dptr; tmp < dend; tmp += *tmp + 1)
397       c = encode_txt(c, tmp+1, *tmp);
398     break;
399
400    case DNS_T_MX:
401     snprintf(buff, sizeof(buff), "%d ", dns_get16(dptr));
402     tmp = dptr + 2;
403     if(dns_getdn(pkt, &tmp, end, dn, DNS_MAXDN) <= 0 || tmp != dend)
404       goto decode_err;
405     dns_dntop(dn, buff + strlen(buff), sizeof(buff) - strlen(buff));
406     break;
407
408    case DNS_T_SOA:
409      if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err;
410      dns_dntop(dn, buff, sizeof(buff));
411      noit_stats_set_metric(&ci->current, "name-server", METRIC_STRING, buff);
412      if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err;
413      dns_dntop(dn, buff, sizeof(buff));
414      noit_stats_set_metric(&ci->current, "email-addr", METRIC_STRING, buff);
415      if(dptr + 5 * sizeof(u_int32_t) != dend) goto decode_err;
416      vu = dns_get32(dptr); dptr += sizeof(u_int32_t);
417      noit_stats_set_metric(&ci->current, "serial", METRIC_UINT32, &vu);
418      /* the serial is what we elect to store as the "answer" as text...
419       * because it rarely changes and that seems the most interesting thing
420       * to track change-log-style.
421       */
422      snprintf(buff, sizeof(buff), "%u", vu);
423      vs = dns_get32(dptr); dptr += sizeof(int32_t);
424      noit_stats_set_metric(&ci->current, "refresh", METRIC_UINT32, &vs);
425      vs = dns_get32(dptr); dptr += sizeof(int32_t);
426      noit_stats_set_metric(&ci->current, "retry", METRIC_UINT32, &vs);
427      vs = dns_get32(dptr); dptr += sizeof(int32_t);
428      noit_stats_set_metric(&ci->current, "expiry", METRIC_UINT32, &vs);
429      vs = dns_get32(dptr); dptr += sizeof(int32_t);
430      noit_stats_set_metric(&ci->current, "minimum", METRIC_UINT32, &vs);
431      break;
432
433    case DNS_T_CNAME:
434    case DNS_T_PTR:
435    case DNS_T_NS:
436    case DNS_T_MB:
437    case DNS_T_MD:
438    case DNS_T_MF:
439    case DNS_T_MG:
440    case DNS_T_MR:
441     if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err;
442     dns_dntop(dn, buff, sizeof(buff));
443     break;
444
445    default:
446     break;
447   }
448   if(*output) {
449     int newlen = strlen(*output) + strlen(", ") + strlen(buff) + 1;
450     char *newstr;
451     newstr = malloc(newlen);
452     snprintf(newstr, newlen, "%s, %s", *output, buff);
453     free(*output);
454     *output = newstr;
455   }
456   else
457     *output = strdup(buff);
458   ci->nrr++;
459   return;
460
461  decode_err:
462   ci->error = strdup("RR decode error");
463   return;
464 }
465
466 static void dns_cb(struct dns_ctx *ctx, void *result, void *data) {
467   int r = dns_status(ctx);
468   int len, i;
469   struct dns_check_info *ci = data;
470   struct dns_parse p;
471   struct dns_rr rr;
472   unsigned nrr;
473   unsigned char dn[DNS_MAXDN];
474   const unsigned char *pkt, *cur, *end;
475   char *result_str[MAX_RR] = { NULL };
476   char *result_combined = NULL;
477
478   /* If out ci isn't active, we must have timed out already */
479   if(!__isactive_ci(ci)) {
480     if(result) free(result);
481     return;
482   }
483
484   ci->timed_out = 0;
485   /* If we don't have a result, explode */
486   if (!result) {
487     ci->error = strdup(dns_strerror(r));
488     goto cleanup;
489   }
490
491   /* Process the packet */
492   pkt = result; end = pkt + r; cur = dns_payload(pkt);
493   dns_getdn(pkt, &cur, end, dn, sizeof(dn));
494   dns_initparse(&p, NULL, pkt, cur, end);
495   p.dnsp_qcls = 0;
496   p.dnsp_qtyp = 0;
497   nrr = 0;
498
499   while((r = dns_nextrr(&p, &rr)) > 0) {
500     if (!dns_dnequal(dn, rr.dnsrr_dn)) continue;
501     if ((ci->query_ctype == DNS_C_ANY || ci->query_ctype == rr.dnsrr_cls) &&
502         (ci->query_rtype == DNS_T_ANY || ci->query_rtype == rr.dnsrr_typ))
503       ++nrr;
504     else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
505       if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
506                     p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
507           rr.dnsrr_dptr != rr.dnsrr_dend) {
508         ci->error = strdup("protocol error");
509         break;
510       }
511       else {
512         noitL(nldeb, "%s.\n", dns_dntosp(dn));
513         noitL(nldeb, " CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf));
514         dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn));
515         noitL(nldeb, " ---> '%s'\n", dn);
516       }
517     }
518   }
519   if (!r && !nrr) {
520     ci->error = strdup("no data");
521   }
522
523   dns_rewind(&p, NULL);
524   p.dnsp_qtyp = ci->query_rtype == DNS_T_ANY ? 0 : ci->query_rtype;
525   p.dnsp_qcls = ci->query_ctype == DNS_C_ANY ? 0 : ci->query_ctype;
526   while(dns_nextrr(&p, &rr) && ci->nrr < MAX_RR)
527     decode_rr(ci, &p, &rr, &result_str[ci->nrr]);
528   if(ci->sort)
529     qsort(result_str, ci->nrr, sizeof(*result_str), cstring_cmp);
530   /* calculate the length and allocate on the stack */
531   len = 0;
532   for(i=0; i<ci->nrr; i++) len += strlen(result_str[i]) + 2;
533   result_combined = alloca(len);
534   /* string it together */
535   len = 0;
536   for(i=0; i<ci->nrr; i++) {
537     int slen;
538     if(i) { memcpy(result_combined + len, ", ", 2); len += 2; }
539     slen = strlen(result_str[i]);
540     memcpy(result_combined + len, result_str[i], slen);
541     len += slen;
542     result_combined[len] = '\0';
543     free(result_str[i]); /* free as we go */
544   }
545   noit_stats_set_metric(&ci->current, "answer", METRIC_STRING, result_combined);
546
547  cleanup:
548   if(result) free(result);
549   if(ci->timeout_event) {
550     eventer_t e = eventer_remove(ci->timeout_event);
551     ci->timeout_event = NULL;
552     if(e) eventer_free(e);
553   }
554   ci->check->flags &= ~NP_RUNNING;
555   dns_check_log_results(ci);
556   __deactivate_ci(ci);
557 }
558
559 static int dns_check_send(noit_module_t *self, noit_check_t *check) {
560   void *vnv_pair = NULL;
561   struct dns_nameval *nv_pair;
562   eventer_t newe;
563   struct timeval p_int, now;
564   struct dns_check_info *ci = check->closure;
565   const char *config_val;
566   const char *rtype = NULL;
567   const char *nameserver = NULL;
568   const char *want_sort = NULL;
569   const char *ctype = "IN";
570   const char *query = NULL;
571   char interpolated_nameserver[1024];
572   char interpolated_query[1024];
573   noit_hash_table check_attrs_hash = NOIT_HASH_EMPTY;
574
575   gettimeofday(&now, NULL);
576   memcpy(&check->last_fire_time, &now, sizeof(now));
577   ci->current.state = NP_BAD;
578   ci->current.available = NP_UNAVAILABLE;
579   ci->timed_out = 1;
580   ci->nrr = 0;
581   ci->sort = 1;
582
583   if(!strcmp(check->name, "in-addr.arpa") ||
584      (strlen(check->name) >= sizeof("::in-addr.arpa") - 1 &&
585       !strcmp(check->name + strlen(check->name) - sizeof("::in-addr.arpa") + 1,
586               "::in-addr.arpa"))) {
587     /* in-addr.arpa defaults:
588      *   nameserver to NULL
589      *   rtype to PTR
590      *   query to %[:inaddrarpa:target].in-addr.arpa
591      */
592     nameserver = NULL;
593     rtype = "PTR";
594     query = "%[:inaddrarpa:target].in-addr.arpa";
595   }
596   else {
597     nameserver = "%[target]";
598     rtype = "A";
599     query = "%[name]";
600   }
601 #define CONFIG_OVERRIDE(a) \
602   if(noit_hash_retr_str(check->config, #a, strlen(#a), \
603                         &config_val) && \
604      strlen(config_val) > 0) \
605     a = config_val
606   CONFIG_OVERRIDE(ctype);
607   CONFIG_OVERRIDE(nameserver);
608   CONFIG_OVERRIDE(rtype);
609   CONFIG_OVERRIDE(query);
610   CONFIG_OVERRIDE(want_sort);
611   if(want_sort && strcasecmp(want_sort, "on") && strcasecmp(want_sort, "true"))
612     ci->sort = 0;
613
614   noit_check_make_attrs(check, &check_attrs_hash);
615
616   if(nameserver) {
617     noit_check_interpolate(interpolated_nameserver,
618                            sizeof(interpolated_nameserver),
619                            nameserver,
620                            &check_attrs_hash, check->config);
621     nameserver = interpolated_nameserver;
622   }
623   if(query) {
624     noit_check_interpolate(interpolated_query,
625                            sizeof(interpolated_query),
626                            query,
627                            &check_attrs_hash, check->config);
628     query = interpolated_query;
629   }
630   noit_hash_destroy(&check_attrs_hash, NULL, NULL);
631
632   check->flags |= NP_RUNNING;
633   noitL(nldeb, "dns_check_send(%p,%s,%s,%s,%s,%s)\n",
634         self, check->target, nameserver ? nameserver : "default",
635         query ? query : "null", ctype, rtype);
636
637   __activate_ci(ci);
638   /* If this ci has a handle and it isn't the one we need,
639    * we should release it
640    */
641   if(ci->h &&
642      ((ci->h->ns == NULL && nameserver != NULL) ||
643       (ci->h->ns != NULL && nameserver == NULL) ||
644       (ci->h->ns && strcmp(ci->h->ns, nameserver)))) {
645     dns_ctx_release(ci->h);
646     ci->h = NULL;
647   }
648   /* use the cached one, unless we don't have one */
649   if(!ci->h) ci->h = dns_ctx_alloc(nameserver);
650
651   /* Lookup out class */
652   if(!noit_hash_retrieve(&dns_ctypes, ctype, strlen(ctype),
653                          &vnv_pair)) {
654     ci->error = strdup("bad class");
655   }
656   else {
657     nv_pair = (struct dns_nameval *)vnv_pair;
658     ci->query_ctype = nv_pair->val;
659   }
660   /* Lookup out rr type */
661   if(!noit_hash_retrieve(&dns_rtypes, rtype, strlen(rtype),
662                          &vnv_pair)) {
663     ci->error = strdup("bad rr type");
664   }
665   else {
666     nv_pair = (struct dns_nameval *)vnv_pair;
667     ci->query_rtype = nv_pair->val;
668   }
669
670   if(!ci->error) {
671     /* Submit the query */
672     int abs;
673     if(!dns_ptodn(query, strlen(query), ci->dn, sizeof(ci->dn), &abs) ||
674        !dns_submit_dn(ci->h->ctx, ci->dn, ci->query_ctype, ci->query_rtype,
675                       abs | DNS_NOSRCH, NULL, dns_cb, ci)) {
676       ci->error = strdup("submission error");
677     }
678     else {
679       dns_timeouts(ci->h->ctx, -1, now.tv_sec);
680     }
681   }
682
683   /* we could have completed by now... if so, we've nothing to do */
684
685   if(!__isactive_ci(ci)) return 0;
686
687   if(ci->error) {
688     /* Errors here are easy, fail and avoid scheduling a timeout */
689     ci->check->flags &= ~NP_RUNNING;
690     dns_check_log_results(ci);
691     __deactivate_ci(ci);
692     return 0;
693   }
694
695   newe = eventer_alloc();
696   newe->mask = EVENTER_TIMER;
697   gettimeofday(&now, NULL);
698   p_int.tv_sec = check->timeout / 1000;
699   p_int.tv_usec = (check->timeout % 1000) * 1000;
700   add_timeval(now, p_int, &newe->whence);
701   newe->closure = ci;
702   newe->callback = dns_check_timeout;
703   ci->timeout_event = newe;
704   eventer_add(newe);
705
706   return 0;
707 }
708
709 static int dns_initiate_check(noit_module_t *self, noit_check_t *check,
710                               int once, noit_check_t *cause) {
711   struct dns_check_info *ci;
712   if(!check->closure)
713     check->closure = calloc(1, sizeof(struct dns_check_info));
714   ci = check->closure;
715   ci->check = check;
716   ci->self = self;
717   INITIATE_CHECK(dns_check_send, self, check);
718   return 0;
719 }
720
721 static int dns_config(noit_module_t *self, noit_hash_table *options) {
722   return 0;
723 }
724
725 static int dns_onload(noit_image_t *self) {
726   nlerr = noit_log_stream_find("error/dns");
727   nldeb = noit_log_stream_find("debug/dns");
728   if(!nlerr) nlerr = noit_stderr;
729   if(!nldeb) nldeb = noit_debug;
730   eventer_name_callback("dns/dns_eventer_callback", dns_eventer_callback);
731   eventer_name_callback("dns/dns_check_timeout", dns_check_timeout);
732   eventer_name_callback("dns/dns_invoke_timeouts", dns_invoke_timeouts);
733   return 0;
734 }
735
736 #include "dns.xmlh"
737 noit_module_t dns = {
738   {
739     NOIT_MODULE_MAGIC,
740     NOIT_MODULE_ABI_VERSION,
741     "dns",
742     "DNS RR checker",
743     dns_xml_description,
744     dns_onload
745   },
746   dns_config,
747   dns_module_init,
748   dns_initiate_check,
749   dns_check_cleanup
750 };
751
Note: See TracBrowser for help on using the browser.