/** * @file dns/rr.c DNS Resource Records * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include static void rr_destructor(void *data) { struct dnsrr *rr = data; mem_deref(rr->name); switch (rr->type) { case DNS_TYPE_NS: mem_deref(rr->rdata.ns.nsdname); break; case DNS_TYPE_CNAME: mem_deref(rr->rdata.cname.cname); break; case DNS_TYPE_SOA: mem_deref(rr->rdata.soa.mname); mem_deref(rr->rdata.soa.rname); break; case DNS_TYPE_PTR: mem_deref(rr->rdata.ptr.ptrdname); break; case DNS_TYPE_MX: mem_deref(rr->rdata.mx.exchange); break; case DNS_TYPE_SRV: mem_deref(rr->rdata.srv.target); break; case DNS_TYPE_NAPTR: mem_deref(rr->rdata.naptr.flags); mem_deref(rr->rdata.naptr.services); mem_deref(rr->rdata.naptr.regexp); mem_deref(rr->rdata.naptr.replace); break; } } struct dnsrr *dns_rr_alloc(void) { return mem_zalloc(sizeof(struct dnsrr), rr_destructor); } int dns_rr_encode(struct mbuf *mb, const struct dnsrr *rr, int64_t ttl_offs, struct hash *ht_dname, size_t start) { uint32_t ttl; uint16_t len; size_t start_rdata; int err = 0; if (!mb || !rr) return EINVAL; ttl = (uint32_t)((rr->ttl > ttl_offs) ? (rr->ttl - ttl_offs) : 0); err |= dns_dname_encode(mb, rr->name, ht_dname, start, true); err |= mbuf_write_u16(mb, htons(rr->type)); err |= mbuf_write_u16(mb, htons(rr->dnsclass)); err |= mbuf_write_u32(mb, htonl(ttl)); err |= mbuf_write_u16(mb, htons(rr->rdlen)); start_rdata = mb->pos; switch (rr->type) { case DNS_TYPE_A: err |= mbuf_write_u32(mb, htonl(rr->rdata.a.addr)); break; case DNS_TYPE_NS: err |= dns_dname_encode(mb, rr->rdata.ns.nsdname, ht_dname, start, true); break; case DNS_TYPE_CNAME: err |= dns_dname_encode(mb, rr->rdata.cname.cname, ht_dname, start, true); break; case DNS_TYPE_SOA: err |= dns_dname_encode(mb, rr->rdata.soa.mname, ht_dname, start, true); err |= dns_dname_encode(mb, rr->rdata.soa.rname, ht_dname, start, true); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.serial)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.refresh)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.retry)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.expire)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.ttlmin)); break; case DNS_TYPE_PTR: err |= dns_dname_encode(mb, rr->rdata.ptr.ptrdname, ht_dname, start, true); break; case DNS_TYPE_MX: err |= mbuf_write_u16(mb, htons(rr->rdata.mx.pref)); err |= dns_dname_encode(mb, rr->rdata.mx.exchange, ht_dname, start, true); break; case DNS_TYPE_AAAA: err |= mbuf_write_mem(mb, rr->rdata.aaaa.addr, 16); break; case DNS_TYPE_SRV: err |= mbuf_write_u16(mb, htons(rr->rdata.srv.pri)); err |= mbuf_write_u16(mb, htons(rr->rdata.srv.weight)); err |= mbuf_write_u16(mb, htons(rr->rdata.srv.port)); err |= dns_dname_encode(mb, rr->rdata.srv.target, ht_dname, start, false); break; case DNS_TYPE_NAPTR: err |= mbuf_write_u16(mb, htons(rr->rdata.naptr.order)); err |= mbuf_write_u16(mb, htons(rr->rdata.naptr.pref)); err |= dns_cstr_encode(mb, rr->rdata.naptr.flags); err |= dns_cstr_encode(mb, rr->rdata.naptr.services); err |= dns_cstr_encode(mb, rr->rdata.naptr.regexp); err |= dns_dname_encode(mb, rr->rdata.naptr.replace, ht_dname, start, false); break; default: err = EINVAL; break; } len = mb->pos - start_rdata; mb->pos = start_rdata - 2; err |= mbuf_write_u16(mb, htons(len)); mb->pos += len; return err; } int dns_rr_decode(struct mbuf *mb, struct dnsrr **rr, size_t start) { int err = 0; struct dnsrr *lrr; if (!mb || !rr) return EINVAL; lrr = dns_rr_alloc(); if (!lrr) return ENOMEM; err = dns_dname_decode(mb, &lrr->name, start); if (err) goto error; if (mbuf_get_left(mb) < 10) goto fmerr; lrr->type = ntohs(mbuf_read_u16(mb)); lrr->dnsclass = ntohs(mbuf_read_u16(mb)); lrr->ttl = ntohl(mbuf_read_u32(mb)); lrr->rdlen = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < lrr->rdlen) goto fmerr; switch (lrr->type) { case DNS_TYPE_A: if (lrr->rdlen != 4) goto fmerr; lrr->rdata.a.addr = ntohl(mbuf_read_u32(mb)); break; case DNS_TYPE_NS: err = dns_dname_decode(mb, &lrr->rdata.ns.nsdname, start); if (err) goto error; break; case DNS_TYPE_CNAME: err = dns_dname_decode(mb, &lrr->rdata.cname.cname, start); if (err) goto error; break; case DNS_TYPE_SOA: err = dns_dname_decode(mb, &lrr->rdata.soa.mname, start); if (err) goto error; err = dns_dname_decode(mb, &lrr->rdata.soa.rname, start); if (err) goto error; if (mbuf_get_left(mb) < 20) goto fmerr; lrr->rdata.soa.serial = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.refresh = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.retry = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.expire = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.ttlmin = ntohl(mbuf_read_u32(mb)); break; case DNS_TYPE_PTR: err = dns_dname_decode(mb, &lrr->rdata.ptr.ptrdname, start); if (err) goto error; break; case DNS_TYPE_MX: if (mbuf_get_left(mb) < 2) goto fmerr; lrr->rdata.mx.pref = ntohs(mbuf_read_u16(mb)); err = dns_dname_decode(mb, &lrr->rdata.mx.exchange, start); if (err) goto error; break; case DNS_TYPE_AAAA: if (lrr->rdlen != 16) goto fmerr; err = mbuf_read_mem(mb, lrr->rdata.aaaa.addr, 16); if (err) goto error; break; case DNS_TYPE_SRV: if (mbuf_get_left(mb) < 6) goto fmerr; lrr->rdata.srv.pri = ntohs(mbuf_read_u16(mb)); lrr->rdata.srv.weight = ntohs(mbuf_read_u16(mb)); lrr->rdata.srv.port = ntohs(mbuf_read_u16(mb)); err = dns_dname_decode(mb, &lrr->rdata.srv.target, start); if (err) goto error; break; case DNS_TYPE_NAPTR: if (mbuf_get_left(mb) < 4) goto fmerr; lrr->rdata.naptr.order = ntohs(mbuf_read_u16(mb)); lrr->rdata.naptr.pref = ntohs(mbuf_read_u16(mb)); err = dns_cstr_decode(mb, &lrr->rdata.naptr.flags); if (err) goto error; err = dns_cstr_decode(mb, &lrr->rdata.naptr.services); if (err) goto error; err = dns_cstr_decode(mb, &lrr->rdata.naptr.regexp); if (err) goto error; err = dns_dname_decode(mb, &lrr->rdata.naptr.replace, start); if (err) goto error; break; default: mb->pos += lrr->rdlen; break; } *rr = lrr; return 0; fmerr: err = EINVAL; error: mem_deref(lrr); return err; } bool dns_rr_cmp(const struct dnsrr *rr1, const struct dnsrr *rr2, bool rdata) { if (!rr1 || !rr2) return false; if (rr1 == rr2) return true; if (rr1->type != rr2->type) return false; if (rr1->dnsclass != rr2->dnsclass) return false; if (str_casecmp(rr1->name, rr2->name)) return false; if (!rdata) return true; switch (rr1->type) { case DNS_TYPE_A: if (rr1->rdata.a.addr != rr2->rdata.a.addr) return false; break; case DNS_TYPE_NS: if (str_casecmp(rr1->rdata.ns.nsdname, rr2->rdata.ns.nsdname)) return false; break; case DNS_TYPE_CNAME: if (str_casecmp(rr1->rdata.cname.cname, rr2->rdata.cname.cname)) return false; break; case DNS_TYPE_SOA: if (str_casecmp(rr1->rdata.soa.mname, rr2->rdata.soa.mname)) return false; if (str_casecmp(rr1->rdata.soa.rname, rr2->rdata.soa.rname)) return false; if (rr1->rdata.soa.serial != rr2->rdata.soa.serial) return false; if (rr1->rdata.soa.refresh != rr2->rdata.soa.refresh) return false; if (rr1->rdata.soa.retry != rr2->rdata.soa.retry) return false; if (rr1->rdata.soa.expire != rr2->rdata.soa.expire) return false; if (rr1->rdata.soa.ttlmin != rr2->rdata.soa.ttlmin) return false; break; case DNS_TYPE_PTR: if (str_casecmp(rr1->rdata.ptr.ptrdname, rr2->rdata.ptr.ptrdname)) return false; break; case DNS_TYPE_MX: if (rr1->rdata.mx.pref != rr2->rdata.mx.pref) return false; if (str_casecmp(rr1->rdata.mx.exchange, rr2->rdata.mx.exchange)) return false; break; case DNS_TYPE_AAAA: if (memcmp(rr1->rdata.aaaa.addr, rr2->rdata.aaaa.addr, 16)) return false; break; case DNS_TYPE_SRV: if (rr1->rdata.srv.pri != rr2->rdata.srv.pri) return false; if (rr1->rdata.srv.weight != rr2->rdata.srv.weight) return false; if (rr1->rdata.srv.port != rr2->rdata.srv.port) return false; if (str_casecmp(rr1->rdata.srv.target, rr2->rdata.srv.target)) return false; break; case DNS_TYPE_NAPTR: if (rr1->rdata.naptr.order != rr2->rdata.naptr.order) return false; if (rr1->rdata.naptr.pref != rr2->rdata.naptr.pref) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.flags, rr2->rdata.naptr.flags)) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.services, rr2->rdata.naptr.services)) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.regexp, rr2->rdata.naptr.regexp)) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.replace, rr2->rdata.naptr.replace)) return false; break; default: return false; } return true; } const char *dns_rr_typename(uint16_t type) { switch (type) { case DNS_TYPE_A: return "A"; case DNS_TYPE_NS: return "NS"; case DNS_TYPE_CNAME: return "CNAME"; case DNS_TYPE_SOA: return "SOA"; case DNS_TYPE_PTR: return "PTR"; case DNS_TYPE_MX: return "MX"; case DNS_TYPE_AAAA: return "AAAA"; case DNS_TYPE_SRV: return "SRV"; case DNS_TYPE_NAPTR: return "NAPTR"; case DNS_QTYPE_IXFR: return "IXFR"; case DNS_QTYPE_AXFR: return "AXFR"; case DNS_QTYPE_ANY: return "ANY"; default: return "??"; } } const char *dns_rr_classname(uint16_t dnsclass) { switch (dnsclass) { case DNS_CLASS_IN: return "IN"; case DNS_QCLASS_ANY: return "ANY"; default: return "??"; } } int dns_rr_print(struct re_printf *pf, const struct dnsrr *rr) { static const size_t w = 24; struct sa sa; size_t n, l; int err; if (!pf || !rr) return EINVAL; l = str_len(rr->name); n = (w > l) ? w - l : 0; err = re_hprintf(pf, "%s.", rr->name); while (n--) err |= pf->vph(" ", 1, pf->arg); err |= re_hprintf(pf, " %10lld %-4s %-7s ", rr->ttl, dns_rr_classname(rr->dnsclass), dns_rr_typename(rr->type)); switch (rr->type) { case DNS_TYPE_A: sa_set_in(&sa, rr->rdata.a.addr, 0); err |= re_hprintf(pf, "%j", &sa); break; case DNS_TYPE_NS: err |= re_hprintf(pf, "%s.", rr->rdata.ns.nsdname); break; case DNS_TYPE_CNAME: err |= re_hprintf(pf, "%s.", rr->rdata.cname.cname); break; case DNS_TYPE_SOA: err |= re_hprintf(pf, "%s. %s. %u %u %u %u %u", rr->rdata.soa.mname, rr->rdata.soa.rname, rr->rdata.soa.serial, rr->rdata.soa.refresh, rr->rdata.soa.retry, rr->rdata.soa.expire, rr->rdata.soa.ttlmin); break; case DNS_TYPE_PTR: err |= re_hprintf(pf, "%s.", rr->rdata.ptr.ptrdname); break; case DNS_TYPE_MX: err |= re_hprintf(pf, "%3u %s.", rr->rdata.mx.pref, rr->rdata.mx.exchange); break; case DNS_TYPE_AAAA: sa_set_in6(&sa, rr->rdata.aaaa.addr, 0); err |= re_hprintf(pf, "%j", &sa); break; case DNS_TYPE_SRV: err |= re_hprintf(pf, "%3u %3u %u %s.", rr->rdata.srv.pri, rr->rdata.srv.weight, rr->rdata.srv.port, rr->rdata.srv.target); break; case DNS_TYPE_NAPTR: err |= re_hprintf(pf, "%3u %3u \"%s\" \"%s\" \"%s\" %s.", rr->rdata.naptr.order, rr->rdata.naptr.pref, rr->rdata.naptr.flags, rr->rdata.naptr.services, rr->rdata.naptr.regexp, rr->rdata.naptr.replace); break; default: err |= re_hprintf(pf, "?"); break; } return err; }