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