/* $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 mxcache; static bhash addrcache; struct mxcheck { const str relay; cbi cb; vec dnsv; vec 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 mxl); void getmx (ptr mxl, int err); void geta (int i, ptr 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 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 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 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 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 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); }