re/src/dns/rr.c
Alfred E. Heggestad 6648fd2393 update splash
2010-11-03 11:34:14 +00:00

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;
}