diff options
author | Eric Faurot <eric@cvs.openbsd.org> | 2014-03-25 09:01:12 +0000 |
---|---|---|
committer | Eric Faurot <eric@cvs.openbsd.org> | 2014-03-25 09:01:12 +0000 |
commit | 799d1131be19f894bf9a6b65c516ad3b25a4193c (patch) | |
tree | cdd17f6141dde5d76f05060c404af09efc3e3525 | |
parent | e9525585fffcf7ac8875d5978a818332f4d777cf (diff) |
Integrate necessary dns packet parsing helpers from asr.
They are not supposed to be exposed.
ok gilles@
-rw-r--r-- | usr.sbin/smtpd/dns.c | 380 |
1 files changed, 360 insertions, 20 deletions
diff --git a/usr.sbin/smtpd/dns.c b/usr.sbin/smtpd/dns.c index 6b9f404804d..9d8b32196a7 100644 --- a/usr.sbin/smtpd/dns.c +++ b/usr.sbin/smtpd/dns.c @@ -1,9 +1,9 @@ -/* $OpenBSD: dns.c,v 1.71 2014/03/14 11:09:45 eric Exp $ */ +/* $OpenBSD: dns.c,v 1.72 2014/03/25 09:01:11 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2011-2012 Eric Faurot <eric@faurot.net> + * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,7 +35,6 @@ #include <string.h> #include "asr.h" -#include "asr_private.h" #include "smtpd.h" #include "log.h" @@ -64,7 +63,83 @@ static void dns_dispatch_ptr(int, struct async_res *, void *); static void dns_dispatch_mx(int, struct async_res *, void *); static void dns_dispatch_mx_preference(int, struct async_res *, void *); -#define print_dname(a,b,c) asr_strdname(a, b, c) +struct unpack { + const char *buf; + size_t len; + size_t offset; + const char *err; +}; + +struct dns_header { + uint16_t id; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +}; + +struct dns_query { + char q_dname[MAXDNAME]; + uint16_t q_type; + uint16_t q_class; +}; + +struct dns_rr { + char rr_dname[MAXDNAME]; + uint16_t rr_type; + uint16_t rr_class; + uint32_t rr_ttl; + union { + struct { + char cname[MAXDNAME]; + } cname; + struct { + uint16_t preference; + char exchange[MAXDNAME]; + } mx; + struct { + char nsname[MAXDNAME]; + } ns; + struct { + char ptrname[MAXDNAME]; + } ptr; + struct { + char mname[MAXDNAME]; + char rname[MAXDNAME]; + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; + } soa; + struct { + struct in_addr addr; + } in_a; + struct { + struct in6_addr addr6; + } in_aaaa; + struct { + uint16_t rdlen; + const void *rdata; + } other; + } rr; +}; + +static char *print_dname(const char *, char *, size_t); +static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *, + char *, size_t); +static int unpack_data(struct unpack *, void *, size_t); +static int unpack_u16(struct unpack *, uint16_t *); +static int unpack_u32(struct unpack *, uint32_t *); +static int unpack_inaddr(struct unpack *, struct in_addr *); +static int unpack_in6addr(struct unpack *, struct in6_addr *); +static int unpack_dname(struct unpack *, char *, size_t); +static void unpack_init(struct unpack *, const char *, size_t); +static int unpack_header(struct unpack *, struct dns_header *); +static int unpack_query(struct unpack *, struct dns_query *); +static int unpack_rr(struct unpack *, struct dns_rr *); + void dns_query_host(uint64_t id, const char *host) @@ -302,10 +377,10 @@ static void dns_dispatch_mx(int ev, struct async_res *ar, void *arg) { struct dns_session *s = arg; - struct asr_unpack pack; - struct asr_dns_header h; - struct asr_dns_query q; - struct asr_dns_rr rr; + struct unpack pack; + struct dns_header h; + struct dns_query q; + struct dns_rr rr; char buf[512]; size_t found; @@ -325,13 +400,13 @@ dns_dispatch_mx(int ev, struct async_res *ar, void *arg) return; } - asr_unpack_init(&pack, ar->ar_data, ar->ar_datalen); - asr_unpack_header(&pack, &h); - asr_unpack_query(&pack, &q); + unpack_init(&pack, ar->ar_data, ar->ar_datalen); + unpack_header(&pack, &h); + unpack_query(&pack, &q); found = 0; for (; h.ancount; h.ancount--) { - asr_unpack_rr(&pack, &rr); + unpack_rr(&pack, &rr); if (rr.rr_type != T_MX) continue; print_dname(rr.rr.mx.exchange, buf, sizeof(buf)); @@ -350,10 +425,10 @@ static void dns_dispatch_mx_preference(int ev, struct async_res *ar, void *arg) { struct dns_session *s = arg; - struct asr_unpack pack; - struct asr_dns_header h; - struct asr_dns_query q; - struct asr_dns_rr rr; + struct unpack pack; + struct dns_header h; + struct dns_query q; + struct dns_rr rr; char buf[512]; int error; @@ -368,11 +443,11 @@ dns_dispatch_mx_preference(int ev, struct async_res *ar, void *arg) } else { error = DNS_ENOTFOUND; - asr_unpack_init(&pack, ar->ar_data, ar->ar_datalen); - asr_unpack_header(&pack, &h); - asr_unpack_query(&pack, &q); + unpack_init(&pack, ar->ar_data, ar->ar_datalen); + unpack_header(&pack, &h); + unpack_query(&pack, &q); for (; h.ancount; h.ancount--) { - asr_unpack_rr(&pack, &rr); + unpack_rr(&pack, &rr); if (rr.rr_type != T_MX) continue; print_dname(rr.rr.mx.exchange, buf, sizeof(buf)); @@ -469,3 +544,268 @@ async_event_dispatch(int fd, short ev, void *arg) free(aev); } } + +static char * +print_dname(const char *_dname, char *buf, size_t max) +{ + const unsigned char *dname = _dname; + char *res; + size_t left, n, count; + + if (_dname[0] == 0) { + strlcpy(buf, ".", max); + return buf; + } + + res = buf; + left = max - 1; + for (n = 0; dname[0] && left; n += dname[0]) { + count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); + memmove(buf, dname + 1, count); + dname += dname[0] + 1; + left -= count; + buf += count; + if (left) { + left -= 1; + *buf++ = '.'; + } + } + buf[0] = 0; + + return (res); +} + +static ssize_t +dname_expand(const unsigned char *data, size_t len, size_t offset, + size_t *newoffset, char *dst, size_t max) +{ + size_t n, count, end, ptr, start; + ssize_t res; + + if (offset >= len) + return (-1); + + res = 0; + end = start = offset; + + for (; (n = data[offset]); ) { + if ((n & 0xc0) == 0xc0) { + if (offset + 2 > len) + return (-1); + ptr = 256 * (n & ~0xc0) + data[offset + 1]; + if (ptr >= start) + return (-1); + if (end < offset + 2) + end = offset + 2; + offset = start = ptr; + continue; + } + if (offset + n + 1 > len) + return (-1); + + /* copy n + at offset+1 */ + if (dst != NULL && max != 0) { + count = (max < n + 1) ? (max) : (n + 1); + memmove(dst, data + offset, count); + dst += count; + max -= count; + } + res += n + 1; + offset += n + 1; + if (end < offset) + end = offset; + } + if (end < offset + 1) + end = offset + 1; + + if (dst != NULL && max != 0) + dst[0] = 0; + if (newoffset) + *newoffset = end; + return (res + 1); +} + +void +unpack_init(struct unpack *unpack, const char *buf, size_t len) +{ + unpack->buf = buf; + unpack->len = len; + unpack->offset = 0; + unpack->err = NULL; +} + +static int +unpack_data(struct unpack *p, void *data, size_t len) +{ + if (p->err) + return (-1); + + if (p->len - p->offset < len) { + p->err = "too short"; + return (-1); + } + + memmove(data, p->buf + p->offset, len); + p->offset += len; + + return (0); +} + +static int +unpack_u16(struct unpack *p, uint16_t *u16) +{ + if (unpack_data(p, u16, 2) == -1) + return (-1); + + *u16 = ntohs(*u16); + + return (0); +} + +static int +unpack_u32(struct unpack *p, uint32_t *u32) +{ + if (unpack_data(p, u32, 4) == -1) + return (-1); + + *u32 = ntohl(*u32); + + return (0); +} + +static int +unpack_inaddr(struct unpack *p, struct in_addr *a) +{ + return (unpack_data(p, a, 4)); +} + +static int +unpack_in6addr(struct unpack *p, struct in6_addr *a6) +{ + return (unpack_data(p, a6, 16)); +} + +static int +unpack_dname(struct unpack *p, char *dst, size_t max) +{ + ssize_t e; + + if (p->err) + return (-1); + + e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max); + if (e == -1) { + p->err = "bad domain name"; + return (-1); + } + if (e < 0 || e > MAXDNAME) { + p->err = "domain name too long"; + return (-1); + } + + return (0); +} + +static int +unpack_header(struct unpack *p, struct dns_header *h) +{ + if (unpack_data(p, h, HFIXEDSZ) == -1) + return (-1); + + h->flags = ntohs(h->flags); + h->qdcount = ntohs(h->qdcount); + h->ancount = ntohs(h->ancount); + h->nscount = ntohs(h->nscount); + h->arcount = ntohs(h->arcount); + + return (0); +} + +static int +unpack_query(struct unpack *p, struct dns_query *q) +{ + unpack_dname(p, q->q_dname, sizeof(q->q_dname)); + unpack_u16(p, &q->q_type); + unpack_u16(p, &q->q_class); + + return (p->err) ? (-1) : (0); +} + +static int +unpack_rr(struct unpack *p, struct dns_rr *rr) +{ + uint16_t rdlen; + size_t save_offset; + + unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); + unpack_u16(p, &rr->rr_type); + unpack_u16(p, &rr->rr_class); + unpack_u32(p, &rr->rr_ttl); + unpack_u16(p, &rdlen); + + if (p->err) + return (-1); + + if (p->len - p->offset < rdlen) { + p->err = "too short"; + return (-1); + } + + save_offset = p->offset; + + switch (rr->rr_type) { + + case T_CNAME: + unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); + break; + + case T_MX: + unpack_u16(p, &rr->rr.mx.preference); + unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); + break; + + case T_NS: + unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); + break; + + case T_PTR: + unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); + break; + + case T_SOA: + unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); + unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); + unpack_u32(p, &rr->rr.soa.serial); + unpack_u32(p, &rr->rr.soa.refresh); + unpack_u32(p, &rr->rr.soa.retry); + unpack_u32(p, &rr->rr.soa.expire); + unpack_u32(p, &rr->rr.soa.minimum); + break; + + case T_A: + if (rr->rr_class != C_IN) + goto other; + unpack_inaddr(p, &rr->rr.in_a.addr); + break; + + case T_AAAA: + if (rr->rr_class != C_IN) + goto other; + unpack_in6addr(p, &rr->rr.in_aaaa.addr6); + break; + default: + other: + rr->rr.other.rdata = p->buf + p->offset; + rr->rr.other.rdlen = rdlen; + p->offset += rdlen; + } + + if (p->err) + return (-1); + + /* make sure that the advertised rdlen is really ok */ + if (p->offset - save_offset != rdlen) + p->err = "bad dlen"; + + return (p->err) ? (-1) : (0); +} |