Logo Search packages:      
Sourcecode: mailavenger version File versions  Download package

asmtpd.C

/* $Id$ */

/*
 *
 * Copyright (C) 2004-2007 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 "rawnet.h"
#include "getopt_long.h"

bool opt_d;
int opt_verbose;

bool terminated;
str path_avenger;
str path_bindir;
str path_pfos;
synfp_collect *synfpc;

struct listener {
  const sockaddr_in sin;
  int lfd;

  list_entry<listener> link;
  ihash_entry<listener> hlink;

  listener (const sockaddr_in &a);
  ~listener ();
  bool init ();
  void doactive (bool on);
  void doaccept ();

  static void active (bool on);
  static bool config (options *opt);
};

static list<listener, &listener::link> listen_list;
static ihash<const sockaddr_in, listener,
           &listener::sin, &listener::hlink> listen_tab;

listener::listener (const sockaddr_in &a)
  : sin (a), lfd (-1)
{
  listen_list.insert_head (this);
  listen_tab.insert (this);
}

listener::~listener ()
{
  if (lfd >= 0) {
    fdcb (lfd, selread, NULL);
    close (lfd);
  }
  listen_list.remove (this);
  listen_tab.remove (this);
}

bool
listener::init ()
{
  if (lfd >= 0)
    return true;
  lfd = inetsocket (SOCK_STREAM, ntohs (sin.sin_port),
                ntohl (sin.sin_addr.s_addr));
  if (lfd < 0) {
    warn ("TCP port %s:%d: %m\n", inet_ntoa (sin.sin_addr),
        htons (sin.sin_port));
    delete this;
    return false;
  }
  close_on_exec (lfd);
  make_async (lfd);
  if (listen (lfd, 5) < 0) {
    close (lfd);
    lfd = -1;
    return false;
  }
  return true;
}

void
listener::doactive (bool on)
{
  if (on)
    fdcb (lfd, selread, wrap (this, &listener::doaccept));
  else
    fdcb (lfd, selread, NULL);
}

void
listener::active (bool on)
{
  for (listener *lp = listen_list.first; lp;
       lp = listen_list.next (lp))
    lp->doactive (on);
}

bool
listener::config (options *opt)
{
  inet_bindaddr.s_addr = htonl (INADDR_ANY);

  bool any = false;
  for (sockaddr_in *sinp = opt->bindaddrv.base ();
       sinp < opt->bindaddrv.lim (); sinp++) {
    if (sinp->sin_addr.s_addr == htonl (INADDR_ANY))
      any = true;
    if (!listen_tab[*sinp])
      vNew listener (*sinp);
  }
  for (listener *lp = listen_list.first, *nlp; lp; lp = nlp) {
    nlp = listen_list.next (lp);
    if (!opt->bindaddrh[lp->sin])
      delete lp;
    else
      lp->init ();
  }

  // XXX
  if (!any)
    for (sockaddr_in *sinp = opt->bindaddrv.base ();
       sinp < opt->bindaddrv.lim (); sinp++)
      if (sinp->sin_addr.s_addr != htonl (INADDR_LOOPBACK)
        && listen_tab[*sinp]) {
      inet_bindaddr = sinp->sin_addr;
      break;
      }

  toggle_listen (true);
  return listen_list.first;
}

newcon::newcon (int f, const sockaddr_in &sin)
  : sin (sin), fd (f), ii (NULL), t (TRUST_NONE),
    cbpending (0), failed (false)
{
}

void
newcon::init ()
{
  cbpending++;

  make_async (fd);
  close_on_exec (fd);

  if (sin.sin_addr.s_addr == htonl (INADDR_LOOPBACK))
    t = TRUST_LOCAL;
  else {
    for (const ipmask *mp = opt->trustednets.base ();
       t < TRUST_RCPT && mp < opt->trustednets.lim (); mp++)
      if ((sin.sin_addr.s_addr & mp->mask) == mp->net)
      t = TRUST_RCPT;

    if (t < TRUST_RCPT) {
      ii = ipinfo::lookup (sin.sin_addr, true);
      if (str err = ii->addcon ()) {
      use (write (fd, err, err.len ()));
      close (fd);
      delete this;
      return;
      }
    }
  }

  smtpd::num_smtpd++;
  toggle_listen ();

  if (!name) {
    cbpending++;
    identptr (fd, wrap (this, &newcon::ident_cb), opt->ident_timeout);
  }
  else if (!h) {
    cbpending++;
    dns_hostbyaddr (sin.sin_addr, wrap (this, &newcon::ptr_cb));
  }
  else {
    cbpending++;
    ptr_cb (h, 0);
  }

#if USE_SYNFP
  if (synfpc && t < TRUST_LOCAL) {
    cbpending++;
    synfpc->lookup (sin, wrap (this, &newcon::synfp_cb));
  }
#endif /* USE_SYNFP */

  maybe_start ();
}

void
newcon::ident_cb (str nn, ptr<hostent> hh, int err)
{
  assert (nn);
  name = nn;
  ptr_cb (hh, err);
}

void
newcon::ptr_cb (ptr<hostent> hh, int err)
{
  if (hh)
    h = hh;
  if (dns_tmperr (err) && sin.sin_addr.s_addr != htonl (INADDR_LOOPBACK)) {
    warn << name << ": " << dns_strerror (err) << "\n";
    if (opt->allow_dnsfail)
      dns_error = strbuf () << name << ": " << dns_strerror (err);
    else {
      str msg (strbuf ("421 %s\r\n", dns_strerror (err)));
      use (write (fd, msg.cstr (), msg.len ())); // Don't care if truncated
      failed = true;
    }
  }

  if (failed || opt->rbls.empty ())
    maybe_start ();
  else {
    rs = New refcounted<rbl_status>;
    rbl_check_con (rs, opt->rbls, sin.sin_addr,
               h ? h->h_name : (char *) NULL,
               wrap (this, &newcon::rbl_cb));
  }
}

void
newcon::rbl_cb ()
{
  maybe_start ();
}

void
newcon::synfp_cb (str fp)
{
#if SYNFP_DEBUG
  if (fp)
    warn ("synfp-cb %s:%d %s\n", inet_ntoa (sin.sin_addr),
        ntohs (sin.sin_port), fp.cstr ());
  else
    warn ("synfp-cb %s:%d NULL\n", inet_ntoa (sin.sin_addr),
        ntohs (sin.sin_port));
#endif

  synfp = fp;
  maybe_start ();
}

void
newcon::maybe_start ()
{
  if (--cbpending)
    return;
  smtpd::num_smtpd--;
  if (failed) {
    if (ii) {
      ii->error ();
      ii->delcon ();
    }
    close (fd);
    delete this;
    toggle_listen ();
    return;
  }

  if (h) {
    str hname = h->h_name;
    for (const str *tdp = opt->trusteddomains.base ();
       t < TRUST_RCPT && tdp < opt->trusteddomains.lim (); tdp++) {
      if (tdp->len () > hname.len ()
        || strcasecmp (tdp->cstr (),
                   hname.cstr () + hname.len () - tdp->len ()))
      continue;
      if (tdp->len () == hname.len ()) {
      t = TRUST_RCPT;
      }
      else
      switch (hname[hname.len () - tdp->len () - 1]) {
      case '.':
      case '@':
        t = TRUST_RCPT;
      }
    }
  }

  if (t >= TRUST_RCPT && ii) {
    ii->delcon ();
    ii = NULL;
  }

  vNew smtpd (ii, fd, sin, name, synfp, t, rs, h, dns_error);
  delete this;
}

void
listener::doaccept ()
{
  sockaddr_in sin;
  socklen_t sinlen = sizeof (sin);
  bzero (&sin, sizeof (sin));

  int fd = accept (lfd, (sockaddr *) &sin, &sinlen);
  if (fd < 0) {
    if (errno != EAGAIN)
      warn ("accept: %m\n");
    return;
  }

  if (opt->debug_smtpd)
    warn ("accepted connection from %s:%d (fd %d)\n",
        inet_ntoa (sin.sin_addr), ntohs (sin.sin_port), fd);
  newcon *nc = New newcon (fd, sin);
  nc->init ();
}

static void
doexit (int val)
{
  warn << progname << " pid " << getpid () << " exiting\n";
  exit (val);
}

void
toggle_listen (bool force)
{
  static bool state;

  if (terminated && !smtpd::num_smtpd) {
    delaycb (0, 2000000, wrap (doexit, 0));
    return;
  }

  bool ostate = state;
  state = !terminated && smtpd::num_smtpd < opt->max_clients;
  if (force || state != ostate)
    listener::active (state);
}

static void
termsig (int sig)
{
  if (terminated) {
    static int nsig;
    if (!smtpd::num_indata) {
      warn ("hard shutdown on second signal\n");
      doexit (1);
    }
    if (++nsig > 2) {
      warn ("aborting clients in data and exiting immediately\n");
      doexit (1);
    }
    warn ("waiting for clients still in data\n");
    return;
  }
  warn ("shutting down on signal %d\n", sig);
  terminated = true;
  clear_filters ();
  while (listen_list.first)
    delete listen_list.first;

  for (smtpd *s = smtplist.first, *ns; s; s = ns) {
    ns = smtplist.next (s);
    s->maybe_shutdown ();
  }

  toggle_listen ();
}

static void
reconfig ()
{
  if (terminated)
    return;

  warn ("re-reading configuration file\n");
  options *nopt = New options;
  if (!parseconfig (nopt, config_file)) {
    delete nopt;
    warn ("errors found in config file, keeping old configuration\n");
    return;
  }

  if (opt->logpriority != nopt->logpriority
      || opt->logtag != nopt->logtag) {
    syslog_priority = nopt->logpriority;
    warn << "switching log tag/priority to "
       << nopt->logtag << "/" << syslog_priority << "\n";
    start_logger (nopt->logtag);
  }

  if (!listener::config (nopt))
    fatal ("No requested TCP ports available\n");
  netpath_reset ();

  delete opt;
  opt = nopt;

#if USE_SYNFP
  delete synfpc;
  synfpc = NULL;
  if (opt->synfp) {
    synfpc = New synfp_collect (opt->synfp_wait, opt->synfp_buf);
    if (!synfpc->init (opt->bindaddrv)) {
      delete synfpc;
      synfpc = NULL;
    }
  }
#endif /* USE_SYNFP */

  ssl_init ();
}

static void
cleargroups ()
{
  /* For efficiency, we want to be able to drop privileges to the
   * avenger user without calling initgroups.  So we'd better get rid
   * of any supplemental privileged groups root might belong to. */
  if (!getuid ()) {
    GETGROUPS_T gid = getgid ();
#ifdef HAVE_EGID_IN_GROUPLIST
    setgroups (1, &gid);
#else /* !HAVE_EGID_IN_GROUPLIST */
    if (setgroups (0, NULL))
      setgroups (1, &gid);
#endif /* !HAVE_EGID_IN_GROUPLIST */
  }
}

static void
dumpstats ()
{
  quota_dump (warnx);
}
static void
smtpstart ()
{
  cleargroups ();
  ssl_init ();

  if (!listener::config (opt))
    fatal ("No requested TCP ports available\n");

  if (opt->smtp_filter)
    run_cmd (opt->smtp_filter, "clear");

  sigcb (SIGINT, wrap (&termsig, SIGINT));
  sigcb (SIGTERM, wrap (&termsig, SIGTERM));
  sigcb (SIGHUP, wrap (reconfig));
  sigcb (SIGUSR1, wrap (dumpstats));

  if (!opt_d) {
    daemonize (opt->logtag);
    if (!chdir ("/"))
      xputenv ("PWD=/");
  }
  warn << progname << " (Mail Avenger) version " << VERSION << ", pid "
       << getpid () << "\n";
  warn ("%s is %s\n", AVENGER, path_avenger.cstr ());
  //warn ("pf.os is %s\n", path_pfos.cstr ());
  warn ("bindir is %s\n", path_bindir.cstr ());

#if USE_SYNFP
  if (opt->synfp) {
    synfpc = New synfp_collect (opt->synfp_wait, opt->synfp_buf);
    if (!synfpc->init (opt->bindaddrv)) {
      delete synfpc;
      synfpc = NULL;
    }
  }
#endif /* USE_SYNFP */
}

static bool tst_eof;
static int tst_n;
static void
spftst_2 (str line, spf_result res, str expl, str mech)
{
  aout << ">>>" << spf_print (res) << ": " << line << "\n";
  if (mech)
    aout << "   (" << mech << ")\n";
  if (!--tst_n && tst_eof)
    exit (0);
}
static void
spftst (str line, int err)
{
  if (!line) {
    tst_eof = true;
    if (!tst_n)
      exit (0);
    return;
  }

  static rxx parse ("^(\\d+(\\.\\d+){3})\\s+(\\S+)(\\s+(\\S+))?$");
  if (!parse.match (line))
    warnx << "?syntax error\n";
  else {
    tst_n++;
    in_addr a;
    a.s_addr = inet_addr (parse[1]);
    spf_check (a, parse[3], wrap (spftst_2, line), parse[5]);
  }

  ain->readline (wrap (spftst));
}

static void
rbltst_3 (str name, ref<rbl_status> stat)
{
  aout << ">>> " << name << "\n";
  aout << "score " << stat->score;
  if (stat->trusted)
    aout << ", whitelisted";
  aout << "\n";
  for (rbl_status::result *rp = stat->results.base ();
       rp < stat->results.lim (); rp++)
    aout << rp->tostr (false) << "\n";
  aout << "----------------\n";

  if (!--tst_n && tst_eof)
    exit (0);
}
static void
rbltst_2 (ref<rbl_status> rs, in_addr addr, ptr<hostent> h, int err)
{
  if (err && dns_tmperr (err))
    warn << inet_ntoa (addr) << ": " << dns_strerror (err) << "\n";
  if (h)
    rbl_check_con (rs, opt->rbls, addr, h->h_name,
               wrap (rbltst_3, inet_ntoa (addr), rs));
  else
    rbl_check_con (rs, opt->rbls, addr, NULL,
               wrap (rbltst_3, inet_ntoa (addr), rs));
}
static void
rbltst (str line, int err)
{
  if (!line) {
    tst_eof = true;
    if (!tst_n)
      exit (0);
    return;
  }

  ref<rbl_status> rs (New refcounted<rbl_status>);
  in_addr a;
  if (str r = extract_domain (line)) {
    tst_n++;
    rbl_check_env (rs, opt->rbls, r, wrap (rbltst_3, line, rs));
  }
  else if (inet_aton (line, &a) == 1) {
    tst_n++;
    dns_hostbyaddr (a, wrap (rbltst_2, rs, a));
  }
  else
    aout << "?syntax error\n";

  ain->readline (wrap (rbltst));
}

static void avenge_usage () __attribute__ ((noreturn));
static void
avenge_usage ()
{
  warnx << "usage: " << progname << " --avenge recipient [sender [ip-addr]]\n";
  exit (1);
}
static void
avenge_c (aios_t in, strbuf sb, ref<vec<str> > cmdv, str line, int err)
{
  static rxx resprx ("^(\\d\\d\\d)(-| )(.*)$");
  if (!line)
    fatal ("test SMTP server: %s\n", strerror (err));
  if (!resprx.match (line))
    fatal ("bad line from test SMTP server:\n%s\n", line.cstr ());
  sb << line << "\n";
  if (resprx[2] == " ") {
    str r (sb);
    //warnx << r;
    if (r[0] != '2') {
      warnx << "\nrejected by SMTP server:\n" << r;
      exit (1);
    }
    sb.tosuio ()->clear ();
    if (cmdv->empty ()) {
      warnx << "\naccepted by SMTP server:\n" << r;
      exit (0);
    }
    //warnx << "\nSMTP test <<< " << cmdv->front () << "\n";
    in << cmdv->pop_front () << "\r\n";
  }
  in->readline (wrap (avenge_c, in, sb, cmdv));
}
static void
avenge_s (int s, sockaddr_in sin, ptr<hostent> h, int err)
{
  str msg;
  if (!h && dns_tmperr (err))
    msg = strbuf ("%s: %s", inet_ntoa (sin.sin_addr), dns_strerror (err));
  vNew smtpd (NULL, s, sin, "SMTP-test", NULL, TRUST_NONE, NULL, h, msg);
}
static void
avenge (int argc, char **argv)
{
  if (argc < 1 || argc > 3)
    avenge_usage ();

  int ls = inetsocket (SOCK_STREAM, 0,
                   htonl (opt->bindaddrv[0].sin_addr.s_addr));
  if (ls < 0)
    fatal ("socket: %m\n");
  if (listen (ls, 1) < 0)
    fatal ("listen: %m\n");

  sockaddr_in sin;
  socklen_t sinlen = sizeof (sin);
  bzero (&sin, sizeof (sin));
  getsockname (ls, (sockaddr *) &sin, &sinlen);
  if (sin.sin_addr.s_addr == htonl (INADDR_ANY)) {
    sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    vec<in_addr> myips;
    for (myipaddrs (&myips); !myips.empty (); myips.pop_front ())
      if (myips[0].s_addr != ntohl (INADDR_LOOPBACK)) {
      sin.sin_addr = myips[0];
      break;
      }
  }

  int c = inetsocket (SOCK_STREAM);
  if (c < 0)
    fatal ("socket: %m\n");
  make_async (c);
  if (connect (c, (sockaddr *) &sin, sizeof (sin)) < 0
      && errno != EINPROGRESS)
    fatal ("TCP connect: %m\n");

  sinlen = sizeof (sin);
  int s = accept (ls, (sockaddr *) &sin, &sinlen);
  if (s < 0)
    fatal ("accept: %m\n");
  close (ls);
  make_async (s);

  opt->debug_avenger = true;
  opt->vrfy_delay = 0;

  if (argc >= 3 && inet_aton (argv[2], &sin.sin_addr) != 1)
    avenge_usage ();
  dns_hostbyaddr (sin.sin_addr, wrap (avenge_s, s, sin));

  ref<vec<str> > cmdv = New refcounted<vec<str> >;
  cmdv->push_back (strbuf () << "ehlo " << opt->hostname);

  if (argc >= 2)
    cmdv->push_back (strbuf () << "mail from:<" << argv[1] <<">");
  else
    cmdv->push_back (strbuf () << "mail from:<postmaster@"
                 << opt->hostname << ">");

  cmdv->push_back (strbuf () << "rcpt to:<" << argv[0] <<">");

  aios_t in (aios::alloc (c));
  in->readline (wrap (avenge_c, in, strbuf (), cmdv));
}

static void
path_init ()
{
  static rxx colonplus (":+");
  str path = getenv ("PATH");
  strbuf sb;
  sb << "PATH=" << path_bindir;
  vec<str> comp;
  split (&comp, colonplus, path);
  while (!comp.empty ())
    if (comp.front () == path_bindir)
      comp.pop_front ();
    else
      sb << ":" << comp.pop_front ();
  path = sb;
  xputenv (path);
  //warn << path << "\n";
}

inline bool
execok (const char *path)
{
  struct stat sb;
  return !stat (path, &sb) && S_ISREG (sb.st_mode) && (sb.st_mode & 0111);
}
inline str
striplast (const char *in)
{
  const char *p = in + strlen (in);
  while (p > in && p[-1] == '/')
    p--;
  while (p > in && p[-1] != '/')
    p--;
  while (p > in && p[-1] == '/')
    p--;
  if (p > in)
    return str (in, p - in);
  return NULL;
}
inline str
stripfirst (const char *in)
{
  while (in && *in && *in != '/')
    in++;
  while (in && *in && *in == '/')
    in++;

  if (*in)
    return in;
  return NULL;
}
static str
mycwd ()
{
  struct stat sb1, sb2;
  if (stat (".", &sb1))
    return NULL;
  if (char *pwd = getenv ("PWD"))
    if (!stat (pwd, &sb2) && sb1.st_dev == sb2.st_dev
      && sb1.st_ino == sb2.st_ino)
      return pwd;
  char buf[MAXPATHLEN + 1];
  return getcwd (buf, sizeof (buf));
}
static str
normalize_path (str path)
{
  str dir;
  if (path[0] != '/') {
    while (path && path[0] == '.' && path[1] == '/')
      path = stripfirst (path);
    if (path && (dir = mycwd ())) {
      if (!strncmp (path, "../", 3))
      if (str ndir = striplast (dir)) {
        path = stripfirst (path);
        dir = ndir;
      }
      path = dir << "/" << path;
    }
    else if (!path)
      path = mycwd ();
  }
  return path;
}
static void
find_avenger ()
{
  str dir = progdir;
  if (!dir) {
    dir = find_program (progname);
    if (dir)
      dir = striplast (dir);
  }
  while (dir && dir.len () && dir[dir.len () - 1] == '/')
    dir = substr (dir, 0, dir.len () - 1);

  str path, bindir;
  if (dir) {
    if ((path = striplast (dir))) {
      bindir = path << "/bin";
      path_pfos = path << "/share/pf.os";
      path = path << "/" << "libexec/" AVENGER;
      if (!execok (path))
      path = NULL;
    }
    if (!path && (path = striplast (dir))) {
      bindir = path << "/util";
      path = path << "/" AVENGER;
      path_pfos = path << "/pf.os";
      if (!execok (path))
      path = NULL;
    }
    else if (!path && dir == ".") {
      bindir = "../util";
      path = "../" AVENGER;
      path_pfos = "../pf.os";
      if (!execok (path))
      path = NULL;
    }
    else if (!path) {
      bindir = "util";
      path = AVENGER;
      path_pfos = "pf.os";
      if (!execok (path))
      path = NULL;
    }
  }
  if (!path) {
    bindir = BINDIR;
    path_pfos = DATADIR "/pf.os";
    if (!execok (path = LIBEXEC "/" AVENGER))
      path = NULL;
  }

  if (path) {
    path = normalize_path (path);
    bindir = normalize_path (bindir);
    path_pfos = normalize_path (path_pfos);
  }

  if (!path)
    fatal ("cannot find %s program\n", AVENGER);
  path_avenger = path;
  path_bindir = bindir;
  if (access (path_pfos, 0) < 0)
    path_pfos = DATADIR "/pf.os";
  if (access (path_pfos, 0) && !access ("/etc/pf.os", 0))
    path_pfos = "/etc/pf.os";
  path_init ();
}

static str
compile_options ()
{
  bool set = false;
  strbuf sb;
#define setopt(opt)                       \
  do {                                    \
    sb << (set ? " " : " (") << #opt;           \
    set = true;                           \
  } while (0)
#if !USE_SYNFP
  setopt (no-synfp);
#endif /* !USE_SYNFP */
#ifdef SASL
  setopt (SASL);
#endif /* SASL */
#ifndef STARTTLS 
  setopt (no-starttls);
#endif /* !STARTTLS */
#undef setopt 
  if (set)
    sb << ")";
  return sb;
}

static void usage () __attribute__ ((noreturn));
static void
usage ()
{
  warnx << "usage: " << progname << " [-d] [-f <config-file]\n"
#if USE_SYNFP
      << "       [--spf | --rbl | --avenge | --synfp | --netpath] ...\n";
#else /* !USE_SYNFP */
      << "       [--spf | --rbl | --avenge | --netpath] ...\n";
#endif /* !USE_SYNFP */
  exit (1);
}

int
main (int argc, char **argv)
{
  setprogname (argv[0]);

  int mode = 0;
  option o[] = {
    { "version", no_argument, &mode, 1 },
    { "help", no_argument, &mode, 2 },
    { "spf", no_argument, &mode, 3 },
    { "rbl", no_argument, &mode, 4 },
#if USE_SYNFP
    { "synfp", no_argument, &mode, 5 },
#endif /* USE_SYNFP */
    { "netpath", no_argument, &mode, 6 },
    { "avenge", no_argument, &mode, 7 },
    { "verbose", no_argument, &opt_verbose, 1 },
    { NULL, 0, NULL, 0 }
  };

  int c;
  while ((c = getopt_long (argc, argv, "+dDf:", o, NULL)) != -1)
    switch (c) {
    case 0:
      break;
    case 'd':
      opt_d = true;
      break;
    case 'D':
      opt_d = false;
      break;
    case 'f':
      config_file = optarg;
      break;
    default:
      usage ();
      break;
    }

  switch (mode) {
  case 1:
    warnx << progname << " (Mail Avenger) " << VERSION
        << compile_options () << "\n"
        << "Copyright (C) 2004-2007 David Mazieres\n"
        << "This program comes with NO WARRANTY,"
        << " to the extent permitted by law.\n"
        << "You may redistribute it under the terms of"
        << " the GNU General Public License;\n"
        << "see the file named COPYING for details.\n";
    return 0;
  case 2:
    usage ();
#if USE_SYNFP
  case 5:
    find_avenger ();
    synfp_test (argc - optind, argv + optind);
    amain ();
#endif /* USE_SYNFP */
  case 6:
    netpath_test (argc - optind, argv + optind);
    amain ();
  }

  if (!parseconfig (opt, config_file))
    fatal ("error parsing asmtpd.conf file\n");
  syslog_priority = opt->logpriority;

  if (mode == 7) {
    find_avenger ();
    avenge (argc - optind, argv + optind);
    amain ();
  }

  if (optind != argc)
    usage ();

  switch (mode) {
  case 0:
    find_avenger ();
    smtpstart ();
    break;
  case 3:
    aout << "SPF test mode; enter: <IP address> <from address>"
      " [<helo host>]\n";
    ain->readline (wrap (spftst));
    amain ();
    break;
  case 4:
    aout << "RBL test mode; enter {<IP address> | <from address>}\n";
    ain->readline (wrap (rbltst));
    amain ();
    break;
  default:
    usage ();
    return 1;
  }

  amain ();
}


Generated by  Doxygen 1.6.0   Back to index