aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-02-17 12:56:00 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-02-17 12:56:00 +0000
commitad4e496b8acfbcaf1ba6aabd2ba8b508f196361d (patch)
tree5960b99cbae5352562b8aa1230119f4702241278
parent4cb6c6dd32298e583c45c49fce63d452bb903f27 (diff)
downloadmailfromd-ad4e496b8acfbcaf1ba6aabd2ba8b508f196361d.tar.gz
mailfromd-ad4e496b8acfbcaf1ba6aabd2ba8b508f196361d.tar.bz2
Begin implementing SPF support
git-svn-id: file:///svnroot/mailfromd/trunk@1250 7a8a7f39-df28-0410-adc6-e0d955640f24
-rw-r--r--ChangeLog15
-rw-r--r--bootstrap1
-rw-r--r--src/Makefile.am5
-rw-r--r--src/bi_dns.m44
-rw-r--r--src/bi_spf.m448
-rw-r--r--src/dns.c564
-rw-r--r--src/dns.h95
-rw-r--r--src/dnsbase.c789
-rw-r--r--src/engine.c2
-rw-r--r--src/mailfrom.h55
-rw-r--r--src/main.c2
-rw-r--r--src/prog.c2
-rw-r--r--src/spf.c810
-rw-r--r--src/spf.h37
14 files changed, 1838 insertions, 591 deletions
diff --git a/ChangeLog b/ChangeLog
index af64c1e8..0eaa275f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2007-02-17 Sergey Poznyakoff <gray@gnu.org.ua>
+
+ * src/spf.c, src/spf.h: Templates for SPF support
+ * src/bi_spf.m4: Interface definitions for SPF support
+ * src/dns.c: Move generic DNS calls to dnsbase.c. Retain only
+ interface calls.
+ * src/dnsbase.c, src/dns.h: Generic DNS support
+
+ * src/mailfrom.h: Move generic DNS declarations to dns.h
+ * src/engine.c, src/bi_dns.m4, src/prog.c: Rename freemx to
+ dns_freemx
+ * src/main.c: Fix warning message wording
+ * src/Makefile.am: Add new sources
+ * bootstrap (MODLIST): Add memrchr
+
2007-02-11 Sergey Poznyakoff <gray@gnu.org.ua>
* gacopyz/trans.awk: Always quote /dev/stderr
diff --git a/bootstrap b/bootstrap
index 965652ae..ed5a5214 100644
--- a/bootstrap
+++ b/bootstrap
@@ -2,6 +2,7 @@
MODLIST="argp\
fprintftime\
malloc\
+ memrchr\
obstack\
realloc\
regex\
diff --git a/src/Makefile.am b/src/Makefile.am
index f5dbd509..7bd5c5c6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -27,6 +27,7 @@ M4_FILES=\
bi_mail.m4\
bi_poll.m4\
bi_sa.m4\
+ bi_spf.m4\
bi_string.m4\
bi_system.m4\
bi_other.m4\
@@ -37,6 +38,7 @@ mailfromd_SOURCES = \
dnscache.c\
db.c\
dns.c\
+ dnsbase.c\
engine.c\
gram.y\
lex.l\
@@ -47,11 +49,12 @@ mailfromd_SOURCES = \
optab.h\
prog.c\
prog.h\
+ spf.c\
symtab.c\
rate.c\
$(M4_FILES:.m4=.c)
-noinst_HEADERS = mailfrom.h mu_dbm.h builtin.h
+noinst_HEADERS = mailfrom.h mu_dbm.h builtin.h dns.h
EXTRA_DIST = gram.h snarf.m4 init.m4 $(M4_FILES) builtin.h builtin.def mf-status.mfi status.mfi mfstat.awk status.mfh optab.opc optab.oph opcode.awk opcodes
BUILT_SOURCES=$(M4_FILES:.m4=.c) builtin.h mf-status.c optab.c optab.h
diff --git a/src/bi_dns.m4 b/src/bi_dns.m4
index 4fc2bcf6..2fa8e393 100644
--- a/src/bi_dns.m4
+++ b/src/bi_dns.m4
@@ -59,7 +59,7 @@ MF_DEFUN(hasmx, NUMBER, STRING string)
if (env_catch(env, mxstat) == 0)
return;
} else
- freemx(mxbuf);
+ dns_freemx(mxbuf);
MF_RETURN(mxstat == mf_success);
}
@@ -118,7 +118,7 @@ MF_DEFUN(ismx, NUMBER, STRING domain, STRING ipstr)
break;
}
}
- freemx(mxbuf);
+ dns_freemx(mxbuf);
}
MF_RETURN(rc);
}
diff --git a/src/bi_spf.m4 b/src/bi_spf.m4
new file mode 100644
index 00000000..f8864a51
--- /dev/null
+++ b/src/bi_spf.m4
@@ -0,0 +1,48 @@
+/* This file is part of mailfromd. -*- c -*-
+ Copyright (C) 2007 Sergey Poznyakoff
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+#include "spf.h"
+
+/* Function for testing SPF check_host. Does nothing useful so far. */
+MF_DEFUN(spf_check_host, NUMBER, STRING ip, STRING domain, STRING sender,
+ STRING helo_domain, STRING my_domain)
+{
+ spf_result res;
+
+ res = spf_check_host(ip, domain, sender,
+ helo_domain,
+ my_domain);
+ MF_RETURN(res);
+}
+END
+
+/* Function for testing SPF check_host. Does nothing useful so far. */
+MF_DEFUN(spf_test_record, NUMBER, STRING record,
+ STRING ip, STRING domain, STRING sender,
+ STRING helo_domain, STRING my_domain)
+{
+ spf_result res;
+
+ res = spf_test_record(record,
+ ip, domain, sender,
+ helo_domain, my_domain);
+ MF_RETURN(res);
+}
+END
+
+MF_INIT
diff --git a/src/dns.c b/src/dns.c
index 8fc17868..7f820e91 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -30,197 +30,10 @@
#include "mailfrom.h"
-struct mx_buffer {
- unsigned pref;
- char *name;
-};
-
-static int
-comp_pref(const void *a, const void *b)
-{
- const struct mx_buffer *ma = a, *mb = b;
- if (ma->pref > mb->pref)
- return 1;
- else if (ma->pref < mb->pref)
- return -1;
- return 0;
-}
-
-/* Obtain MX records for domain name HOST. Return them in mxbuf, sorted by
- preference in ascending order.
- Notice that we *have* to use old BIND-4-style method, since glibc folks
- in their incredible wiseness have not exported new interface functions
- from libresolve.so. The only way to access them would be to link with
- -lresolve statically.
-
- Let's hope that someday this stupidity will change. */
-
-static mf_status
-_getmx(char *host, char *answer, size_t answer_size, mxbuf_t mxbuf,
- size_t *pcount, unsigned long *pttl)
-{
- int i, n, nmx;
- struct mx_buffer mx_buffer[MAXMXCOUNT];
- HEADER *hp;
- unsigned char *eom, *cp;
- unsigned short qdcount, ancount;
- struct __res_state stat;
-
- debug1(80,"Getting MX records for %s", host);
- memset((void *)&stat, 0, sizeof(struct __res_state));
- res_ninit(&stat);
- n = res_nquery (&stat, host, C_IN, T_MX, answer, answer_size);
- res_nclose(&stat);
- if (n < 0) {
- debug2(10, "res_nquery failed (errno=%s, h_errno=%d)",
- mu_strerror(errno), h_errno);
- switch (h_errno) {
- case NO_DATA:
- case NO_RECOVERY:
- case HOST_NOT_FOUND:
- return mf_not_found;
-
- case TRY_AGAIN:
- case -1:
- return mf_temp_failure;
-
- default:
- mu_error("res_nquery(%s) failed with unexpected h_errno %d",
- host, h_errno);
- return mf_failure;
- }
- }
-
- if (n > answer_size)
- n = answer_size;
-
- hp = (HEADER*)answer;
- cp = (unsigned char *) answer + HFIXEDSZ;
- eom = (unsigned char *) answer + n;
-
- /* Skip query part */
- for (qdcount = ntohs((unsigned short)hp->qdcount);
- qdcount--;
- cp += n + QFIXEDSZ) {
- if ((n = dn_skipname(cp, eom)) < 0)
- return mf_failure;
- }
-
- ancount = ntohs((unsigned short)hp->ancount);
-
- /* Collect MX records */
- for (i = nmx = 0; i < ancount; i++) {
- unsigned short pref, type;
- char tname[NS_MAXDNAME];
- unsigned long ttl;
-
- if ((n = dn_expand((unsigned char *)answer,
- eom, cp, tname, sizeof tname)) < 0)
- break;
- cp += n;
- GETSHORT(type, cp);
- cp += INT16SZ;
- GETLONG(ttl, cp);
- if (*pttl > ttl)
- *pttl = ttl;
- GETSHORT(n, cp);
- if (type != T_MX) {
- debug2(90,"unexpected answer type %d, size %d\n",
- type, n);
- cp += n;
- continue;
- }
- GETSHORT(pref, cp);
- if ((n = dn_expand((u_char *)answer, eom, cp,
- tname, sizeof tname)) < 0)
- break;
- cp += n;
- mx_buffer[nmx].pref = pref;
- mx_buffer[nmx].name = strdup(tname);
- debug2(20,"MX %u %s", mx_buffer[nmx].pref,
- mx_buffer[nmx].name);
- if (++nmx >= MAXMXCOUNT)
- break;
- }
-
- /* Sort according to preference value */
- qsort(mx_buffer, nmx, sizeof mx_buffer[0], comp_pref);
-
- /* Prepare return value */
- memset(mxbuf, 0, sizeof(mxbuf_t));
- for (i = 0; i < nmx; i++)
- mxbuf[i] = mx_buffer[i].name;
- *pcount = nmx;
- return mf_success;
-}
-
-int
-is_ipaddr(char *addr)
-{
- int dot_count;
- int digit_count;
-
- dot_count = 0;
- digit_count = 0;
- while (*addr != 0 && *addr != ' ') {
- if (*addr == '.') {
- if (++dot_count > 3)
- break;
- digit_count = 0;
- } else if (!(isdigit(*addr) && ++digit_count <= 3)) {
- return 0;
- }
- addr++;
- }
-
- return dot_count == 3;
-}
-
-MUTEX_DCL(mf_mutex)
-
-/* Return MX records for the given HOST. If no records were found, recurse
- to its parent domains until any record is found or recursion depth reaches
- MAXDEPTH */
-static mf_status
-get_mx_records(char *host, int maxdepth, mxbuf_t mxbuf, size_t *mxcount,
- unsigned long *ttl)
+mf_status
+dns_to_mf_status(dns_status stat)
{
- char *hbuf = NULL;
- mf_status status = mf_failure;
-
- MUTEX_LOCK(mf_mutex);
- if (is_ipaddr(host)) {
- status = resolve_ipstr(host, &hbuf);
- if (status != mf_success)
- host = NULL;
- }
-
- if (host) {
- unsigned char *answer = malloc(MAXPACKET);
- if (!answer)
- status = mf_failure;
- else {
- char *p;
- int depth;
-
- for (p = host, depth = 0; p && depth < maxdepth;
- p++, depth++) {
- *ttl = ~(unsigned long)0;
- status = _getmx(p, answer, MAXPACKET, mxbuf,
- mxcount, ttl);
- if (status == mf_success
- || status == mf_temp_failure)
- break;
- p = strchr(p, '.');
- if (!p)
- break;
- }
- free(answer);
- }
- }
- MUTEX_UNLOCK(mf_mutex);
- free(hbuf);
- return status;
+ return (mf_status) stat;
}
mf_status
@@ -228,312 +41,20 @@ getmx(char *host, mxbuf_t mxbuf)
{
mf_status rc = dns_cache_get(T_MX, host, mxbuf, MAXMXCOUNT);
if (!mf_resolved(rc)) {
+ dns_status status;
unsigned long ttl;
size_t mxcnt;
- rc = get_mx_records(host, 1, mxbuf, &mxcnt, &ttl);
- if (rc == mf_success)
+
+ status = dns_get_mx_records(host, 1, mxbuf, &mxcnt, &ttl);
+ if (status == dns_success)
dns_cache_put(T_MX, host, ttl, mxbuf, mxcnt);
else
dns_cache_put(T_MX, host, 0, NULL, 0);
+ rc = dns_to_mf_status(status);
}
return rc;
}
-void
-freemx(mxbuf_t mxbuf)
-{
- int i;
- for (i = 0; i < MAXMXCOUNT && mxbuf[i]; i++)
- free(mxbuf[i]);
-}
-
-#define MAXCNAMEDEPTH 24 /* FIXME */
-
-#define LOOKUP_FAILURE -1
-#define LOOKUP_SUCCESS 0
-#define LOOKUP_CNAME 1
-
-struct loop_data {
- int qtype;
- char *name;
- size_t name_size;
- char *domain;
- size_t domain_size;
- char *answer;
- size_t answer_size;
- char *hbuf;
- size_t hbsize;
- time_t ttl;
- mf_status status;
- size_t loopcnt;
- int atype;
-};
-
-#define NSIZE MAX(MAXPACKET, MAXDNAME*2+2)
-#define SET_STATUS(lp,s) if ((lp)->status != mf_success) (lp)->status = s
-
-typedef struct {
- GACOPYZ_UINT32_T x;
- char a;
-} align;
-
-static int
-domain_name_cmp(const char *ptr, const char *name, const char *domain)
-{
- int c;
- while (*name)
- if (c = *ptr++ - *name++)
- return c;
- if (*domain && *ptr != '.')
- return *ptr - *domain;
- ptr++;
- while (*domain)
- if (c = *ptr++ - *domain++)
- return c;
- return 0;
-}
-
-int
-cnameloop(struct loop_data *lp)
-{
- int i, n, rc;
- HEADER *hp;
- unsigned char *eom, *cp;
- unsigned short qdcount, ancount;
- char *p;
- size_t len;
- struct __res_state statb;
- char *hap = lp->hbuf; /*FIXME*/
-
- memset((void *)&statb, 0, sizeof(struct __res_state));
- res_ninit(&statb);
- n = res_nquerydomain (&statb, lp->name, lp->domain, C_IN, lp->qtype,
- lp->answer, lp->answer_size);
- res_nclose(&statb);
- if (n < 0) {
- debug2(10, "res_nquerydomain failed (errno=%s, h_errno=%d)",
- mu_strerror(errno), h_errno);
- switch (h_errno) {
- case NO_DATA:
- case NO_RECOVERY:
- case HOST_NOT_FOUND:
- SET_STATUS(lp, mf_not_found);
- return LOOKUP_FAILURE;
-
- case TRY_AGAIN:
- case -1:
- SET_STATUS(lp, mf_temp_failure);
- return LOOKUP_FAILURE;
-
- default:
- mu_error("res_nquerydomain(%s) failed with unexpected h_errno %d",
- lp->name, h_errno);
- SET_STATUS(lp, mf_failure);
- return LOOKUP_FAILURE;
- }
- }
-
- if (n > lp->answer_size)
- n = lp->answer_size;
-
- hp = (HEADER*) lp->answer;
- cp = (unsigned char *) lp->answer + HFIXEDSZ;
- eom = (unsigned char *) lp->answer + n;
-
- /* Skip query part */
- for (qdcount = ntohs((unsigned short)hp->qdcount);
- qdcount--;
- cp += n + QFIXEDSZ) {
- if ((n = dn_skipname(cp, eom)) < 0) {
- SET_STATUS(lp, mf_failure);
- return LOOKUP_FAILURE;
- }
- }
-
- for (ancount = ntohs((unsigned short) hp->ancount), i = 0;
- i < ancount && cp < eom;
- cp += n, i++) {
- unsigned short type;
- unsigned long ttl = ~(unsigned long)0;
- char nbuf[NSIZE];
- char *bp = nbuf;
- size_t blen = sizeof nbuf;
- size_t l;
-
- n = dn_expand((unsigned char *) lp->answer, eom, cp,
- nbuf, sizeof nbuf);
- if (n < 0)
- break;
- cp += n;
- GETSHORT(type, cp);
- cp += INT16SZ; /* skip over class */
- GETLONG(ttl, cp);
- if (lp->ttl > ttl)
- lp->ttl = ttl;
- GETSHORT(n, cp); /* rdlength */
- switch (type) {
- case T_A:
- if (domain_name_cmp(bp, lp->name, lp->domain) != 0) {
- cp += n;
- continue;
- }
- /* Skip host name */
- l = strlen(bp) + 1;
- bp += l;
- blen -= l;
-
- blen -= sizeof(align) - ((u_long)bp % sizeof(align));
- bp += sizeof(align) - ((u_long)bp % sizeof(align));
-
- if (bp + n >= nbuf + blen) {
- printf("size (%d) too big\n", n);
- continue;
- }
- /* FIXME: This is not ncessary now that we retrieve
- only the first A record, but will be necessary in
- the future, when the entire array of A records will
- be returned */
- if (hap >= lp->hbuf + lp->hbsize) {
- printf("Too many addresses\n");
- cp += n;
- continue;
- }
- memmove(hap, cp, n);
- bp += n;
- blen -= n;
- cp += n;
- hap += n;
- lp->atype = T_A;
- SET_STATUS(lp, mf_success);
- return LOOKUP_SUCCESS;
-
- case T_PTR:
- if ((rc = dn_expand((unsigned char *)lp->answer,
- eom, cp,
- nbuf, sizeof(nbuf))) < 0) {
- SET_STATUS(lp, mf_failure);
- return LOOKUP_FAILURE;
- }
-
- strncpy(lp->hbuf, nbuf, lp->hbsize);
- lp->hbuf[lp->hbsize-1] = 0;
- lp->atype = T_PTR;
- SET_STATUS(lp, mf_success);
- return LOOKUP_SUCCESS;
-
- case T_CNAME:
- if (--lp->loopcnt == 0) {
- mu_error ("DNS failure: CNAME loop for %s",
- lp->name);
- SET_STATUS(lp, mf_failure);
- return LOOKUP_FAILURE;
- }
-
- if ((rc = dn_expand((unsigned char *)lp->answer,
- eom, cp,
- nbuf, sizeof(nbuf))) < 0) {
- SET_STATUS(lp, mf_failure);
- return LOOKUP_FAILURE;
- }
-
- strncpy(lp->hbuf, nbuf, lp->hbsize);
- lp->hbuf[lp->hbsize-1] = 0;
- /* RFC 1034 section 3.6 specifies that CNAME
- should point at the canonical name -- but
- urges software to try again anyway.
- */
- p = strchr (lp->hbuf, '.');
- if (!p)
- return LOOKUP_SUCCESS;
- len = p - lp->hbuf;
- if (len + 1 >= lp->name_size)
- return LOOKUP_FAILURE;
- memcpy(lp->name, lp->hbuf, len);
- lp->name[len] = 0;
- len = strlen (p + 1);
- if (len + 1 >= lp->domain_size)
- return LOOKUP_FAILURE;
- strcpy(lp->domain, p + 1);
- lp->atype = T_CNAME;
- SET_STATUS(lp, mf_success);
- return LOOKUP_CNAME;
- }
- }
- SET_STATUS(lp, mf_not_found);
- return LOOKUP_FAILURE;
-}
-
-mf_status
-_resolve_ipstr(const char *ipstr, const char *domain,
- char *answer, size_t answer_size,
- char *hbuf, size_t hbsize, unsigned long *ttl)
-{
- int i;
- char revipstr[3*4+3+1];
- char namebuf[NSIZE];
- char domainbuf[NSIZE];
- const char *p;
- char *q;
- struct loop_data ld;
-
- ld.qtype = T_ANY;
-
- if (!domain) {
- q = revipstr + strlen (ipstr);
- *q = 0;
- for (i = 0, p = ipstr; *p && i < 4; i++) {
- int len;
-
- for (len = 0; p[len] && p[len] != '.'; len++)
- ;
- q -= len;
- memcpy(q, p, len);
- if (q > revipstr)
- *--q = '.';
- p += len;
- if (*p == '.')
- p++;
- }
-
- if (*p || i != 4)
- return mf_failure;
-
- ld.name = namebuf;
- ld.name_size = sizeof namebuf;
- strcpy(namebuf, revipstr);
- } else {
- strncpy(namebuf, ipstr, sizeof(namebuf)-1);
- namebuf[sizeof(namebuf)-1] = 0;
- ld.name = namebuf;
- ld.name_size = strlen(ld.name);
- }
-
- ld.domain = domainbuf;
- strcpy(domainbuf, domain ? domain : "in-addr.arpa");
- ld.domain_size = sizeof domainbuf;
- ld.answer = answer;
- ld.answer_size = answer_size;
- ld.hbuf = hbuf;
- ld.hbsize = hbsize;
- ld.ttl = ~(unsigned long)0;
- ld.status = mf_failure;
- ld.loopcnt = MAXCNAMEDEPTH;
-
- while (cnameloop(&ld) == LOOKUP_CNAME)
- ;
-
- if (ld.status == mf_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);
- }
-
- *ttl = ld.ttl;
- return ld.status;
-}
-
mf_status
resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf)
{
@@ -542,7 +63,7 @@ resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf)
char *hbuf;
if (!answer)
- answer = malloc(MAXPACKET);
+ answer = xmalloc(MAXPACKET);
debug1(80,"Getting canonical name for %s", ipstr);
status = dns_cache_get(T_PTR, ipstr, &hbuf, 1);
@@ -554,23 +75,24 @@ resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf)
} else {
unsigned long ttl;
char buffer[256];
+ dns_status dstat;
- MUTEX_LOCK(mf_mutex);
- status = _resolve_ipstr(ipstr, domain, answer, MAXPACKET,
- buffer, sizeof buffer, &ttl);
- MUTEX_UNLOCK(mf_mutex);
- switch (status) {
- case mf_success:
+ dstat = dns_resolve_ipstr(ipstr, domain, answer, MAXPACKET,
+ buffer, sizeof buffer, &ttl);
+
+ switch (dstat) {
+ case dns_success:
debug2(80, "%s resolved to %s", ipstr, buffer);
*phbuf = xstrdup(buffer);
dns_cache_put(T_PTR, ipstr, ttl, phbuf, 1);
break;
- case mf_not_found:
+ case dns_not_found:
dns_cache_put(T_PTR, ipstr, 0, NULL, 0);
default:
debug1(80, "%s not resolved", ipstr);
}
+ status = dns_to_mf_status(dstat);
}
return status;
}
@@ -582,41 +104,6 @@ resolve_ipstr(const char *ipstr, char **phbuf)
}
mf_status
-_resolve_hostname(char *host, char *answer, size_t answer_size,
- char *ipbuf, size_t ipbsize, unsigned long *ttl)
-{
- struct loop_data ld;
- char domainbuf[256] = "";
-
- ld.qtype = T_A;
- ld.name = host;
- ld.name_size = strlen(host);
- ld.domain = domainbuf;
- ld.domain_size = sizeof domainbuf;
- ld.answer = answer;
- ld.answer_size = answer_size;
- ld.hbuf = ipbuf;
- ld.hbsize = ipbsize;
- ld.ttl = ~(unsigned long)0;
- ld.status = mf_failure;
- ld.loopcnt = MAXCNAMEDEPTH;
-
- while (cnameloop(&ld) == LOOKUP_CNAME)
- ;
-
- if (ld.status == mf_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);
- }
-
- *ttl = ld.ttl;
- return ld.status;
-}
-
-mf_status
resolve_hostname(const char *host, char **pipbuf)
{
mf_status status;
@@ -636,27 +123,26 @@ resolve_hostname(const char *host, char **pipbuf)
} else {
unsigned long ttl;
char *tmphost = xstrdup(host);
+ dns_status dstat;
- MUTEX_LOCK(mf_mutex);
- status = _resolve_hostname(tmphost, answer, MAXPACKET,
- buffer, sizeof buffer,
- &ttl);
- MUTEX_UNLOCK(mf_mutex);
+ dstat = dns_resolve_hostname(tmphost, answer, MAXPACKET,
+ buffer, sizeof buffer,
+ &ttl);
free(tmphost);
- switch (status) {
- case mf_success:
+ switch (dstat) {
+ case dns_success:
ipbuf = xstrdup(buffer);
debug2(80, "%s resolved to %s", host, ipbuf);
*pipbuf = ipbuf;
dns_cache_put(T_A, host, ttl, &ipbuf, 1);
break;
- case mf_not_found:
+ case dns_not_found:
dns_cache_put(T_A, host, 0, NULL, 0);
default:
debug1(80, "%s not resolved", host);
}
+ status = dns_to_mf_status(dstat);
}
return status;
}
-
diff --git a/src/dns.h b/src/dns.h
new file mode 100644
index 00000000..7b7fd7b4
--- /dev/null
+++ b/src/dns.h
@@ -0,0 +1,95 @@
+/* This file is part of mailfromd.
+ Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+typedef enum {
+ dns_success,
+ dns_not_found,
+ dns_failure,
+ dns_temp_failure,
+} dns_status;
+
+#define NELEMS(c) (sizeof(c)/sizeof((c)[0]))
+
+/* Libresolv-related defines and DNS functions */
+#ifndef MAXPACKET
+# define MAXPACKET 8192 /* max packet size used internally by BIND */
+#endif
+
+#define MAXMXCOUNT 32
+typedef char *mxbuf_t[MAXMXCOUNT];
+
+#define MAXCNAMEDEPTH 24 /* FIXME */
+
+#ifdef HAVE_PTHREAD
+# define MUTEX_DCL(name) \
+ static pthread_mutex_t name = PTHREAD_MUTEX_INITIALIZER;
+# define MUTEX_LOCK(name) \
+ pthread_mutex_lock(&name)
+# define MUTEX_UNLOCK(name) \
+ pthread_mutex_unlock(&name);
+#else
+# define MUTEX_DCL(name)
+# define MUTEX_LOCK(name)
+# define MUTEX_UNLOCK(name)
+#endif
+
+#if !defined res_ninit && !defined HAVE_RES_NINIT
+# define res_ninit(s)
+#endif
+
+#if !defined res_nclose && !defined HAVE_RES_NCLOSE
+# define res_nclose(s)
+#endif
+
+#if !defined res_nquery && !defined HAVE_RES_NQUERY
+# define res_nquery(stat,name,class,type,answer,anslen) \
+ res_query(name,class,type,answer,anslen)
+#endif
+
+#if !defined res_nquerydomain && !defined HAVE_RES_NQUERYDOMAIN
+# define res_nquerydomain(stat,name,domain,class,type,answer,anslen) \
+ res_querydomain(name,domain,class,type,answer,anslen)
+#endif
+
+int dns_str_is_ipv4(char *addr);
+dns_status dns_get_mx_records(char *host, int maxdepth, mxbuf_t mxbuf,
+ size_t *mxcount, unsigned long *ttl);
+void dns_freemx(mxbuf_t mxbuf);
+
+int dns_reverse_ipstr(const char *ipstr, char *revipstr);
+
+dns_status dns_resolve_ipstr(const char *ipstr, const char *domain,
+ char *answer, size_t answer_size,
+ char *hbuf, size_t hbsize, unsigned long *ttl);
+
+dns_status dns_resolve_hostname(char *host, char *answer, size_t answer_size,
+ char *ipbuf, size_t ipbsize,
+ unsigned long *ttl);
+dns_status a_lookup(char *host,
+ GACOPYZ_UINT32_T *ipbuf, size_t ipbsize, size_t *ipcount,
+ unsigned long *ttl, char *answer, size_t answer_size);
+
+dns_status ptr_lookup(struct in_addr ip,
+ char **names, size_t maxnames, unsigned long *ttl,
+ char *answer, size_t answer_size);
+dns_status txt_lookup(char *name,
+ char **names, size_t maxnames, unsigned long *ttl,
+ char *answer, size_t answer_size);
+
+dns_status ptr_validate(const char *ipstr, char ***vnptr, size_t *vcount);
+
diff --git a/src/dnsbase.c b/src/dnsbase.c
new file mode 100644
index 00000000..49c86f03
--- /dev/null
+++ b/src/dnsbase.c
@@ -0,0 +1,789 @@
+/* This file is part of mailfromd.
+ Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+#include "mailfrom.h"
+
+struct mx_buffer {
+ unsigned pref;
+ char *name;
+};
+
+static int
+comp_pref(const void *a, const void *b)
+{
+ const struct mx_buffer *ma = a, *mb = b;
+ if (ma->pref > mb->pref)
+ return 1;
+ else if (ma->pref < mb->pref)
+ return -1;
+ return 0;
+}
+
+/* Obtain MX records for domain name HOST. Return them in mxbuf, sorted by
+ preference in ascending order.
+ Notice that we *have* to use old BIND-4-style method, since glibc folks
+ in their incredible wiseness have not exported new interface functions
+ from libresolve.so. The only way to access them would be to link with
+ -lresolve statically.
+
+ Let's hope that someday this stupidity will change. */
+
+static dns_status
+_getmx(char *host, char *answer, size_t answer_size, mxbuf_t mxbuf,
+ size_t *pcount, unsigned long *pttl)
+{
+ int i, n, nmx;
+ struct mx_buffer mx_buffer[MAXMXCOUNT];
+ HEADER *hp;
+ unsigned char *eom, *cp;
+ unsigned short qdcount, ancount;
+ struct __res_state stat;
+
+ debug1(80,"Getting MX records for %s", host);
+ memset((void *)&stat, 0, sizeof(struct __res_state));
+ res_ninit(&stat);
+ n = res_nquery (&stat, host, C_IN, T_MX, answer, answer_size);
+ res_nclose(&stat);
+ if (n < 0) {
+ debug2(10, "res_nquery failed (errno=%s, h_errno=%d)",
+ mu_strerror(errno), h_errno);
+ switch (h_errno) {
+ case NO_DATA:
+ case NO_RECOVERY:
+ case HOST_NOT_FOUND:
+ return dns_not_found;
+
+ case TRY_AGAIN:
+ case -1:
+ return dns_temp_failure;
+
+ default:
+ mu_error("res_nquery(%s) failed with unexpected h_errno %d",
+ host, h_errno);
+ return dns_failure;
+ }
+ }
+
+ if (n > answer_size)
+ n = answer_size;
+
+ hp = (HEADER*)answer;
+ cp = (unsigned char *) answer + HFIXEDSZ;
+ eom = (unsigned char *) answer + n;
+
+ /* Skip query part */
+ for (qdcount = ntohs((unsigned short)hp->qdcount);
+ qdcount--;
+ cp += n + QFIXEDSZ) {
+ if ((n = dn_skipname(cp, eom)) < 0)
+ return dns_failure;
+ }
+
+ ancount = ntohs((unsigned short)hp->ancount);
+
+ /* Collect MX records */
+ for (i = nmx = 0; i < ancount; i++) {
+ unsigned short pref, type;
+ char tname[NS_MAXDNAME];
+ unsigned long ttl;
+
+ if ((n = dn_expand((unsigned char *)answer,
+ eom, cp, tname, sizeof tname)) < 0)
+ break;
+ cp += n;
+ GETSHORT(type, cp);
+ cp += INT16SZ;
+ GETLONG(ttl, cp);
+ if (*pttl > ttl)
+ *pttl = ttl;
+ GETSHORT(n, cp);
+ if (type != T_MX) {
+ debug2(90,"unexpected answer type %d, size %d\n",
+ type, n);
+ cp += n;
+ continue;
+ }
+ GETSHORT(pref, cp);
+ if ((n = dn_expand((u_char *)answer, eom, cp,
+ tname, sizeof tname)) < 0)
+ break;
+ cp += n;
+ mx_buffer[nmx].pref = pref;
+ mx_buffer[nmx].name = strdup(tname);
+ debug2(20,"MX %u %s", mx_buffer[nmx].pref,
+ mx_buffer[nmx].name);
+ if (++nmx >= MAXMXCOUNT)
+ break;
+ }
+
+ /* Sort according to preference value */
+ qsort(mx_buffer, nmx, sizeof mx_buffer[0], comp_pref);
+
+ /* Prepare return value */
+ memset(mxbuf, 0, sizeof(mxbuf_t));
+ for (i = 0; i < nmx; i++)
+ mxbuf[i] = mx_buffer[i].name;
+ *pcount = nmx;
+ return dns_success;
+}
+
+int
+dns_str_is_ipv4(char *addr)
+{
+ int dot_count;
+ int digit_count;
+
+ dot_count = 0;
+ digit_count = 0;
+ while (*addr != 0 && *addr != ' ') {
+ if (*addr == '.') {
+ if (++dot_count > 3)
+ break;
+ digit_count = 0;
+ } else if (!(isdigit(*addr) && ++digit_count <= 3)) {
+ return 0;
+ }
+ addr++;
+ }
+
+ return dot_count == 3;
+}
+
+MUTEX_DCL(dns_mutex)
+
+/* Return MX records for the given HOST. If no records were found, recurse
+ to its parent domains until any record is found or recursion depth reaches
+ MAXDEPTH */
+dns_status
+dns_get_mx_records(char *host, int maxdepth, mxbuf_t mxbuf, size_t *mxcount,
+ unsigned long *ttl)
+{
+ char *hbuf = NULL;
+ dns_status status = dns_failure;
+
+ MUTEX_LOCK(dns_mutex);
+ if (dns_str_is_ipv4(host)) {