int dns_parse_a4(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, void **result) { assert(sizeof(struct in_addr) == 4); assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_A); return dns_parse_a(qdn, pkt, cur, end, result, 4); }
int dns_parse_a6(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, void **result) { #ifdef AF_INET6 assert(sizeof(struct in6_addr) == 16); #endif assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_AAAA); return dns_parse_a(qdn, pkt, cur, end, result, 16); }
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 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); }