/* This file is part of mailfromd. -*- c -*- Copyright (C) 2006, 2007, 2008 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 3, 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, see . */ #include #include #include #include MF_DEFUN(primitive_hostname, STRING, STRING string) { char *hbuf; mf_status stat; stat = resolve_ipstr(string, &hbuf); MF_ASSERT(stat == mf_success, mf_status_to_exception(stat), _("Cannot resolve IP %s"), string); pushs(env, hbuf); free(hbuf); } END MF_DEFUN(primitive_resolve, STRING, STRING string, OPTIONAL, STRING domain) { char *ipstr; mf_status stat; if (MF_OPTVAL(domain,"")[0]) { stat = resolve_ipstr_domain(string, domain, &ipstr); MF_ASSERT(stat == mf_success, mf_status_to_exception(stat), _("Cannot resolve %s.%s"), string, domain); } else { stat = resolve_hostname(string, &ipstr); MF_ASSERT(stat == mf_success, mf_status_to_exception(stat), _("Cannot resolve %s"), string); } pushs(env, ipstr); free(ipstr); } END static int ipaddr_cmp(const void *a, const void *b) { GACOPYZ_UINT32_T ipa = ntohl(*(GACOPYZ_UINT32_T*)a); GACOPYZ_UINT32_T ipb = ntohl(*(GACOPYZ_UINT32_T*)b); if (ipa < ipb) return -1; if (ipa > ipb) return 1; return 0; } MF_DEFUN(dns_getaddr, STRING, STRING string) { GACOPYZ_UINT32_T ipbuf[64]; /* FIXME: arbitrary limit */ size_t i, ipcount; unsigned long ttl; dns_status dnstat; dnstat = a_lookup(string, ipbuf, NELEMS(ipbuf), &ipcount, &ttl, NULL, 0); switch (dnstat) { case dns_success: { MF_OBSTACK_BEGIN(); qsort(ipbuf, ipcount, sizeof ipbuf[0], ipaddr_cmp); for (i = 0; i < ipcount; i++) { struct in_addr addr; char *q; addr.s_addr = ipbuf[i]; q = inet_ntoa(addr); if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(q); } MF_OBSTACK_1GROW(0); MF_RETURN_OBSTACK(); } case dns_not_found: MF_RETURN_STRING(""); default: MF_THROW(dns_to_mf_status(dnstat), _("Failed to get A record for %s"), string); } } END static int hostname_cmp(const void *a, const void *b) { return strcmp(*(const char**) a, *(const char**) b); } MF_DEFUN(dns_getname, STRING, STRING ipstr) { dns_status dnstat; struct in_addr addr; unsigned long ttl; char *names[64]; MF_ASSERT(inet_aton(ipstr, &addr), mfe_invip, _("Invalid IP: %s"), ipstr); dnstat = ptr_lookup(addr, names, NELEMS(names), &ttl, NULL, 0); switch (dnstat) { case dns_success: { size_t i; size_t ncount; for (ncount = 0; ncount < NELEMS(names) && names[ncount]; ncount++); qsort(names, ncount, sizeof names[0], hostname_cmp); for (i = 0; i < ncount; i++) { if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(names[i]); } MF_OBSTACK_1GROW(0); for (; i < ncount; i++) free(names[i]); MF_RETURN_OBSTACK(); } case dns_not_found: MF_RETURN_STRING(""); default: MF_THROW(dns_to_mf_status(dnstat), _("Failed to get PTR record for %s"), ipstr); } } END MF_DEFUN(primitive_hasmx, NUMBER, STRING string) { mxbuf_t mxbuf; mf_status mxstat; mxstat = getmx(string, mxbuf); MF_ASSERT(mxstat == mf_success || mxstat == mf_not_found, mf_status_to_exception(mxstat), _("Cannot get MX records for %s"), string); if (mxstat == mf_success) { dns_freemx(mxbuf); MF_RETURN(1); } MF_RETURN(0); } END MF_DEFUN(getmx, STRING, STRING domain, OPTIONAL, NUMBER resolve) { mxbuf_t mxbuf; mf_status mxstat; if (MF_OPTVAL(resolve)) mxstat = getmxip(domain, mxbuf); else mxstat = getmx(domain, mxbuf); MF_ASSERT(mxstat == mf_success || mxstat == mf_not_found, mf_status_to_exception(mxstat), _("Cannot get MX records for %s"), domain); if (mxstat == mf_not_found) MF_RETURN_STRING(""); else { int i; MF_OBSTACK_BEGIN(); for (i = 0; i < MAXMXCOUNT && mxbuf[i]; i++) { if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(mxbuf[i]); } MF_OBSTACK_1GROW(0); dns_freemx(mxbuf); MF_RETURN_OBSTACK(); } } END static int resolve_host(const char *string, unsigned long *ip) { int rc; struct in_addr addr; char *ipstr; if (inet_aton(string, &addr)) { *ip = addr.s_addr; return 0; } if (resolve_hostname(string, &ipstr) != mf_success) return 1; rc = inet_aton(ipstr, &addr); free(ipstr); if (rc == 0) { mu_error(_("INTERNAL ERROR at %s:%d: resolve_hostname returned " "invalid IP address: %s"), __FILE__, __LINE__, ipstr); return 1; } *ip = addr.s_addr; return 0; } MF_DEFUN(primitive_ismx, NUMBER, STRING domain, STRING ipstr) { mxbuf_t mxbuf; mf_status mxstat; unsigned long ip; int rc = 0; int i; MF_ASSERT(resolve_host(ipstr, &ip) == 0, mfe_noresolve, _("Cannot resolve host name %s"), ipstr); mxstat = getmx(domain, mxbuf); MF_ASSERT(mxstat == mf_success, mf_status_to_exception(mxstat), _("Cannot get MXs for %s"), domain); for (i = 0; i < MAXMXCOUNT && mxbuf[i]; i++) { unsigned long n; if (!resolve_host(mxbuf[i], &n) && n == ip) { rc = 1; break; } } dns_freemx(mxbuf); MF_RETURN(rc); } END MF_DEFUN(relayed, NUMBER, STRING s) { MF_RETURN(relayed_domain_p(s)); } END MF_DEFUN(listens, NUMBER, STRING s, OPTIONAL, NUMBER port) { MF_RETURN(listens_on (s, MF_OPTVAL(port, 25)) == mf_success); } END MF_DEFUN(domainpart, STRING, STRING str) { char *p = strchr(str, '@'); MF_RETURN_STRING(p ? p+1 : str); } END MF_DEFUN(localpart, STRING, STRING str) { char *p = strchr(str, '@'); if (p) { size_t off; size_t size = p - str; char *string_space = MF_ALLOC_HEAP(off, size + 1); memcpy(string_space, str, size); string_space[size] = 0; MF_RETURN(off); } else MF_RETURN_STRING(str); } END MF_INIT