1 /* $Id$ */
2 
3 /*
4  *
5  * Copyright (C) 2004 David Mazieres (dm@uun.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2, or (at
10  * your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20  * USA
21  *
22  */
23 
24 #include "asmtpd.h"
25 #include "rawnet.h"
26 
27 ihash<const in_addr, ipinfo, &ipinfo::addr, &ipinfo::link> iitab;
28 ihash<const str, userinfo, &userinfo::user, &userinfo::link> uitab;
29 
30 void
run_cmd(const char * cmd,const char * arg1,const char * arg2)31 run_cmd (const char *cmd, const char *arg1, const char *arg2)
32 {
33   const char *av[] = { cmd, arg1, arg2, NULL };
34   pid_t pid = spawn (cmd, av);
35   if (pid < 0)
36     warn ("%s: %m\n", cmd);
37   else
38     waitpid (pid, NULL, 0);
39 }
40 
41 lazycb_t *quota_maintenance_cb;
42 static void
quota_maintenance()43 quota_maintenance ()
44 {
45   iitab.traverse (&ipinfo::maybe_delete);
46   uitab.traverse (&userinfo::maybe_delete);
47 }
48 
quota()49 quota::quota ()
50   : last (timenow)
51 {
52   if (!quota_maintenance_cb)
53     quota_maintenance_cb = lazycb (1200, wrap (quota_maintenance));
54 }
55 
56 void
decay(bool del)57 quota::decay (bool del)
58 {
59   if (last > timenow)
60     last = timenow;
61   u_int interval = (timenow - last) / (3600 / msgmult);
62   last += interval * (3600 / msgmult);
63   if (interval || del)
64     do_decay (del, interval);
65 }
66 
ipinfo(in_addr a)67 ipinfo::ipinfo (in_addr a)
68   : addr (a), filt (false), ncon (0), ndeliv (0), nerr (0),
69     trp (NULL), netpath_time (0), nhops (0)
70 {
71   iitab.insert (this);
72 }
73 
~ipinfo()74 ipinfo::~ipinfo ()
75 {
76   iitab.remove (this);
77   setfilter (false);
78 
79   /* This shouldn't happen because the count should be bumped for new
80    * clients.  If it did happen, we could drop a callback because of
81    * netpath_addcb. */
82   assert (!trp);
83 }
84 
85 ipinfo *
lookup(struct in_addr a,bool create)86 ipinfo::lookup (struct in_addr a, bool create)
87 {
88   if (struct ipinfo *ii = iitab[a])
89     return ii;
90   if (create)
91     return New ipinfo (a);
92   return NULL;
93 }
94 
95 void
setfilter(bool newfilt)96 ipinfo::setfilter (bool newfilt)
97 {
98   if (filt == newfilt || terminated)
99     return;
100   filt = newfilt;
101   if (opt->smtp_filter && !terminated)
102     run_cmd (opt->smtp_filter, filt ? "add" : "del", inet_ntoa (addr));
103 }
104 void
setfilter()105 ipinfo::setfilter ()
106 {
107   setfilter (ncon >= opt->con_max_per_ip
108 	     || nerr + msgmult > opt->err_max_per_ip * msgmult
109 	     /* || ndeliv + msgmult > opt->msg_max_per_ip * msgmult */
110 	     );
111 }
112 
113 void
do_netpath(in_addr src)114 ipinfo::do_netpath (in_addr src)
115 {
116   if (!opt->netpath || trp || timenow < netpath_time + 60)
117     return;
118 
119   sockaddr_in ss;
120   bzero (&ss, sizeof (ss));
121   ss.sin_family = AF_INET;
122   ss.sin_addr = src;
123 
124   sockaddr_in dst;
125   bzero (&dst, sizeof (dst));
126   dst.sin_family = AF_INET;
127   dst.sin_port = htons (0);
128   dst.sin_addr = addr;
129 
130   trp = ::netpath (&dst , 0, wrap (this, &ipinfo::netpath_cb), &ss);
131 }
132 
133 void
netpath_cb(int total_hops,in_addr * ap,int ac)134 ipinfo::netpath_cb (int total_hops, in_addr *ap, int ac)
135 {
136   trp = NULL;
137   if (total_hops > 0)
138     nhops = total_hops;
139   else
140     nhops = -1;
141   if (ac > 0) {
142     strbuf sb;
143     for (int i = 0; i < ac; i++) {
144       if (i)
145 	sb << " ";
146       sb << inet_ntoa (ap[i]);
147     }
148     netpath = sb;
149   }
150   else
151     netpath = NULL;
152   netpath_time = timenow;
153 }
154 
155 void
do_decay(bool del,u_int interval)156 ipinfo::do_decay (bool del, u_int interval)
157 {
158   decay_var (ndeliv, opt->msg_rate_per_ip, interval);
159   decay_var (nerr, opt->err_rate_per_ip, interval);
160   setfilter ();
161   if (del && !ncon && !ndeliv && !nerr && !trp)
162     delete this;
163 }
164 
165 static str deliverr ("421 too much load, please back off\r\n");
166 static str errerr ("421 too many errors, please back off\r\n");
167 str
rcpt()168 ipinfo::rcpt ()
169 {
170   if (!check_var (ndeliv, opt->msg_max_per_ip))
171     return deliverr;
172   ndeliv += msgmult;
173   return NULL;
174 }
175 
176 str
status()177 ipinfo::status ()
178 {
179   decay ();
180   if (check_var (nerr, opt->err_max_per_ip))
181     return NULL;
182   error ();
183   return errerr;
184 }
185 
186 str
addcon()187 ipinfo::addcon ()
188 {
189   if (!check_var (nerr, opt->err_max_per_ip))
190     // Don't decay or bump error -- just wait for next maintenance
191     return errerr;
192   else if (str st = status ())
193     return st;
194   else if (ncon >= opt->con_max_per_ip) {
195     static str conerr ("421 too many open connections\r\n");
196     error ();
197     return conerr;
198   }
199 
200   ncon++;
201   return NULL;
202 }
203 
userinfo(str u)204 userinfo::userinfo (str u)
205   : user (u), ndeliv (0)
206 {
207   uitab.insert (this);
208 }
209 
~userinfo()210 userinfo::~userinfo ()
211 {
212   uitab.remove (this);
213 }
214 
215 void
do_decay(bool del,u_int interval)216 userinfo::do_decay (bool del, u_int interval)
217 {
218   decay_var (ndeliv, opt->msg_rate_per_user, interval);
219   if (del && !ndeliv)
220     delete this;
221 }
222 
223 userinfo *
lookup(str u,bool create)224 userinfo::lookup (str u, bool create)
225 {
226   if (struct userinfo *ui = uitab[u])
227     return ui;
228   if (create)
229     return New userinfo (u);
230   return NULL;
231 }
232 
233 str
rcpt()234 userinfo::rcpt ()
235 {
236   if (!check_var (ndeliv, opt->msg_max_per_user))
237     return deliverr;
238   ndeliv += msgmult;
239   return NULL;
240 }
241 
242 void
clear_filters()243 clear_filters ()
244 {
245   for (ipinfo *ii = iitab.first (); ii; ii = iitab.next (ii))
246     if (ii->filt && opt->smtp_filter)
247       run_cmd (opt->smtp_filter, "del", inet_ntoa (ii->addr));
248 }
249 
250 void
quota_dump(const strbuf & sb)251 quota_dump (const strbuf &sb)
252 {
253   quota_maintenance ();
254   sb << "250----------------------------------------\r\n"
255      << "250-IP address       F  #conn  #rcpt  #errs\r\n";
256   for (ipinfo *ii = iitab.first (); ii; ii = iitab.next (ii)) {
257     ii->decay ();
258     strbuf line ("250-%-15s  %c   % 4d   % 4d   % 4d\r\n",
259 		 inet_ntoa (ii->addr), ii->filt ? '*' : ' ',
260 		 ii->ncon, ii->ndeliv/ipinfo::msgmult,
261 		 ii->nerr/ipinfo::msgmult);
262     sb << line;
263   }
264 
265   sb << "250----------------------------------------\r\n"
266      << "250-Sending entity             #rcpt\r\n";
267   for (userinfo *ui = uitab.first (); ui; ui = uitab.next (ui)) {
268     ui->decay ();
269     sb.fmt ("250-%-26s  % 4d\r\n", ui->user.cstr (),
270 	    ui->ndeliv/userinfo::msgmult);
271   }
272 
273   sb << "250 ---------------------------------------\r\n";
274 }
275