/* This file is part of Mailfromd. -*- c -*- Copyright (C) 2006-2020 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 . */ MF_BUILTIN_MODULE #include #include #include #include #include "srvcfg.h" #include "global.h" 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) { size_t i; dns_status dnstat; struct dns_reply r; dnstat = a_lookup(string, &r); switch (dnstat) { case dns_success: { MF_OBSTACK_BEGIN(); qsort(r.data.ip, r.count, sizeof r.data.ip[0], ipaddr_cmp); for (i = 0; i < r.count; i++) { struct in_addr addr; char *q; addr.s_addr = r.data.ip[i]; q = inet_ntoa(addr); if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(q); } dns_reply_free(&r); MF_OBSTACK_1GROW(0); MF_RETURN_OBSTACK(); } case dns_not_found: MF_RETURN(""); default: MF_THROW(mf_status_to_exception(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; struct dns_reply r; MF_ASSERT(inet_aton(ipstr, &addr), mfe_invip, _("invalid IP: %s"), ipstr); dnstat = ptr_lookup(addr, &r); switch (dnstat) { case dns_success: { size_t i; qsort(r.data.str, r.count, sizeof r.data.str[0], hostname_cmp); MF_OBSTACK_BEGIN(); for (i = 0; i < r.count; i++) { if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW((char*)r.data.str[i]); } MF_OBSTACK_1GROW(0); dns_reply_free(&r); MF_RETURN_OBSTACK(); } case dns_not_found: MF_RETURN(""); default: MF_THROW(mf_status_to_exception(dns_to_mf_status(dnstat)), _("failed to get PTR record for %s"), ipstr); } } END MF_DEFUN(primitive_hasmx, NUMBER, STRING string) { struct dns_reply repl; mf_status mxstat; mxstat = dns_to_mf_status(mx_lookup(string, 0, &repl)); MF_ASSERT(mxstat == mf_success || mxstat == mf_not_found, mf_status_to_exception(mxstat), _("cannot get MX records for %s"), string); dns_reply_free(&repl); if (mxstat == mf_success) { MF_RETURN(1); } MF_RETURN(0); } END MF_DEFUN(getmx, STRING, STRING domain, OPTIONAL, NUMBER resolve) { mf_status mxstat; struct dns_reply reply; int i; mxstat = dns_to_mf_status(mx_lookup(domain, MF_OPTVAL(resolve), &reply)); if (!mf_resolved(mxstat)) { MF_THROW(mf_status_to_exception(mxstat), _("cannot get MX records for %s"), domain); } if (mxstat == mf_not_found) { MF_RETURN(""); } else if (reply.type == dns_reply_ip) { MF_OBSTACK_BEGIN(); for (i = 0; i < reply.count; i++) { struct in_addr s; s.s_addr = reply.data.ip[i]; if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(inet_ntoa(s)); } } else { MF_OBSTACK_BEGIN(); for (i = 0; i < reply.count; i++) { if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(reply.data.str[i]); } } MF_OBSTACK_1GROW(0); dns_reply_free(&reply); MF_RETURN_OBSTACK(); } END static dns_status resolve_host(const char *string, struct dns_reply *reply) { struct in_addr addr; if (inet_aton(string, &addr)) { dns_reply_init(reply, dns_reply_ip, 1); reply->data.ip[0] = addr.s_addr; return dns_success; } return a_lookup(string, reply); } static int dns_replies_intersect(struct dns_reply const *a, struct dns_reply const *b) { int i, j; if (a->type == b->type && a->type == dns_reply_ip) { for (i = 0; i < a->count; i++) for (j = 0; j < b->count; j++) if (a->data.ip[i] == b->data.ip[j]) return 1; } return 0; } MF_DEFUN(primitive_ismx, NUMBER, STRING domain, STRING ipstr) { struct dns_reply areply, mxreply; dns_status status; int rc = 0; status = resolve_host(ipstr, &areply); MF_ASSERT(status == dns_success, mf_status_to_exception(dns_to_mf_status(status)), _("cannot resolve host name %s"), ipstr); status = mx_lookup(domain, 1, &mxreply); if (status != dns_success) { dns_reply_free(&areply); MF_THROW(mf_status_to_exception(dns_to_mf_status(status)), _("cannot get MXs for %s"), domain); } rc = dns_replies_intersect(&areply, &mxreply); dns_reply_free(&areply); dns_reply_free(&mxreply); MF_RETURN(rc); } END MF_DEFUN(relayed, NUMBER, STRING s) { MF_RETURN(relayed_domain_p(s)); } END MF_DEFUN(ptr_validate, NUMBER, STRING s) { int rc, res; switch (rc = ptr_validate(s, NULL)) { case dns_success: res = 1; break; case dns_not_found: res = 0; break; default: MF_THROW(mf_status_to_exception(dns_to_mf_status(rc)), _("failed to get PTR record for %s"), s); } MF_RETURN(res); } END MF_DEFUN(primitive_hasns, NUMBER, STRING dom) { struct dns_reply repl; mf_status stat = dns_to_mf_status(ns_lookup(dom, 0, &repl)); MF_ASSERT(stat == mf_success || stat == mf_not_found, mf_status_to_exception(stat), _("cannot get NS records for %s"), dom); dns_reply_free(&repl); if (stat == mf_success) { MF_RETURN(1); } MF_RETURN(0); } END static int cmp_ip(void const *a, void const *b) { GACOPYZ_UINT32_T ipa = *(GACOPYZ_UINT32_T const *)a; GACOPYZ_UINT32_T ipb = *(GACOPYZ_UINT32_T const *)b; if (ipa < ipb) return -1; if (ipa > ipb) return 1; return 0; } static int cmp_str(void const *a, void const *b) { char * const *stra = a; char * const *strb = b; return strcmp(*stra, *strb); } MF_DEFUN(getns, STRING, STRING domain, OPTIONAL, NUMBER resolve, NUMBER sort) { mf_status stat; struct dns_reply reply; int i; stat = dns_to_mf_status(ns_lookup(domain, MF_OPTVAL(resolve), &reply)); if (!mf_resolved(stat)) { MF_THROW(mf_status_to_exception(stat), _("cannot get MX records for %s"), domain); } if (stat == mf_not_found) { MF_RETURN(""); } MF_OBSTACK_BEGIN(); if (reply.type == dns_reply_ip) { if (sort) qsort(reply.data.ip, reply.count, sizeof(reply.data.ip[0]), cmp_ip); for (i = 0; i < reply.count; i++) { struct in_addr s; s.s_addr = reply.data.ip[i]; if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(inet_ntoa(s)); } } else { if (sort) qsort(reply.data.str, reply.count, sizeof(reply.data.str[0]), cmp_str); for (i = 0; i < reply.count; i++) { if (i > 0) MF_OBSTACK_1GROW(' '); MF_OBSTACK_GROW(reply.data.str[i]); } } MF_OBSTACK_1GROW(0); dns_reply_free(&reply); MF_RETURN_OBSTACK(); } END