root/src/modules/dns.c

Revision b5631fad3b0879167cae847f495df915daff65f1, 24.7 kB (checked in by Dan Di Spaltro <dan@cloudkick.com>, 2 years ago)

Include rtt no matter if there is an error or not.

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