574 lines
12 KiB
C
574 lines
12 KiB
C
/**
|
|
* @file dns/rr.c DNS Resource Records
|
|
*
|
|
* Copyright (C) 2010 Creytiv.com
|
|
*/
|
|
#include <string.h>
|
|
#include <re_types.h>
|
|
#include <re_fmt.h>
|
|
#include <re_list.h>
|
|
#include <re_mem.h>
|
|
#include <re_mbuf.h>
|
|
#include <re_net.h>
|
|
#include <re_sa.h>
|
|
#include <re_dns.h>
|
|
|
|
|
|
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;
|
|
}
|