static int dns_module_check_timeout(eventer_t e, int mask, void *closure, struct timeval *now) { struct dns_check_info *ci; ci = closure; ci->timeout_event = NULL; dns_check_log_results(ci); __deactivate_ci(ci); return 0; }
static int dns_check_send(noit_module_t *self, noit_check_t *check, noit_check_t *cause) { void *vnv_pair = NULL; struct dns_nameval *nv_pair; eventer_t newe; struct timeval p_int, now; struct dns_check_info *ci = check->closure; const char *config_val; const char *rtype = NULL; const char *nameserver = NULL; int port = 0; const char *port_str = NULL; const char *want_sort = NULL; const char *ctype = "IN"; const char *query = NULL; char interpolated_nameserver[1024]; char interpolated_query[1024]; noit_hash_table check_attrs_hash = NOIT_HASH_EMPTY; BAIL_ON_RUNNING_CHECK(check); gettimeofday(&now, NULL); memcpy(&check->last_fire_time, &now, sizeof(now)); ci->current.state = NP_BAD; ci->current.available = NP_UNAVAILABLE; ci->timed_out = 1; ci->nrr = 0; ci->sort = 1; if(!strcmp(check->name, "in-addr.arpa") || (strlen(check->name) >= sizeof("::in-addr.arpa") - 1 && !strcmp(check->name + strlen(check->name) - sizeof("::in-addr.arpa") + 1, "::in-addr.arpa"))) { /* in-addr.arpa defaults: * nameserver to NULL * rtype to PTR * query to %[:inaddrarpa:target] */ nameserver = NULL; rtype = "PTR"; query = "%[:inaddrarpa:target_ip]"; } else { nameserver = "%[target_ip]"; rtype = "A"; query = "%[name]"; } if(noit_hash_retr_str(check->config, "port", strlen("port"), &port_str)) { port = atoi(port_str); } #define CONFIG_OVERRIDE(a) \ if(noit_hash_retr_str(check->config, #a, strlen(#a), \ &config_val) && \ strlen(config_val) > 0) \ a = config_val CONFIG_OVERRIDE(ctype); CONFIG_OVERRIDE(nameserver); CONFIG_OVERRIDE(rtype); CONFIG_OVERRIDE(query); CONFIG_OVERRIDE(want_sort); if(nameserver && !strcmp(nameserver, "default")) nameserver = NULL; if(want_sort && strcasecmp(want_sort, "on") && strcasecmp(want_sort, "true")) ci->sort = 0; noit_check_make_attrs(check, &check_attrs_hash); if(nameserver) { noit_check_interpolate(interpolated_nameserver, sizeof(interpolated_nameserver), nameserver, &check_attrs_hash, check->config); nameserver = interpolated_nameserver; } if(query) { noit_check_interpolate(interpolated_query, sizeof(interpolated_query), query, &check_attrs_hash, check->config); query = interpolated_query; } noit_hash_destroy(&check_attrs_hash, NULL, NULL); check->flags |= NP_RUNNING; noitL(nldeb, "dns_check_send(%p,%s,%s,%s,%s,%s)\n", self, check->target, nameserver ? nameserver : "default", query ? query : "null", ctype, rtype); __activate_ci(ci); /* If this ci has a handle and it isn't the one we need, * we should release it */ if(ci->h && ((ci->h->ns == NULL && nameserver != NULL) || (ci->h->ns != NULL && nameserver == NULL) || (ci->h->ns && strcmp(ci->h->ns, nameserver)))) { dns_ctx_release(ci->h); ci->h = NULL; } /* use the cached one, unless we don't have one */ if(!ci->h) ci->h = dns_ctx_alloc(nameserver, port); if(!ci->h) ci->error = strdup("bad nameserver"); /* Lookup out class */ if(!noit_hash_retrieve(&dns_ctypes, ctype, strlen(ctype), &vnv_pair)) { if(ci->error) free(ci->error); ci->error = strdup("bad class"); } else { nv_pair = (struct dns_nameval *)vnv_pair; ci->query_ctype = nv_pair->val; } /* Lookup out rr type */ if(!noit_hash_retrieve(&dns_rtypes, rtype, strlen(rtype), &vnv_pair)) { if(ci->error) free(ci->error); ci->error = strdup("bad rr type"); } else { nv_pair = (struct dns_nameval *)vnv_pair; ci->query_rtype = nv_pair->val; } if(!ci->error) { /* Submit the query */ int abs; if(!dns_ptodn(query, strlen(query), ci->dn, sizeof(ci->dn), &abs) || !dns_submit_dn(ci->h->ctx, ci->dn, ci->query_ctype, ci->query_rtype, abs | DNS_NOSRCH, NULL, dns_cb, ci)) { ci->error = strdup("submission error"); } else { dns_timeouts(ci->h->ctx, -1, now.tv_sec); } } /* we could have completed by now... if so, we've nothing to do */ if(!__isactive_ci(ci)) return 0; if(ci->error) { /* Errors here are easy, fail and avoid scheduling a timeout */ ci->check->flags &= ~NP_RUNNING; dns_check_log_results(ci); __deactivate_ci(ci); return 0; } newe = eventer_alloc(); newe->mask = EVENTER_TIMER; gettimeofday(&now, NULL); p_int.tv_sec = check->timeout / 1000; p_int.tv_usec = (check->timeout % 1000) * 1000; add_timeval(now, p_int, &newe->whence); newe->closure = ci; newe->callback = dns_check_timeout; ci->timeout_event = newe; eventer_add(newe); return 0; }
static void dns_cb(struct dns_ctx *ctx, void *result, void *data) { int r = dns_status(ctx); int len, i; struct dns_check_info *ci = data; struct dns_parse p; struct dns_rr rr; unsigned nrr; unsigned char dn[DNS_MAXDN]; const unsigned char *pkt, *cur, *end; char *result_str[MAX_RR] = { NULL }; char *result_combined = NULL; /* If out ci isn't active, we must have timed out already */ if(!__isactive_ci(ci)) { if(result) free(result); return; } ci->timed_out = 0; /* If we don't have a result, explode */ if (!result) { ci->error = strdup(dns_strerror(r)); goto cleanup; } /* Process the packet */ pkt = result; end = pkt + r; cur = dns_payload(pkt); dns_getdn(pkt, &cur, end, dn, sizeof(dn)); dns_initparse(&p, NULL, pkt, cur, end); p.dnsp_qcls = 0; p.dnsp_qtyp = 0; nrr = 0; while((r = dns_nextrr(&p, &rr)) > 0) { if (!dns_dnequal(dn, rr.dnsrr_dn)) continue; if ((ci->query_ctype == DNS_C_ANY || ci->query_ctype == rr.dnsrr_cls) && (ci->query_rtype == DNS_T_ANY || ci->query_rtype == rr.dnsrr_typ)) ++nrr; else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) { if (dns_getdn(pkt, &rr.dnsrr_dptr, end, p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 || rr.dnsrr_dptr != rr.dnsrr_dend) { ci->error = strdup("protocol error"); break; } else { int32_t on = 1; /* This actually updates what we're looking for */ dns_dntodn(p.dnsp_dnbuf, ci->dn, sizeof(dn)); noit_stats_set_metric(ci->check, &ci->current, "cname", METRIC_INT32, &on); /* Now follow the leader */ noitL(nldeb, "%s. CNAME %s.\n", dns_dntosp(dn), dns_dntosp(p.dnsp_dnbuf)); dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn)); noitL(nldeb, " ---> '%s'\n", dns_dntosp(dn)); } } } if (!r && !nrr) { ci->error = strdup("no data"); } dns_rewind(&p, NULL); p.dnsp_qtyp = ci->query_rtype == DNS_T_ANY ? 0 : ci->query_rtype; p.dnsp_qcls = ci->query_ctype == DNS_C_ANY ? 0 : ci->query_ctype; while(dns_nextrr(&p, &rr) && ci->nrr < MAX_RR) decode_rr(ci, &p, &rr, &result_str[ci->nrr]); if(ci->sort) qsort(result_str, ci->nrr, sizeof(*result_str), cstring_cmp); /* calculate the length and allocate on the stack */ len = 0; for(i=0; i<ci->nrr; i++) len += strlen(result_str[i]) + 2; result_combined = alloca(len); result_combined[0] = '\0'; /* string it together */ len = 0; for(i=0; i<ci->nrr; i++) { int slen; if(i) { memcpy(result_combined + len, ", ", 2); len += 2; } slen = strlen(result_str[i]); memcpy(result_combined + len, result_str[i], slen); len += slen; result_combined[len] = '\0'; free(result_str[i]); /* free as we go */ } noit_stats_set_metric(ci->check, &ci->current, "answer", METRIC_STRING, result_combined); cleanup: if(result) free(result); if(ci->timeout_event) { eventer_t e = eventer_remove(ci->timeout_event); ci->timeout_event = NULL; if(e) eventer_free(e); } ci->check->flags &= ~NP_RUNNING; dns_check_log_results(ci); __deactivate_ci(ci); }