/* $Id: mxcheck.C,v 1.6 2006/02/15 08:48:58 dm Exp $ */
/*
*
* Copyright (C) 2003 David Mazieres (dm@uun.org)
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*/
#include "asmtpd.h"
#include "init.h"
static u_int64_t mxcache_initno;
static qhash<str, int> mxcache;
static bhash<in_addr> addrcache;
struct mxcheck {
const str relay;
cbi cb;
vec<dnsreq *> dnsv;
vec<str> mxes;
u_int done;
int err;
u_int not_best;
mxcheck (str r, cbi c);
~mxcheck ();
void finish (int e) { (*cb) (e); delete this; }
void init (ptr<mxlist> mxl);
void getmx (ptr<mxlist> mxl, int err);
void geta (int i, ptr<hostent> h, int thiserr);
};
mxcheck::mxcheck (str r, cbi c)
: relay (r), cb (c), done (0), err (0)
{
}
mxcheck::~mxcheck ()
{
for (dnsreq **rp = dnsv.base (); rp < dnsv.lim (); rp++)
if (*rp)
dnsreq_cancel (*rp);
}
void
mxcheck::init (ptr<mxlist> mxl)
{
dnsv.push_back (NULL);
if (mxl)
getmx (mxl, 0);
else if (dnsreq *rq = dns_mxbyname (relay, wrap (this, &mxcheck::getmx)))
dnsv[0] = rq;
}
void
mxcheck::getmx (ptr<mxlist> mxl, int thiserr)
{
dnsv[0] = NULL;
if (thiserr && dns_tmperr (thiserr)) {
maybe_warn (strbuf () << relay << " (MX query): "
<< dns_strerror (thiserr) << "\n");
finish (thiserr);
return;
}
if (!mxl) {
#if 0
if (!strcasecmp (relay, opt->hostname)) {
not_best = 1;
mxes.push_back (relay);
}
else
#endif
{
/* The old implicit MX record approach is deprecated... people
* should specify explicit MX records if they want mail. */
finish (NXDOMAIN);
return;
}
}
else
for (u_int i = 0; i < mxl->m_nmx; i++) {
if (mxl->m_mxes[i].pref <= mxl->m_mxes[0].pref)
not_best = i + 1;
mxes.push_back (mxl->m_mxes[i].name);
}
err = NXDOMAIN;
dnsv.setsize (mxes.size ());
for (u_int i = 0; i < mxes.size (); i++) {
if (dnsreq *rp
= dns_hostbyname (mxes[i], wrap (this, &mxcheck::geta, i)))
dnsv[i] = rp;
}
}
void
mxcheck::geta (int i, ptr<hostent> h, int thiserr)
{
dnsv[i] = NULL;
done++;
if (thiserr && dns_tmperr (thiserr) && err > 0) {
maybe_warn (strbuf () << mxes[i] << " (A query): "
<< dns_strerror (thiserr) << "\n");
err = thiserr;
}
if (h)
for (char **ap = h->h_addr_list; *ap; ap++)
if (addrcache[*(in_addr *) *ap]) {
if (i < int (not_best)) {
mxcache.insert (relay, 0);
finish (0);
return;
}
else {
err = -1;
mxcache.insert (relay, -1);
}
}
if (done >= mxes.size ())
finish (err);
}
static void
addrcache_init ()
{
static ifchgcb_t *ccb;
bool any = false;
mxcache.clear ();
addrcache.clear ();
mxcache_initno = opt->configno;
for (const sockaddr_in *ap = opt->bindaddrv.base ();
ap < opt->bindaddrv.lim (); ap++) {
if (ap->sin_addr.s_addr == htonl (INADDR_ANY))
any = true;
else
addrcache.insert (ap->sin_addr);
}
if (any) {
vec<in_addr> addrs;
if (!myipaddrs (&addrs))
fatal ("Failed to get my own IP address\n");
for (in_addr *ap = addrs.base (); ap < addrs.lim (); ap++)
addrcache.insert (*ap);
if (!ccb)
ccb = ifchgcb (wrap (addrcache_init));
}
else if (ccb)
ifchgcb_remove (ccb);
}
void
ismxlocal (str relay, cbi cb, ptr<mxlist> mxl)
{
if (mxcache_initno != opt->configno)
addrcache_init ();
relay = mytolower (relay);
if (int *ip = mxcache[relay])
(*cb) (*ip);
else
(New mxcheck (relay, cb))->init (mxl);
}
syntax highlighted by Code2HTML, v. 0.9.1