static int dns_interpolate_inaddr_arpa(char *buff, int len, const char *key, const char *ip) { const char *b, *e; char *o; unsigned char dn[DNS_MAXDN]; int il; struct { struct in_addr addr; struct in6_addr addr6; } a; /* This function takes a dot delimited string as input and * reverses the parts split on dot. */ (void)key; if (dns_pton(AF_INET, ip, &a.addr) > 0) { dns_a4todn(&a.addr, 0, dn, sizeof(dn)); dns_dntop(dn,buff,len); return strlen(buff); } else if (dns_pton(AF_INET6, ip, &a.addr6) > 0) { dns_a6todn(&a.addr6, 0, dn, sizeof(dn)); dns_dntop(dn,buff,len); return strlen(buff); } o = buff; il = strlen(ip); if(len <= il) { /* not enough room for ip and '\0' */ if(len > 0) buff[0] = '\0'; return 0; } e = ip + il; b = e - 1; while(b >= ip) { const char *term; while(b >= ip && *b != '.') b--; /* Rewind to previous part */ term = b + 1; /* term is one ahead, we went past it */ if(term != e) memcpy(o, term, e - term); /* no sense in copying nothing */ o += e - term; /* advance the term length */ e = b; b = e - 1; if(e >= ip) *o++ = '.'; /* we must be at . */ } *o = '\0'; assert((o - buff) == il); return o - buff; }
void zlog(int level, const struct zone *zone, const char *fmt, ...) { va_list ap; char buf[128]; char name[DNS_MAXDOMAIN+1]; va_start(ap, fmt); vssprintf(buf, sizeof(buf), fmt, ap); va_end(ap); dns_dntop(zone->z_dn, name, sizeof(name)); dslog(level, 0, "zone %.70s: %s", name, buf); }
int dns_parse_srv(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, void **result) { struct dns_rr_srv *ret; struct dns_parse p; struct dns_rr rr; int r, l; char *sp; dnsc_t srv[DNS_MAXDN]; assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_SRV); /* first, validate the answer and count size of the result */ l = 0; dns_initparse(&p, qdn, pkt, cur, end); while((r = dns_nextrr(&p, &rr)) > 0) { cur = rr.dnsrr_dptr + 6; r = dns_getdn(pkt, &cur, end, srv, sizeof(srv)); if (r <= 0 || cur != rr.dnsrr_dend) return DNS_E_PROTOCOL; l += dns_dntop_size(srv); } if (r < 0) return DNS_E_PROTOCOL; if (!p.dnsp_nrr) return DNS_E_NODATA; /* next, allocate and set up result */ l += dns_stdrr_size(&p); ret = malloc(sizeof(*ret) + sizeof(struct dns_srv) * p.dnsp_nrr + l); if (!ret) return DNS_E_NOMEM; ret->dnssrv_nrr = p.dnsp_nrr; ret->dnssrv_srv = (struct dns_srv *)(ret+1); /* and 3rd, fill in result, finally */ sp = (char*)(ret->dnssrv_srv + p.dnsp_nrr); for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { ret->dnssrv_srv[r].name = sp; cur = rr.dnsrr_dptr; ret->dnssrv_srv[r].priority = dns_get16(cur); ret->dnssrv_srv[r].weight = dns_get16(cur+2); ret->dnssrv_srv[r].port = dns_get16(cur+4); cur += 6; dns_getdn(pkt, &cur, end, srv, sizeof(srv)); sp += dns_dntop(srv, sp, DNS_MAXNAME); } dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); *result = ret; return 0; }
static void dumpstats(void) { struct dnsstats tot; char name[DNS_MAXDOMAIN+1]; FILE *f; struct zone *z; f = fopen(statsfile, "a"); if (f) fprintf(f, "%ld", (long)time(NULL)); #define C ":%" PRI_DNSCNT tot = gstats; for(z = zonelist; z; z = z->z_next) { #define add(x) tot.x += z->z_stats.x add(b_in); add(b_out); add(q_ok); add(q_nxd); add(q_err); #undef add if (f) { dns_dntop(z->z_dn, name, sizeof(name)); #define delta(x) z->z_stats.x - z->z_pstats.x fprintf(f, " %s" C C C C C, name, delta(q_ok) + delta(q_nxd) + delta(q_err), delta(q_ok), delta(q_nxd), delta(b_in), delta(b_out)); #undef delta } if (stats_relative) z->z_pstats = z->z_stats; } if (f) { #define delta(x) tot.x - gptot.x fprintf(f, " *" C C C C C "\n", delta(q_ok) + delta(q_nxd) + delta(q_err), delta(q_ok), delta(q_nxd), delta(b_in), delta(b_out)); #undef delta fclose(f); } if (stats_relative) gptot = tot; #undef C }
static void logstats(int reset) { time_t t = time(NULL); time_t d = t - stats_time; struct dnsstats tot = gstats; char name[DNS_MAXDOMAIN+1]; struct zone *z; #define C(x) " " #x "=%" PRI_DNSCNT for(z = zonelist; z; z = z->z_next) { #define add(x) tot.x += z->z_stats.x add(b_in); add(b_out); add(q_ok); add(q_nxd); add(q_err); #undef add dns_dntop(z->z_dn, name, sizeof(name)); dslog(LOG_INFO, 0, "stats for %ldsecs zone %.60s:" C(tot) C(ok) C(nxd) C(err) C(in) C(out), (long)d, name, z->z_stats.q_ok + z->z_stats.q_nxd + z->z_stats.q_err, z->z_stats.q_ok, z->z_stats.q_nxd, z->z_stats.q_err, z->z_stats.b_in, z->z_stats.b_out); } dslog(LOG_INFO, 0, "stats for %ldsec:" C(tot) C(ok) C(nxd) C(err) C(in) C(out), (long)d, tot.q_ok + tot.q_nxd + tot.q_err, tot.q_ok, tot.q_nxd, tot.q_err, tot.b_in, tot.b_out); #undef C if (reset) { for(z = zonelist; z; z = z->z_next) { memset(&z->z_stats, 0, sizeof(z->z_stats)); memset(&z->z_pstats, 0, sizeof(z->z_pstats)); } memset(&gstats, 0, sizeof(gstats)); memset(&gptot, 0, sizeof(gptot)); stats_time = t; } }
const char *dns_dntosp(dnscc_t *dn) { return dns_dntop(dn, name, sizeof(name)) > 0 ? name : 0; }
static void decode_rr(struct dns_check_info *ci, struct dns_parse *p, struct dns_rr *rr, char **output) { char buff[DNS_MAXDN], *txt_str = buff, *c; u_int32_t ttl, vu; int32_t vs; int totalsize; const unsigned char *pkt = p->dnsp_pkt; const unsigned char *end = p->dnsp_end; const unsigned char *dptr = rr->dnsrr_dptr; const unsigned char *dend = rr->dnsrr_dend; unsigned char *dn = rr->dnsrr_dn; const unsigned char *tmp; /* Not interested unless it is the answer to my exact question */ if (!dns_dnequal(ci->dn, dn)) return; if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) { /* We don't handle EDNS0 OPT records */ goto decode_err; } noitL(nldeb, "%s. %u %s %s\n", dns_dntosp(dn), rr->dnsrr_ttl, dns_classname(rr->dnsrr_cls), dns_typename(rr->dnsrr_typ)); ttl = rr->dnsrr_ttl; noit_stats_set_metric(ci->check, &ci->current, "ttl", METRIC_UINT32, &ttl); switch(rr->dnsrr_typ) { case DNS_T_A: if (rr->dnsrr_dsz != 4) goto decode_err; snprintf(buff, sizeof(buff), "%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]); break; case DNS_T_AAAA: if (rr->dnsrr_dsz != 16) goto decode_err; inet_ntop(AF_INET6, dptr, buff, sizeof(buff)); break; case DNS_T_TXT: totalsize = 0; for(tmp = dptr; tmp < dend; totalsize += *tmp, tmp += *tmp + 1) if(tmp + *tmp + 1 > dend) goto decode_err; /* worst case: every character escaped + '\0' */ txt_str = alloca(totalsize * 3 + 1); if(!txt_str) goto decode_err; c = txt_str; for(tmp = dptr; tmp < dend; tmp += *tmp + 1) c = encode_txt(c, tmp+1, *tmp); break; case DNS_T_MX: snprintf(buff, sizeof(buff), "%d ", dns_get16(dptr)); tmp = dptr + 2; if(dns_getdn(pkt, &tmp, end, dn, DNS_MAXDN) <= 0 || tmp != dend) goto decode_err; dns_dntop(dn, buff + strlen(buff), sizeof(buff) - strlen(buff)); break; case DNS_T_SOA: if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err; dns_dntop(dn, buff, sizeof(buff)); noit_stats_set_metric(ci->check, &ci->current, "name-server", METRIC_STRING, buff); if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err; dns_dntop(dn, buff, sizeof(buff)); noit_stats_set_metric(ci->check, &ci->current, "email-addr", METRIC_STRING, buff); if(dptr + 5 * sizeof(u_int32_t) != dend) goto decode_err; vu = dns_get32(dptr); dptr += sizeof(u_int32_t); noit_stats_set_metric(ci->check, &ci->current, "serial", METRIC_UINT32, &vu); /* the serial is what we elect to store as the "answer" as text... * because it rarely changes and that seems the most interesting thing * to track change-log-style. */ snprintf(buff, sizeof(buff), "%u", vu); vs = dns_get32(dptr); dptr += sizeof(int32_t); noit_stats_set_metric(ci->check, &ci->current, "refresh", METRIC_UINT32, &vs); vs = dns_get32(dptr); dptr += sizeof(int32_t); noit_stats_set_metric(ci->check, &ci->current, "retry", METRIC_UINT32, &vs); vs = dns_get32(dptr); dptr += sizeof(int32_t); noit_stats_set_metric(ci->check, &ci->current, "expiry", METRIC_UINT32, &vs); vs = dns_get32(dptr); dptr += sizeof(int32_t); noit_stats_set_metric(ci->check, &ci->current, "minimum", METRIC_UINT32, &vs); break; case DNS_T_CNAME: case DNS_T_PTR: case DNS_T_NS: case DNS_T_MB: case DNS_T_MD: case DNS_T_MF: case DNS_T_MG: case DNS_T_MR: if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto decode_err; dns_dntop(dn, buff, sizeof(buff)); break; default: break; } if(*output) { int newlen = strlen(*output) + strlen(", ") + strlen(buff) + 1; char *newstr; newstr = malloc(newlen); snprintf(newstr, newlen, "%s, %s", *output, buff); free(*output); *output = newstr; } else *output = strdup(txt_str); ci->nrr++; return; decode_err: ci->error = strdup("RR decode error"); return; }
static void dns_cb(struct dns_ctx *ctx, void *result, void *data) { int r = dns_status(ctx); dns_lookup_ctx_t *dlc = data; struct dns_parse p; struct dns_rr rr; unsigned nrr = 0; unsigned char dn[DNS_MAXDN]; const unsigned char *pkt, *cur, *end; lua_State *L; if(!dlc->active) goto cleanup; if(!result) goto cleanup; L = dlc->ci->coro_state; 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; while(dns_nextrr(&p, &rr) > 0) { const char *fieldname = NULL; char buff[DNS_MAXDN], *txt_str, *c; int totalsize; const unsigned char *pkt = p.dnsp_pkt; const unsigned char *end = p.dnsp_end; const unsigned char *dptr = rr.dnsrr_dptr; const unsigned char *dend = rr.dnsrr_dend; unsigned char *dn = rr.dnsrr_dn; const unsigned char *tmp; if (!dns_dnequal(dn, rr.dnsrr_dn)) continue; if ((dlc->query_ctype == DNS_C_ANY || dlc->query_ctype == rr.dnsrr_cls) && (dlc->query_rtype == DNS_T_ANY || dlc->query_rtype == rr.dnsrr_typ)) { lua_newtable(L); lua_pushinteger(L, rr.dnsrr_ttl); lua_setfield(L, -2, "ttl"); switch(rr.dnsrr_typ) { case DNS_T_A: if(rr.dnsrr_dsz == 4) { snprintf(buff, sizeof(buff), "%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]); lua_pushstring(L, buff); lua_setfield(L, -2, "a"); } break; case DNS_T_AAAA: if(rr.dnsrr_dsz == 16) { inet_ntop(AF_INET6, dptr, buff, 16); lua_pushstring(L, buff); lua_setfield(L, -2, "aaaa"); } break; case DNS_T_TXT: totalsize = 0; for(tmp = dptr; tmp < dend; totalsize += *tmp, tmp += *tmp + 1) if(tmp + *tmp + 1 > dend) break; /* worst case: every character escaped + '\0' */ txt_str = alloca(totalsize * 3 + 1); if(!txt_str) break; c = txt_str; for(tmp = dptr; tmp < dend; tmp += *tmp + 1) c = encode_txt(c, tmp+1, *tmp); lua_pushstring(L, txt_str); lua_setfield(L, -2, "txt"); break; case DNS_T_MX: lua_pushinteger(L, dns_get16(dptr)); lua_setfield(L, -2, "preference"); tmp = dptr + 2; if(dns_getdn(pkt, &tmp, end, dn, DNS_MAXDN) <= 0 || tmp != dend) break; dns_dntop(dn, buff + strlen(buff), sizeof(buff) - strlen(buff)); lua_pushstring(L, buff); lua_setfield(L, -2, "mx"); break; case DNS_T_CNAME: if(!fieldname) fieldname = "cname"; case DNS_T_PTR: if(!fieldname) fieldname = "ptr"; case DNS_T_NS: if(!fieldname) fieldname = "ns"; case DNS_T_MB: if(!fieldname) fieldname = "mb"; case DNS_T_MD: if(!fieldname) fieldname = "md"; case DNS_T_MF: if(!fieldname) fieldname = "mf"; case DNS_T_MG: if(!fieldname) fieldname = "mg"; case DNS_T_MR: if(!fieldname) fieldname = "mr"; if(dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) break; dns_dntop(dn, buff, sizeof(buff)); lua_pushstring(L, buff); lua_setfield(L, -2, fieldname); break; default: break; } ++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) { break; } } } cleanup: if(result) free(result); if(dlc->active) noit_lua_resume(dlc->ci, nrr); lookup_ctx_release(dlc); }