/* This file is part of Mailfromd. -*- c -*-
Copyright (C) 2006-2011, 2015-2017 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
#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;
unsigned long ttl;
dns_status dnstat;
struct dns_reply r;
dnstat = a_lookup(string, &r, &ttl);
switch (dnstat) {
case dns_success: {
MF_OBSTACK_BEGIN();
qsort(r.base, r.count, sizeof r.base[0], ipaddr_cmp);
for (i = 0; i < r.count; i++) {
struct in_addr addr;
char *q;
addr.s_addr = dns_reply_ip(&r, 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;
unsigned long ttl;
struct dns_reply r;
MF_ASSERT(inet_aton(ipstr, &addr),
mfe_invip,
_("invalid IP: %s"), ipstr);
dnstat = ptr_lookup(addr, &r, &ttl);
switch (dnstat) {
case dns_success: {
size_t i;
qsort(r.base, r.count, sizeof r.base[0], hostname_cmp);
MF_OBSTACK_BEGIN();
for (i = 0; i < r.count; i++) {
if (i > 0)
MF_OBSTACK_1GROW(' ');
MF_OBSTACK_GROW((char*)r.base[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 mxbuf mxbuf;
mf_status mxstat;
mxbuf.mx_flags = 0;
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);
mxbuf_free(&mxbuf);
if (mxstat == mf_success) {
MF_RETURN(1);
}
MF_RETURN(0);
}
END
MF_DEFUN(getmx, STRING, STRING domain, OPTIONAL, NUMBER no_resolve)
{
mf_status mxstat;
if (MF_OPTVAL(no_resolve)) {
GACOPYZ_UINT32_T *ipbuf;
size_t ipcount;
mxstat = getmxip(domain, &ipbuf, &ipcount);
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 {
int i;
MF_OBSTACK_BEGIN();
for (i = 0; i < ipcount; i++) {
struct in_addr s;
s.s_addr = htonl(ipbuf[i]);
if (i > 0)
MF_OBSTACK_1GROW(' ');
MF_OBSTACK_GROW(inet_ntoa(s));
}
free(ipbuf);
MF_OBSTACK_1GROW(0);
MF_RETURN_OBSTACK();
}
} else {
struct mxbuf mxbuf;
mxstat = getmx(domain, &mxbuf);
if (!mf_resolved(mxstat)) {
mxbuf_free(&mxbuf);
MF_THROW(mf_status_to_exception(mxstat),
_("cannot get MX records for %s"), domain);
}
if (mxstat == mf_not_found) {
mxbuf_free(&mxbuf);
MF_RETURN("");
} else {
int i;
MF_OBSTACK_BEGIN();
for (i = 0; i < mxbuf.mx_cnt; i++) {
if (i > 0)
MF_OBSTACK_1GROW(' ');
MF_OBSTACK_GROW(mxbuf.mx_buf[i]);
}
MF_OBSTACK_1GROW(0);
mxbuf_free(&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)
{
GACOPYZ_UINT32_T *ipbuf;
size_t ipcount;
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);
ip = ntohl(ip);
mxstat = getmxip(domain, &ipbuf, &ipcount);
if (mxstat != mf_success) {
MF_THROW(mf_status_to_exception(mxstat),
_("cannot get MXs for %s"), domain);
}
for (i = 0; i < ipcount; i++) {
if (ipbuf[i] == ip) {
rc = 1;
break;
}
}
free(ipbuf);
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, NULL, 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_INIT