/* 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