diff options
author | Eric Faurot <eric@faurot.net> | 2014-03-26 10:36:55 +0100 |
---|---|---|
committer | Eric Faurot <eric@faurot.net> | 2014-03-26 10:36:55 +0100 |
commit | f227da67d98ecb8a5191d6c1b365504418dc33f6 (patch) | |
tree | 05b7e4c62ff99be904986754a5accc030f00a5c2 | |
parent | docuemnt deliver as (diff) | |
download | OpenSMTPD-f227da67d98ecb8a5191d6c1b365504418dc33f6.tar.xz OpenSMTPD-f227da67d98ecb8a5191d6c1b365504418dc33f6.zip |
update asr related code after latest API changesopensmtpd-201403261203
-rw-r--r-- | smtpd/dns.c | 427 | ||||
-rw-r--r-- | smtpd/filters/asr_event.c | 20 | ||||
-rw-r--r-- | smtpd/filters/asr_event.h | 12 | ||||
-rw-r--r-- | smtpd/filters/filter_dnsbl.c | 4 |
4 files changed, 398 insertions, 65 deletions
diff --git a/smtpd/dns.c b/smtpd/dns.c index 635df924..2536f7ad 100644 --- a/smtpd/dns.c +++ b/smtpd/dns.c @@ -3,7 +3,7 @@ /* * 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" @@ -55,16 +54,92 @@ struct dns_session { }; struct async_event; -struct async_event * async_run_event(struct async *, - void (*)(int, struct async_res *, void *), void *); +struct async_event * async_run_event(struct asr_query *, + void (*)(struct asr_result *, void *), void *); static void dns_lookup_host(struct dns_session *, const char *, int); -static void dns_dispatch_host(int, struct async_res *, void *); -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 *); +static void dns_dispatch_host(struct asr_result *, void *); +static void dns_dispatch_ptr(struct asr_result *, void *); +static void dns_dispatch_mx(struct asr_result *, void *); +static void dns_dispatch_mx_preference(struct asr_result *, void *); + +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 *); -#define print_dname(a,b,c) asr_strdname(a, b, c) void dns_query_host(uint64_t id, const char *host) @@ -155,7 +230,7 @@ dns_imsg(struct mproc *p, struct imsg *imsg) struct sockaddr_storage ss; struct dns_session *s; struct sockaddr *sa; - struct async *as; + struct asr_query *as; struct msg m; const char *domain, *mx, *host; socklen_t sl; @@ -250,7 +325,7 @@ dns_imsg(struct mproc *p, struct imsg *imsg) } static void -dns_dispatch_host(int ev, struct async_res *ar, void *arg) +dns_dispatch_host(struct asr_result *ar, void *arg) { struct dns_session *s; struct dns_lookup *lookup = arg; @@ -284,7 +359,7 @@ dns_dispatch_host(int ev, struct async_res *ar, void *arg) } static void -dns_dispatch_ptr(int ev, struct async_res *ar, void *arg) +dns_dispatch_ptr(struct asr_result *ar, void *arg) { struct dns_session *s = arg; @@ -299,13 +374,13 @@ dns_dispatch_ptr(int ev, struct async_res *ar, void *arg) } static void -dns_dispatch_mx(int ev, struct async_res *ar, void *arg) +dns_dispatch_mx(struct asr_result *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)); @@ -347,13 +422,13 @@ dns_dispatch_mx(int ev, struct async_res *ar, void *arg) } static void -dns_dispatch_mx_preference(int ev, struct async_res *ar, void *arg) +dns_dispatch_mx_preference(struct asr_result *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)); @@ -400,7 +475,7 @@ dns_lookup_host(struct dns_session *s, const char *host, int preference) { struct dns_lookup *lookup; struct addrinfo hints; - struct async *as; + void *as; lookup = xcalloc(1, sizeof *lookup, "dns_lookup_host"); lookup->preference = preference; @@ -417,17 +492,17 @@ dns_lookup_host(struct dns_session *s, const char *host, int preference) /* Generic libevent glue for asr */ struct async_event { - struct async *async; + struct asr_query *async; struct event ev; - void (*callback)(int, struct async_res *, void *); + void (*callback)(struct asr_result *, void *); void *arg; }; static void async_event_dispatch(int, short, void *); struct async_event * -async_run_event(struct async * async, - void (*cb)(int, struct async_res *, void *), void *arg) +async_run_event(struct asr_query * async, + void (*cb)(struct asr_result *, void *), void *arg) { struct async_event *aev; struct timeval tv; @@ -449,23 +524,285 @@ static void async_event_dispatch(int fd, short ev, void *arg) { struct async_event *aev = arg; - struct async_res ar; - int r; + struct asr_result ar; struct timeval tv; - while ((r = asr_async_run(aev->async, &ar)) == ASYNC_YIELD) - aev->callback(r, &ar, aev->arg); - event_del(&aev->ev); - if (r == ASYNC_COND) { + + if (asr_run(aev->async, &ar) == 0) { event_set(&aev->ev, ar.ar_fd, - ar.ar_cond == ASYNC_READ ? EV_READ : EV_WRITE, - async_event_dispatch, aev); + ar.ar_cond == ASR_WANT_READ ? EV_READ : EV_WRITE, + async_event_dispatch, aev); tv.tv_sec = ar.ar_timeout / 1000; tv.tv_usec = (ar.ar_timeout % 1000) * 1000; event_add(&aev->ev, &tv); - } else { /* ASYNC_DONE */ - aev->callback(r, &ar, aev->arg); + } else { + aev->callback(&ar, aev->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); +} diff --git a/smtpd/filters/asr_event.c b/smtpd/filters/asr_event.c index a59fcaea..a6eaf867 100644 --- a/smtpd/filters/asr_event.c +++ b/smtpd/filters/asr_event.c @@ -25,8 +25,8 @@ void async_event_dispatch(int, short, void *); struct async_event * -async_run_event(struct async * async, - void (*cb)(int, struct async_res *, void *), void *arg) +async_run_event(struct asr_query * async, + void (*cb)(struct asr_result *, void *), void *arg) { struct async_event *aev; struct timeval tv; @@ -46,23 +46,19 @@ void async_event_dispatch(int fd, short ev, void *arg) { struct async_event *aev = arg; - struct async_res ar; - int r; + struct asr_result ar; struct timeval tv; - while ((r = asr_async_run(aev->async, &ar)) == ASYNC_YIELD) - aev->callback(r, &ar, aev->arg); - event_del(&aev->ev); - if (r == ASYNC_COND) { + if (asr_run(aev->async, &ar) == 0) { event_set(&aev->ev, ar.ar_fd, - ar.ar_cond == ASYNC_READ ? EV_READ : EV_WRITE, - async_event_dispatch, aev); + ar.ar_cond == ASR_WANT_READ ? EV_READ : EV_WRITE, + async_event_dispatch, aev); tv.tv_sec = ar.ar_timeout / 1000; tv.tv_usec = (ar.ar_timeout % 1000) * 1000; event_add(&aev->ev, &tv); - } else { /* ASYNC_DONE */ - aev->callback(r, &ar, aev->arg); + } else { /* done */ + aev->callback(&ar, aev->arg); free(aev); } } diff --git a/smtpd/filters/asr_event.h b/smtpd/filters/asr_event.h index 17b199ed..823b276e 100644 --- a/smtpd/filters/asr_event.h +++ b/smtpd/filters/asr_event.h @@ -20,15 +20,15 @@ #include <event.h> -struct async; -struct async_res; +struct asr_query; +struct asr_result; struct async_event { - struct async *async; + struct asr_query *async; struct event ev; - void (*callback)(int, struct async_res *, void *); + void (*callback)(struct asr_result *, void *); void *arg; }; -struct async_event * async_run_event(struct async *, - void (*)(int, struct async_res *, void *), void *); +struct async_event * async_run_event(struct asr_query *, + void (*)(struct asr_result *, void *), void *); diff --git a/smtpd/filters/filter_dnsbl.c b/smtpd/filters/filter_dnsbl.c index 1d8a2316..a998e6e6 100644 --- a/smtpd/filters/filter_dnsbl.c +++ b/smtpd/filters/filter_dnsbl.c @@ -32,7 +32,7 @@ const char * dnsbl_host = "dnsbl.sorbs.net"; static void -dnsbl_event_dispatch(int ret, struct async_res *ar, void *arg) +dnsbl_event_dispatch(struct asr_result *ar, void *arg) { uint64_t *q = arg; @@ -52,7 +52,7 @@ dnsbl_on_connect(uint64_t id, struct filter_connect *conn) struct addrinfo hints; struct sockaddr_in *sain; in_addr_t in_addr; - struct async *as; + struct asr_query *as; uint64_t *q; char buf[512]; |