diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-02-17 12:56:00 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-02-17 12:56:00 +0000 |
commit | ad4e496b8acfbcaf1ba6aabd2ba8b508f196361d (patch) | |
tree | 5960b99cbae5352562b8aa1230119f4702241278 /src | |
parent | 4cb6c6dd32298e583c45c49fce63d452bb903f27 (diff) | |
download | mailfromd-ad4e496b8acfbcaf1ba6aabd2ba8b508f196361d.tar.gz mailfromd-ad4e496b8acfbcaf1ba6aabd2ba8b508f196361d.tar.bz2 |
Begin implementing SPF support
git-svn-id: file:///svnroot/mailfromd/trunk@1250 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/bi_dns.m4 | 4 | ||||
-rw-r--r-- | src/bi_spf.m4 | 48 | ||||
-rw-r--r-- | src/dns.c | 564 | ||||
-rw-r--r-- | src/dns.h | 95 | ||||
-rw-r--r-- | src/dnsbase.c | 789 | ||||
-rw-r--r-- | src/engine.c | 2 | ||||
-rw-r--r-- | src/mailfrom.h | 55 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/prog.c | 2 | ||||
-rw-r--r-- | src/spf.c | 810 | ||||
-rw-r--r-- | src/spf.h | 37 |
12 files changed, 1822 insertions, 591 deletions
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 @@ -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)) { + status = resolve_ipstr(host, &hbuf); + if (status != dns_success) + host = NULL; + } + + if (host) { + unsigned char *answer = malloc(MAXPACKET); + if (!answer) + status = dns_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 == dns_success + || status == dns_temp_failure) + break; + p = strchr(p, '.'); + if (!p) + break; + } + free(answer); + } + } + MUTEX_UNLOCK(dns_mutex); + free(hbuf); + return status; +} + +void +dns_freemx(mxbuf_t mxbuf) +{ + int i; + for (i = 0; i < MAXMXCOUNT && mxbuf[i]; i++) + free(mxbuf[i]); +} + +#define LOOKUP_FAILURE -1 +#define LOOKUP_SUCCESS 0 +#define LOOKUP_CNAME 1 + +struct loop_data { + int qtype; /* Type of the query */ + char *name; /* Key to look up */ + size_t name_size; /* Length of the key */ + char *domain; /* Domain name */ + size_t domain_size; /* Length of the domain name */ + char *answer; /* Answer buffer */ + size_t answer_size; /* Size of answer buffer |