diff options
Diffstat (limited to 'lib/dns.c')
-rw-r--r-- | lib/dns.c | 363 |
1 files changed, 176 insertions, 187 deletions
@@ -31,6 +31,7 @@ #include "libmf.h" #include "dns.h" #include "mailutils/alloc.h" +#include "mailutils/argcv.h" struct mx_buffer { unsigned pref; @@ -70,8 +71,10 @@ static dns_status _getmx(const char *host, unsigned char *answer, size_t answer_size, struct mxbuf *mxbuf, unsigned long *pttl) { - int i, n, nmx; - struct mx_buffer mx_buffer[MAXMXCOUNT]; + struct mx_buffer *mx_buffer = NULL; + size_t mx_max = 0; + size_t nmx = 0; + int i, n; HEADER *hp; unsigned char *eom, *cp; unsigned short qdcount, ancount; @@ -149,23 +152,28 @@ _getmx(const char *host, unsigned char *answer, size_t answer_size, tname, sizeof tname)) < 0) break; cp += n; + if (nmx == mx_max) + mx_buffer = mu_2nrealloc(mx_buffer, + &mx_max, + sizeof(mx_buffer[0])); + mx_buffer[nmx].pref = pref; - mx_buffer[nmx].name = strdup(tname); + mx_buffer[nmx].name = mu_strdup(tname); mu_debug(debug_handle, MU_DEBUG_TRACE2, ("MX %u %s", mx_buffer[nmx].pref, mx_buffer[nmx].name)); - if (++nmx >= mxbuf->mx_max) - break; + nmx++; } /* Sort according to preference value */ qsort(mx_buffer, nmx, sizeof mx_buffer[0], comp_pref); /* Prepare return value */ - mxbuf_init(mxbuf); + mxbuf_init(mxbuf, nmx); for (i = 0; i < nmx; i++) mxbuf->mx_buf[i] = mx_buffer[i].name; - mxbuf->mx_cnt = nmx; + mxbuf->mx_cnt = i; + free(mx_buffer); return dns_success; } @@ -211,10 +219,7 @@ dns_get_mx_records(const char *host, int maxdepth, struct mxbuf *mxbuf, } if (host) { - unsigned char *answer = malloc(MAXPACKET); - if (!answer) - status = dns_failure; - else { + unsigned char *answer = mu_alloc(MAXPACKET); const char *p; int depth; @@ -222,8 +227,7 @@ dns_get_mx_records(const char *host, int maxdepth, struct mxbuf *mxbuf, p++, depth++) { if (ttl) *ttl = ~(unsigned long)0; - status = _getmx(p, answer, MAXPACKET, mxbuf, - ttl); + status = _getmx(p, answer, MAXPACKET, mxbuf, ttl); if (status == dns_success || status == dns_temp_failure) break; @@ -233,27 +237,30 @@ dns_get_mx_records(const char *host, int maxdepth, struct mxbuf *mxbuf, } free(answer); } - } MUTEX_UNLOCK(dns_mutex); free(hbuf); return status; } void -mxbuf_init(struct mxbuf *mxbuf) +mxbuf_init(struct mxbuf *mxbuf, size_t nmx) { - if (!(mxbuf->mx_flags & MXF_MAX)) - mxbuf->mx_max = MAXMXCOUNT; if (mxbuf->mx_flags & MXF_REUSE) { - int i; + size_t i; - for (i = 0; i < mxbuf->mx_max && mxbuf->mx_buf[i]; i++) { + for (i = 0; i < mxbuf->mx_cnt; i++) { free(mxbuf->mx_buf[i]); mxbuf->mx_buf[i] = NULL; } + if (nmx > mxbuf->mx_max) { + mxbuf->mx_buf = + mu_realloc(mxbuf->mx_buf, + nmx * sizeof(mxbuf->mx_buf[0])); + mxbuf->mx_max = nmx; + } } else { - mxbuf->mx_buf = mu_calloc(mxbuf->mx_max, - sizeof(mxbuf->mx_buf[0])); + mxbuf->mx_buf = mu_calloc(nmx, sizeof(mxbuf->mx_buf[0])); + mxbuf->mx_max = nmx; mxbuf->mx_flags |= MXF_REUSE; } mxbuf->mx_cnt = 0; @@ -267,7 +274,10 @@ mxbuf_free(struct mxbuf *mxbuf) for (i = 0; i < mxbuf->mx_max && mxbuf->mx_buf[i]; i++) free(mxbuf->mx_buf[i]); + free(mxbuf->mx_buf); mxbuf->mx_buf = NULL; + mxbuf->mx_flags &= ~MXF_REUSE; + mxbuf->mx_cnt = mxbuf->mx_max = 0; } } @@ -275,6 +285,44 @@ mxbuf_free(struct mxbuf *mxbuf) #define LOOKUP_SUCCESS 0 #define LOOKUP_CNAME 1 +void +dns_reply_free(struct dns_reply *r) +{ + if (r->base) { + size_t i; + for (i = 0; i < r->count; i++) + free(r->base[i]); + free(r->base); + } +} + +static void +dns_reply_append(struct dns_reply *r, void *ptr, size_t len, int last) +{ + if (r->count == r->max) + r->base = mu_2nrealloc(r->base, + &r->max, + sizeof(r->base[0])); + if (last && r->count) { + while (r->last_len + len > r->last_max) + r->base[r->count-1] = + mu_2nrealloc(r->base[r->count-1], + &r->last_max, + 1); + } else if (ptr == NULL) { + r->base[r->count] = NULL; + return; + } else { + r->base[r->count] = mu_alloc(len); + r->count++; + r->last_len = 0; + r->last_max = len; + } + + memcpy((char*)r->base[r->count-1] + r->last_len, ptr, len); + r->last_len += len; +} + struct loop_data { int qtype; /* Type of the query */ char *name; /* Key to look up */ @@ -285,9 +333,7 @@ struct loop_data { size_t answer_size; /* Size of answer buffer */ /* Return data: */ - char *hbuf; /* Return buffer */ - size_t hbsize; /* Size of return buffer */ - size_t hbcount; /* Number of items returned */ + struct dns_reply repl; time_t ttl; /* TTL value */ dns_status status; /* Status */ @@ -431,20 +477,11 @@ cname_loop_body(struct loop_data *lp) if (bp + n >= nbuf + blen) { mu_debug(debug_handle, MU_DEBUG_TRACE0, ("size (%d) too big", n)); - } else { - if (n != sizeof(GACOPYZ_UINT32_T)) { - mu_debug(debug_handle, MU_DEBUG_TRACE0, - ("unsupported address size: %d", - n)); - } else if (lp->hbcount * sizeof(GACOPYZ_UINT32_T) >= lp->hbsize) + } else if (n != sizeof(GACOPYZ_UINT32_T)) { mu_debug(debug_handle, MU_DEBUG_TRACE0, - ("Too many addresses")); - else { - memmove((GACOPYZ_UINT32_T *) - lp->hbuf + lp->hbcount, - cp, n); - lp->hbcount++; - } + ("unsupported address size: %d", n)); + } else { + dns_reply_append(&lp->repl, cp, n, 0); } bp += n; blen -= n; @@ -461,12 +498,7 @@ cname_loop_body(struct loop_data *lp) SET_STATUS(lp, dns_failure); return LOOKUP_FAILURE; } - l = strlen(nbuf); - if (lp->hbcount + l >= lp->hbsize) - break; - memcpy(lp->hbuf + lp->hbcount, nbuf, l); - lp->hbcount += l; - lp->hbuf[lp->hbcount++] = 0; + dns_reply_append(&lp->repl, nbuf, strlen(nbuf) + 1, 0); lp->atype = T_PTR; SET_STATUS(lp, dns_success); break; @@ -476,17 +508,18 @@ cname_loop_body(struct loop_data *lp) continue; else { unsigned char *cur, *end; + int i = 0; + cur = cp; end = cp + n; while (cur < end) { l = *cur++; - if (lp->hbcount + l >= lp->hbsize) - break; - memcpy(lp->hbuf + lp->hbcount, cur, l); - lp->hbcount += l; - cur += l; + dns_reply_append(&lp->repl, + cur, l, + i > 0); + i++; } - lp->hbuf[lp->hbcount++] = 0; + dns_reply_append(&lp->repl, "", 1, 1); lp->atype = T_TXT; SET_STATUS(lp, dns_success); } @@ -534,6 +567,14 @@ static void cnameloop(struct loop_data *lp) { MUTEX_LOCK(dns_mutex); + + lp->ttl = ~(unsigned long)0; + lp->repl.base = NULL; + lp->repl.max = 0; + lp->repl.count = 0; + lp->status = dns_failure; + lp->loopcnt = MAXCNAMEDEPTH; + if (!lp->answer) { static unsigned char *answer; if (!answer) @@ -587,7 +628,7 @@ dns_reverse_ipstr(const char *ipstr, char *revipstr) dns_status dns_resolve_ipstr(const char *ipstr, const char *domain, unsigned char *answer, size_t answer_size, - char *hbuf, size_t hbsize, unsigned long *ttl) + char **hbuf, unsigned long *ttl) { char namebuf[NSIZE]; char domainbuf[NSIZE]; @@ -613,21 +654,19 @@ dns_resolve_ipstr(const char *ipstr, const char *domain, ld.domain_size = sizeof domainbuf; ld.answer = answer; ld.answer_size = answer_size; - ld.hbuf = hbuf; - ld.hbsize = hbsize; - ld.hbcount = 0; - ld.ttl = ~(unsigned long)0; - ld.status = dns_failure; - ld.loopcnt = MAXCNAMEDEPTH; cnameloop(&ld); - if (ld.status == dns_success && ld.atype == T_A) { + if (ld.status == dns_success) { + if (ld.atype == T_A) { struct in_addr s; - char *p; - s.s_addr = *(GACOPYZ_UINT32_T*)hbuf; - p = inet_ntoa(s); - strncpy(hbuf, p, hbsize); + s.s_addr = *(GACOPYZ_UINT32_T*)ld.repl.base[0]; + *hbuf = mu_strdup(inet_ntoa(s)); + } else if (ld.atype == T_PTR) { + *hbuf = mu_strdup(ld.repl.base[0]); + } else + abort(); + dns_reply_free(&ld.repl); } *ttl = ld.ttl; @@ -637,7 +676,7 @@ dns_resolve_ipstr(const char *ipstr, const char *domain, dns_status dns_resolve_hostname(const char *host, unsigned char *answer, size_t answer_size, - char *ipbuf, size_t ipbsize, unsigned long *ttl) + char **ipbuf, unsigned long *ttl) { struct loop_data ld; char namebuf[NSIZE]; @@ -653,21 +692,14 @@ dns_resolve_hostname(const char *host, ld.domain_size = sizeof domainbuf; ld.answer = answer; ld.answer_size = answer_size; - ld.hbuf = ipbuf; - ld.hbsize = ipbsize; - ld.hbcount = 0; - ld.ttl = ~(unsigned long)0; - ld.status = dns_failure; - ld.loopcnt = MAXCNAMEDEPTH; cnameloop(&ld); if (ld.status == dns_success && ld.atype == T_A) { struct in_addr s; - char *p; - s.s_addr = *(GACOPYZ_UINT32_T*)ipbuf; - p = inet_ntoa(s); - strncpy(ipbuf, p, ipbsize); + s.s_addr = *(GACOPYZ_UINT32_T*)ld.repl.base[0]; + *ipbuf = mu_strdup(inet_ntoa(s)); + dns_reply_free(&ld.repl); } *ttl = ld.ttl; @@ -676,9 +708,7 @@ dns_resolve_hostname(const char *host, dns_status -a_lookup(const char *host, - GACOPYZ_UINT32_T *ipbuf, size_t ipbsize, size_t *ipcount, - unsigned long *ttl, unsigned char *answer, size_t answer_size) +a_lookup(const char *host, struct dns_reply *repl, unsigned long *ttl) { struct loop_data ld; char namebuf[NSIZE]; @@ -691,45 +721,23 @@ a_lookup(const char *host, ld.name_size = sizeof namebuf; ld.domain = domainbuf; ld.domain_size = sizeof domainbuf; - ld.answer = answer; - ld.answer_size = answer_size; - ld.hbuf = (char*) ipbuf; - ld.hbsize = ipbsize * sizeof ipbuf[0]; - ld.hbcount = 0; - ld.ttl = ~(unsigned long)0; - ld.status = dns_failure; - ld.loopcnt = MAXCNAMEDEPTH; + ld.answer = NULL; + ld.answer_size = 0; cnameloop(&ld); if (ld.status == dns_success) { - if (ipcount) - *ipcount = ld.hbcount; + *repl = ld.repl; if (ttl) *ttl = ld.ttl; } return ld.status; } -static void -textbuf_to_argv(const char *hbuf, size_t hsize, char **argv, size_t argc) -{ - size_t i; - const char *p; - - for (i = 0, p = hbuf; i < argc - 1 && p < hbuf + hsize; - p += strlen(p) + 1) - argv[i++] = mu_strdup(p); - argv[i] = NULL; -} - dns_status -ptr_lookup(struct in_addr ip, - char **names, size_t maxnames, unsigned long *ttl, - unsigned char *answer, size_t answer_size) +ptr_lookup(struct in_addr ip, struct dns_reply *repl, unsigned long *ttl) { char namebuf[NSIZE]; char domainbuf[NSIZE]; - char hbuf[NSIZE]; struct loop_data ld; char *p; @@ -743,19 +751,13 @@ ptr_lookup(struct in_addr ip, ld.domain = domainbuf; strcpy(domainbuf, "in-addr.arpa"); ld.domain_size = sizeof domainbuf; - ld.answer = answer; - ld.answer_size = answer_size; - ld.hbuf = hbuf; - ld.hbsize = sizeof hbuf; - ld.hbcount = 0; - ld.ttl = ~(unsigned long)0; - ld.status = dns_failure; - ld.loopcnt = MAXCNAMEDEPTH; + ld.answer = NULL; + ld.answer_size = 0; cnameloop(&ld); if (ld.status == dns_success) { - textbuf_to_argv(ld.hbuf, ld.hbcount, names, maxnames); + *repl = ld.repl; if (ttl) *ttl = ld.ttl; } @@ -764,12 +766,9 @@ ptr_lookup(struct in_addr ip, } dns_status -txt_lookup(const char *name, - char **names, size_t maxnames, unsigned long *ttl, - unsigned char *answer, size_t answer_size) +txt_lookup(const char *name, struct dns_reply *repl, unsigned long *ttl) { char domainbuf[NSIZE]; - char hbuf[NSIZE]; struct loop_data ld; ld.qtype = T_TXT; @@ -779,21 +778,15 @@ txt_lookup(const char *name, domainbuf[0] = 0; ld.domain = domainbuf; ld.domain_size = sizeof domainbuf; - ld.answer = answer; - ld.answer_size = answer_size; - ld.hbuf = hbuf; - ld.hbsize = sizeof hbuf; - ld.hbcount = 0; - ld.ttl = ~(unsigned long)0; - ld.status = dns_failure; - ld.loopcnt = MAXCNAMEDEPTH; + ld.answer = NULL; + ld.answer_size = 0; cnameloop(&ld); free(ld.name); if (ld.status == dns_success) { - textbuf_to_argv(ld.hbuf, ld.hbcount, names, maxnames); + *repl = ld.repl; if (ttl) *ttl = ld.ttl; } @@ -801,16 +794,19 @@ txt_lookup(const char *name, return ld.status; } -/* FIXME: This is a placeholder for a function that should look up for any - SPF or TXT records for DOMAIN. If any SPF are found, TXT should be - discarded. - For the time being, it handles only TXT */ dns_status spf_lookup(const char *domain, - char **txt, size_t maxtxt, unsigned long *ttl, - unsigned char *answer, size_t answer_size) + char ***txtv, size_t *txtc, unsigned long *ttl) { - return txt_lookup(domain, txt, maxtxt, ttl, answer, answer_size); + dns_status res; + struct dns_reply r; + res = txt_lookup(domain, &r, ttl); + if (res == dns_success) { + *txtv = (char**)r.base; + if (txtc) + *txtc = r.count; + } + return res; } /* rfc4408, chapter 5.5 */ @@ -819,60 +815,58 @@ ptr_validate(const char *ipstr, char ***vnptr, size_t *vcount, unsigned long *pttl) { struct in_addr ip; - char *names[11], **p; - char *vnames[10]; - size_t vi = 0; + size_t i; int status; unsigned long minttl = ~(unsigned long)0; unsigned long ttl; + struct dns_reply ptr_repl; + char **sv = NULL; + size_t sc = 0; + size_t sn = 0; if (!inet_aton(ipstr, &ip)) return dns_failure; - status = ptr_lookup(ip, names, NELEMS(names), &ttl, NULL, 0); + status = ptr_lookup(ip, &ptr_repl, &ttl); if (status != dns_success) return status; UPDATE_TTL(minttl, ttl); - - for (p = names; *p; p++) { - GACOPYZ_UINT32_T ipbuf[10]; - size_t ipcount; - status = a_lookup(*p, - ipbuf, NELEMS(ipbuf), - &ipcount, - &ttl, NULL, 0); - if (status == dns_success && vi < NELEMS(vnames[0])) { - size_t i; + for (i = 0; i < ptr_repl.count; i++) { + struct dns_reply r; + status = a_lookup((char*)ptr_repl.base[i], &r, &ttl); + if (status == dns_success) { + size_t k; UPDATE_TTL(minttl, ttl); - for (i = 0; i < ipcount; i++) - if (ipbuf[i] == ip.s_addr) { - vnames[vi++] = *p; - if (vnptr) - *p = NULL; + for (k = 0; k < r.count; k++) { + if (dns_reply_ip(&r, k) == ip.s_addr) { + if (sc == sn) + sv = mu_2nrealloc(sv, &sn, + sizeof(sv[0])); + sv[sc++] = mu_strdup(ptr_repl.base[i]); break; } } - free(*p); + dns_reply_free(&r); + } } + dns_reply_free(&ptr_repl); - if (vi > 0) { + if (sc == 0) { + return dns_not_found; + } if (pttl) *pttl = minttl; - if (vnptr) { - size_t i; - - *vnptr = mu_calloc(vi+1, sizeof vnptr[0]); - for (i = 0; i < vi; i++) - (*vnptr)[i] = vnames[i]; - (*vnptr)[i] = NULL; - *vcount = vi; - } - } + if (vcount) + *vcount = sc; + if (vnptr) + *vnptr = sv; + else + mu_argcv_free(sc, sv); - return vi > 0 ? dns_success : dns_not_found; + return dns_success; } @@ -891,43 +885,41 @@ mf_to_dns_status(mf_status stat) mf_status getmx(const char *host, struct mxbuf *mxbuf) { - mxbuf_init(mxbuf); return dns_to_mf_status(dns_get_mx_records(host, 1, mxbuf, NULL)); } mf_status -getmxip(char *host, GACOPYZ_UINT32_T *ipbuf, size_t mxmax, size_t *pcount) +getmxip(char *host, GACOPYZ_UINT32_T **pipbuf, size_t *pcount) { struct mxbuf mxbuf; mf_status mxstat; - size_t ipcount; + GACOPYZ_UINT32_T *ipbuf = NULL; + size_t ipcount = 0; + size_t ipmax = 0; size_t i; - mxbuf.mx_max = mxmax; - mxbuf.mx_flags = MXF_MAX; mxstat = getmx(host, &mxbuf); if (mxstat != mf_success) return mxstat; ipcount = 0; for (i = 0; i < mxbuf.mx_cnt; i++) { - size_t ipn; - dns_status stat = a_lookup(mxbuf.mx_buf[i], - ipbuf + ipcount, - mxmax - ipcount, - &ipn, - NULL, NULL, 0); + struct dns_reply r; + dns_status stat = a_lookup(mxbuf.mx_buf[i], &r, NULL); if (stat == dns_success) { size_t n; - for (n = 0; ipcount < mxmax && n < ipn; - n++, ipcount++) - ipbuf[ipcount] = ntohl(ipbuf[ipcount]); - if (ipcount == mxmax) - break; + for (n = 0; n < r.count; n++, ipcount++) { + if (ipcount == ipmax) + ipbuf = mu_2nrealloc(ipbuf, &ipmax, + sizeof(ipbuf)); + ipbuf[ipcount] = ntohl(dns_reply_ip(&r, n)); + } + dns_reply_free(&r); } } mxbuf_free(&mxbuf); + *pipbuf = ipbuf; *pcount = ipcount; return mf_success; } @@ -937,7 +929,7 @@ resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf) { static unsigned char *answer; unsigned long ttl; - char buffer[256]; + char *hbuf; dns_status dstat; if (!answer) @@ -946,13 +938,13 @@ resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf) ("Getting canonical name for %s", ipstr)); dstat = dns_resolve_ipstr(ipstr, domain, answer, MAXPACKET, - buffer, sizeof buffer, &ttl); + &hbuf, &ttl); switch (dstat) { case dns_success: mu_debug(debug_handle, MU_DEBUG_TRACE8, - ("%s resolved to %s", ipstr, buffer)); - *phbuf = mu_strdup(buffer); + ("%s resolved to %s", ipstr, hbuf)); + *phbuf = hbuf; break; default: @@ -972,24 +964,21 @@ mf_status resolve_hostname(const char *host, char **pipbuf) { static unsigned char *answer; - char buffer[256]; char *ipbuf; unsigned long ttl; char *tmphost = mu_strdup(host); dns_status dstat; if (!answer) - answer = malloc(MAXPACKET); + answer = mu_alloc(MAXPACKET); mu_debug(debug_handle, MU_DEBUG_TRACE8, ("Getting IP address for %s", host)); dstat = dns_resolve_hostname(tmphost, answer, MAXPACKET, - buffer, sizeof buffer, - &ttl); + &ipbuf, &ttl); free(tmphost); switch (dstat) { case dns_success: - ipbuf = mu_strdup(buffer); mu_debug(debug_handle, MU_DEBUG_TRACE8, ("%s resolved to %s", host, ipbuf)); *pipbuf = ipbuf; |