summaryrefslogtreecommitdiffabout
path: root/lib/dns.c
Side-by-side diff
Diffstat (limited to 'lib/dns.c') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/dns.c397
1 files changed, 193 insertions, 204 deletions
diff --git a/lib/dns.c b/lib/dns.c
index e23e9ce..73c2364 100644
--- a/lib/dns.c
+++ b/lib/dns.c
@@ -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,28 +219,23 @@ 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 {
- const char *p;
- int depth;
+ unsigned char *answer = mu_alloc(MAXPACKET);
+ const char *p;
+ int depth;
- for (p = host, depth = 0; p && depth < maxdepth;
- p++, depth++) {
- if (ttl)
- *ttl = ~(unsigned long)0;
- status = _getmx(p, answer, MAXPACKET, mxbuf,
- ttl);
- if (status == dns_success
- || status == dns_temp_failure)
- break;
- p = strchr(p, '.');
- if (!p)
- break;
- }
- free(answer);
+ for (p = host, depth = 0; p && depth < maxdepth;
+ p++, depth++) {
+ if (ttl)
+ *ttl = ~(unsigned long)0;
+ status = _getmx(p, answer, MAXPACKET, mxbuf, ttl);
+ if (status == dns_success
+ || status == dns_temp_failure)
+ break;
+ p = strchr(p, '.');
+ if (!p)
+ break;
}
+ free(answer);
}
MUTEX_UNLOCK(dns_mutex);
free(hbuf);
@@ -240,20 +243,24 @@ dns_get_mx_records(const char *host, int maxdepth, struct mxbuf *mxbuf,
}
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 (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)
- mu_debug(debug_handle, MU_DEBUG_TRACE0,
- ("Too many addresses"));
- else {
- memmove((GACOPYZ_UINT32_T *)
- lp->hbuf + lp->hbcount,
- cp, n);
- lp->hbcount++;
- }
+ dns_reply_append(&lp->repl, cp, n, 0);
}
bp += n;
blen -= n;
@@ -460,13 +497,8 @@ cname_loop_body(struct loop_data *lp)
nbuf, sizeof(nbuf))) < 0) {
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) {
- struct in_addr s;
- char *p;
- s.s_addr = *(GACOPYZ_UINT32_T*)hbuf;
- p = inet_ntoa(s);
- strncpy(hbuf, p, hbsize);
+ if (ld.status == dns_success) {
+ if (ld.atype == T_A) {
+ struct in_addr s;
+ 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 (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;
- 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;
-
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;
}
+ }
+ dns_reply_free(&r);
}
- free(*p);
}
-
- if (vi > 0) {
- 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;
- }
+ dns_reply_free(&ptr_repl);
+
+ if (sc == 0) {
+ return dns_not_found;
}
+ if (pttl)
+ *pttl = minttl;
+ 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;

Return to:

Send suggestions and report system problems to the System administrator.