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

quota.C

/* $Id$ */

/*
 *
 * Copyright (C) 2004 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"

ihash<const in_addr, ipinfo, &ipinfo::addr, &ipinfo::link> iitab;
ihash<const str, userinfo, &userinfo::user, &userinfo::link> uitab;

void
run_cmd (const char *cmd, const char *arg1, const char *arg2)
{
  const char *av[] = { cmd, arg1, arg2, NULL };
  pid_t pid = spawn (cmd, av);
  if (pid < 0)
    warn ("%s: %m\n", cmd);
  else
    waitpid (pid, NULL, 0);
}

lazycb_t *quota_maintenance_cb;
static void
quota_maintenance ()
{
  iitab.traverse (&ipinfo::maybe_delete);
  uitab.traverse (&userinfo::maybe_delete);
}

quota::quota ()
  : last (timenow)
{
  if (!quota_maintenance_cb)
    quota_maintenance_cb = lazycb (1200, wrap (quota_maintenance));
}

void
quota::decay (bool del)
{
  if (last > timenow)
    last = timenow;
  u_int interval = (timenow - last) / (3600 / msgmult);
  last += interval * (3600 / msgmult);
  if (interval || del)
    do_decay (del, interval);
}

ipinfo::ipinfo (in_addr a)
  : addr (a), filt (false), ncon (0), ndeliv (0), nerr (0),
    trp (NULL), netpath_time (0), nhops (0)
{
  iitab.insert (this);
}

ipinfo::~ipinfo ()
{
  iitab.remove (this);
  setfilter (false);

  /* This shouldn't happen because the count should be bumped for new
   * clients.  If it did happen, we could drop a callback because of
   * netpath_addcb. */
  assert (!trp);
}

ipinfo *
ipinfo::lookup (struct in_addr a, bool create)
{
  if (struct ipinfo *ii = iitab[a])
    return ii;
  if (create)
    return New ipinfo (a);
  return NULL;
}

void
ipinfo::setfilter (bool newfilt)
{
  if (filt == newfilt || terminated)
    return;
  filt = newfilt;
  if (opt->smtp_filter && !terminated)
    run_cmd (opt->smtp_filter, filt ? "add" : "del", inet_ntoa (addr));
}
void
ipinfo::setfilter ()
{
  setfilter (ncon >= opt->con_max_per_ip
           || nerr + msgmult > opt->err_max_per_ip * msgmult
           /* || ndeliv + msgmult > opt->msg_max_per_ip * msgmult */
           );
}

void
ipinfo::do_netpath (in_addr src)
{
  if (!opt->netpath || trp || timenow < netpath_time + 60)
    return;

  sockaddr_in ss;
  bzero (&ss, sizeof (ss));
  ss.sin_family = AF_INET;
  ss.sin_addr = src;

  sockaddr_in dst;
  bzero (&dst, sizeof (dst));
  dst.sin_family = AF_INET;
  dst.sin_port = htons (0);
  dst.sin_addr = addr;

  trp = ::netpath (&dst , 0, wrap (this, &ipinfo::netpath_cb), &ss);
}

void
ipinfo::netpath_cb (int total_hops, in_addr *ap, int ac)
{
  trp = NULL;
  if (total_hops > 0)
    nhops = total_hops;
  else
    nhops = -1;
  if (ac > 0) {
    strbuf sb;
    for (int i = 0; i < ac; i++) {
      if (i)
      sb << " ";
      sb << inet_ntoa (ap[i]);
    }
    netpath = sb;
  }
  else
    netpath = NULL;
  netpath_time = timenow;
}

void
ipinfo::do_decay (bool del, u_int interval)
{
  decay_var (ndeliv, opt->msg_rate_per_ip, interval);
  decay_var (nerr, opt->err_rate_per_ip, interval);
  setfilter ();
  if (del && !ncon && !ndeliv && !nerr && !trp)
    delete this;
}

static str deliverr ("421 too much load, please back off\r\n");
static str errerr ("421 too many errors, please back off\r\n");
str
ipinfo::rcpt ()
{
  if (!check_var (ndeliv, opt->msg_max_per_ip))
    return deliverr;
  ndeliv += msgmult;
  return NULL;
}

str
ipinfo::status ()
{
  decay ();
  if (check_var (nerr, opt->err_max_per_ip))
    return NULL;
  error ();
  return errerr;
}

str
ipinfo::addcon ()
{
  if (!check_var (nerr, opt->err_max_per_ip))
    // Don't decay or bump error -- just wait for next maintenance
    return errerr;
  else if (str st = status ())
    return st;
  else if (ncon >= opt->con_max_per_ip) {
    static str conerr ("421 too many open connections\r\n");
    error ();
    return conerr;
  }

  ncon++;
  return NULL;
}

userinfo::userinfo (str u)
  : user (u), ndeliv (0)
{
  uitab.insert (this);
}

userinfo::~userinfo ()
{
  uitab.remove (this);
}

void
userinfo::do_decay (bool del, u_int interval)
{
  decay_var (ndeliv, opt->msg_rate_per_user, interval);
  if (del && !ndeliv)
    delete this;
}

userinfo *
userinfo::lookup (str u, bool create)
{
  if (struct userinfo *ui = uitab[u])
    return ui;
  if (create)
    return New userinfo (u);
  return NULL;
}

str
userinfo::rcpt ()
{
  if (!check_var (ndeliv, opt->msg_max_per_user))
    return deliverr;
  ndeliv += msgmult;
  return NULL;
}

void
clear_filters ()
{
  for (ipinfo *ii = iitab.first (); ii; ii = iitab.next (ii))
    if (ii->filt && opt->smtp_filter)
      run_cmd (opt->smtp_filter, "del", inet_ntoa (ii->addr));
}

void
quota_dump (const strbuf &sb)
{
  quota_maintenance ();
  sb << "250----------------------------------------\r\n"
     << "250-IP address       F  #conn  #rcpt  #errs\r\n";
  for (ipinfo *ii = iitab.first (); ii; ii = iitab.next (ii)) {
    ii->decay ();
    strbuf line ("250-%-15s  %c   % 4d   % 4d   % 4d\r\n",
             inet_ntoa (ii->addr), ii->filt ? '*' : ' ', 
             ii->ncon, ii->ndeliv/ipinfo::msgmult,
             ii->nerr/ipinfo::msgmult);
    sb << line;
  }

  sb << "250----------------------------------------\r\n"
     << "250-Sending entity             #rcpt\r\n";
  for (userinfo *ui = uitab.first (); ui; ui = uitab.next (ui)) {
    ui->decay ();
    sb.fmt ("250-%-26s  % 4d\r\n", ui->user.cstr (),
          ui->ndeliv/userinfo::msgmult);
  }

  sb << "250 ---------------------------------------\r\n";
}

Generated by  Doxygen 1.6.0   Back to index