1 /*
2 * (C) Copyright 1993-94, Herve Schauer Consultants
3 *
4 * This module written by Christophe.Wolfhugel@hsc-sec.fr
5 * is to be used under the same conditions and terms (and absence
6 * or warranties) than the other modules of the Sendmail package.
7 *
8 * ABSOLUTELY NO WARRANTY. USE AT YOUR OWN RISKS.
9 *
10 * Version: 940417, applied a patch from Paul Graham <pjg@acsu.buffalo.edu>
11 * (lockfile syntax in xla.c did not reflect anymore the
12 * new one used by sendmail, now fine).
13 *
14 */
15
16
17 #ifdef XLA
18
19 #ifndef MAXLARULES
20 # define MAXLARULES 20
21 #endif
22
23 # include "sendmail.h"
24
25 typedef struct {
26 short queue; /* # of connexions to have queueing */
27 short reject; /* # of conn. to reject */
28 short num; /* # of increments this process */
29 char *mask; /* Mask (domain) */
30 } XLARULE;
31
32 char *XlaFname; /* Work file name */
33 char XlaHostName[1024]; /* Temporary */
34 int XlaNext; /* # of XlaRules */
35 pid_t XlaPid; /* Pid updating the tables */
36 XLARULE XlaRules[MAXLARULES]; /* The rules themselves */
37 short XlaCtr[MAXLARULES]; /* Counter (for work file only) */
38
39 extern bool lockfile();
40
41 /*
42 ** XLAMATCH -- Matches a fnmatch like expression.
43 **
44 ** Parameters:
45 ** mask -- mask to match the string too;
46 ** name -- string.
47 **
48 ** Mask can either be a plain string or a simplified fnmatch like mask:
49 ** *.string or string.*
50 ** No other alternatives are accepted.
51 **
52 ** Returns:
53 ** none.
54 **
55 ** Side Effects:
56 ** none.
57 */
58
59 bool
XlaMatch(mask,name)60 XlaMatch(mask, name)
61 char *mask, *name;
62 {
63 int l1, l2;
64
65 l1 = strlen(mask); l2 = strlen(name);
66 if (l1 == 1 && mask[0] == '*') return(TRUE);
67 if (mask[0] == '*' && mask[1] == '.') {
68 if (l2 < (l1 - 2)) return(FALSE);
69 if (strcasecmp(&mask[2], name) == 0) return(TRUE);
70 if (strcasecmp(&mask[1], name + l2 - l1 + 1) == 0) return(TRUE);
71 return(FALSE);
72 }
73 if (l1 < 3) return(FALSE);
74 if (mask[l1 -1] == '*') {
75 if (l2 < l1 - 1) return(FALSE);
76 if (strncasecmp(mask, name, l1 - 1) == 0) return(TRUE);
77 return(FALSE);
78 }
79 if (strcasecmp(mask, name) == 0) return(TRUE);
80 return(FALSE);
81 }
82
83 /*
84 ** XLAZERO -- Zeroes the used variables
85 **
86 ** Just initializes some variables, called once at sendmail
87 ** startup.
88 **
89 ** Parameters:
90 ** none.
91 **
92 ** Returns:
93 ** none.
94 **
95 ** Side Effects:
96 ** none.
97 */
98
xla_zero()99 xla_zero()
100 {
101 if (tTd(59, 1)) {
102 printf("xla_zero\n");
103 }
104 XlaFname = NULL;
105 XlaNext = 0;
106 XlaPid = 0;
107 memset((char *) &XlaRules[0], 0, sizeof(XLARULE) * MAXLARULES);
108 }
109
110
111 /*
112 ** XLAINIT -- initialized extended load average stuff
113 **
114 ** This routine handles the L lines appearing in the configuration
115 ** file.
116 **
117 ** L/etc/sendmail.la indicates the working file
118 ** Lmask #1 #2 Xtended LA to apply to mask
119 ** #1 = Queueing # of connections
120 ** #2 = Reject connections.
121 **
122 ** Parameters:
123 ** line -- the cf file line to parse.
124 **
125 ** Returns:
126 ** none.
127 **
128 ** Side Effects:
129 ** Builds several internal tables.
130 */
131
xla_init(line)132 xla_init(line)
133 char *line;
134 {
135 char *s;
136
137 if (tTd(59, 1)) {
138 printf("xla_init line: %s\n", line);
139 }
140 if (XlaFname == NULL && *line == '/') { /* Work file name */
141 XlaFname = newstr(line);
142 if (tTd(59, 10))
143 printf("xla_init: fname = %s\n", XlaFname);
144 return;
145 }
146 if (XlaNext == MAXLARULES) {
147 syserr("too many xla rules defined (%d max)", MAXLARULES);
148 return;
149 }
150 s = strtok(line, " \t");
151 if (s == NULL) {
152 syserr("xla: line unparseable");
153 return;
154 }
155 XlaRules[XlaNext].mask = newstr(s);
156 s = strtok(NULL, " \t");
157 if (s == NULL) {
158 syserr("xla: line unparseable");
159 return;
160 }
161 XlaRules[XlaNext].queue = atoi(s);
162 s = strtok(NULL, " \t");
163 if (s == NULL) {
164 syserr("xla: line unparseable");
165 return;
166 }
167 XlaRules[XlaNext].reject = atoi(s);
168 if (tTd(59, 10))
169 printf("xla_init: rule #%d = %s q=%d r=%d\n", XlaNext,
170 XlaRules[XlaNext].mask,
171 XlaRules[XlaNext].queue, XlaRules[XlaNext].reject);
172 XlaNext++;
173 }
174
175
176 /*
177 ** XLACREATEFILE -- Create the working file
178 **
179 ** Tries to create the working file, called each time sendmail is
180 ** invoked with the -bd option.
181 **
182 ** Parameters:
183 ** none.
184 **
185 ** Returns:
186 ** none.
187 **
188 ** Side Effects:
189 ** Creates the working file (sendmail.la) and zeroes it.
190 */
191
xla_create_file()192 xla_create_file()
193 {
194 int fd, i;
195
196 if (tTd(59, 1))
197 printf("xla_create_file:\n");
198 if (XlaFname == NULL) return;
199 fd = open(XlaFname, O_RDWR|O_CREAT, 0644);
200 if (fd == -1) {
201 XlaFname = NULL;
202 syserr("xla_create_file: open failed");
203 return;
204 }
205 if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
206 close(fd);
207 XlaFname = NULL;
208 syserr("xla_create_file: can't set lock");
209 return;
210 }
211 if (ftruncate(fd, 0) == -1) {
212 close(fd);
213 XlaFname = NULL;
214 syserr("xla_create_file: can't truncate XlaFname");
215 return;
216 }
217 if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
218 XlaFname == NULL;
219 syserr("xla_create_file: can't write XlaFname");
220 }
221 close(fd);
222 }
223
224
225 /*
226 ** XLASMTPOK -- Checks if all slots are in use
227 **
228 ** Check is there are still some slots available for an SMTP
229 ** connection.
230 **
231 ** Parameters:
232 ** none.
233 **
234 ** Returns:
235 ** TRUE -- slots are available;
236 ** FALSE -- no more slots.
237 **
238 ** Side Effects:
239 ** Reads a file, uses a lock and updates sendmail.la if a slot
240 ** is free for use.
241 */
242
243 bool
xla_smtp_ok()244 xla_smtp_ok()
245 {
246 int fd, i;
247
248 if (tTd(59, 1))
249 printf("xla_smtp_ok:\n");
250 if (XlaFname == NULL) return(TRUE);
251 fd = open(XlaFname, O_RDWR, 0644);
252 if (fd == -1) {
253 XlaFname = NULL;
254 syserr("xla_smtp_ok: open failed");
255 return(TRUE);
256 }
257 if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
258 close(fd);
259 XlaFname = NULL;
260 syserr("xla_smtp_ok: can't set lock");
261 return(TRUE);
262 }
263 if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
264 close(fd);
265 XlaFname = NULL;
266 syserr("xla_smtp_ok: can't read XlaFname");
267 return(TRUE);
268 }
269 close(fd);
270 for (i = 0; i < XlaNext; i++) {
271 if (XlaCtr[i] < XlaRules[i].reject)
272 return(TRUE);
273 }
274 return(FALSE);
275 }
276
277
278 /*
279 ** XLAHOSTOK -- Can we accept a connection from this host
280 **
281 ** Check the quota for the indicated host
282 **
283 ** Parameters:
284 ** name -- host name or IP# (string)
285 **
286 ** Returns:
287 ** TRUE -- we can accept the connection;
288 ** FALSE -- we must refuse the connection.1
289 **
290 ** Side Effects:
291 ** Reads and writes a file, uses a lock and still updates
292 ** sendmail.la is a slot gets assigned.
293 */
294
295 bool
xla_host_ok(name)296 xla_host_ok(name)
297 char *name;
298 {
299 int fd, i;
300
301 if (tTd(59, 1))
302 printf("xla_host_ok:\n");
303 if (XlaFname == NULL) return(TRUE);
304 fd = open(XlaFname, O_RDWR, 0644);
305 if (fd == -1) {
306 XlaFname = NULL;
307 syserr("xla_host_ok: open failed");
308 return(TRUE);
309 }
310 XlaPid = getpid();
311 if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
312 close(fd);
313 XlaFname = NULL;
314 syserr("xla_host_ok: can't set lock");
315 return(TRUE);
316 }
317 if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
318 close(fd);
319 XlaFname = NULL;
320 syserr("xla_smtp_ok: can't read XlaFname");
321 return(TRUE);
322 }
323 strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
324 XlaHostName[sizeof(XlaHostName) -1] = 0;
325 i = strlen(name) - 1;
326 if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
327 for (i = 0; i < XlaNext; i++) {
328 if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
329 if (XlaCtr[i] < XlaRules[i].reject) {
330 if (XlaRules[i].num++ == 0) {
331 XlaCtr[i]++;
332 lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
333 if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
334 XlaFname = NULL;
335 }
336 close(fd);
337 return(TRUE);
338 }
339 close(fd);
340 return(FALSE);
341 }
342 }
343 close(fd);
344 return(TRUE);
345 }
346
347 /*
348 ** XLANOQUEUEOK -- Can we sent this message to the remote host
349 **
350 ** Check if we can send to the remote host
351 **
352 ** Parameters:
353 ** name -- host name or IP# (string)
354 **
355 ** Returns:
356 ** TRUE -- we can send the message to the remote site;
357 ** FALSE -- we can't connect the remote host, queue.
358 **
359 ** Side Effects:
360 ** Reads and writes a file, uses a lock.
361 ** And still updates the sendmail.la file.
362 */
363
364 bool
xla_noqueue_ok(name)365 xla_noqueue_ok(name)
366 char *name;
367 {
368 int fd, i;
369
370 if (tTd(59, 1))
371 printf("xla_noqueue_ok:\n");
372 if (XlaFname == NULL) return(TRUE);
373 fd = open(XlaFname, O_RDWR, 0644);
374 if (fd == -1) {
375 XlaFname = NULL;
376 syserr("xla_noqueue_ok: open failed");
377 return(TRUE);
378 }
379 if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
380 close(fd);
381 XlaFname = NULL;
382 syserr("xla_noqueue_ok: can't set lock");
383 return(TRUE);
384 }
385 XlaPid = getpid();
386 if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
387 close(fd);
388 XlaFname = NULL;
389 syserr("xla_noqueue_ok: can't read XlaFname");
390 return(TRUE);
391 }
392 strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
393 XlaHostName[sizeof(XlaHostName) -1] = 0;
394 i = strlen(name) - 1;
395 if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
396 for (i = 0; i < XlaNext; i++) {
397 if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
398 if (XlaCtr[i] < XlaRules[i].queue) {
399 if (XlaRules[i].num++ == 0) {
400 XlaCtr[i]++;
401 lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
402 if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
403 XlaFname = NULL;
404 }
405 close(fd);
406 return(TRUE);
407 }
408 close(fd);
409 return(FALSE);
410 }
411 }
412 close(fd);
413 return(TRUE);
414 }
415
416
417 /*
418 ** XLAHOSTEND -- Notice that a connection is terminated.
419 **
420 ** Updates the counters to reflect the end of an SMTP session
421 ** (in or outgoing).
422 **
423 ** Parameters:
424 ** name -- host name or IP# (string)
425 **
426 ** Returns:
427 ** none.
428 **
429 ** Side Effects:
430 ** Reads and writes a file, uses a lock.
431 ** And still updates sendmail.la.
432 */
433
xla_host_end(name)434 xla_host_end(name)
435 char *name;
436 {
437 int fd, i;
438
439 if (tTd(59, 1))
440 printf("xla_host_end:\n");
441 if (XlaFname == NULL || XlaPid != getpid()) return;
442 fd = open(XlaFname, O_RDWR, 0644);
443 if (fd == -1) {
444 XlaFname = NULL;
445 syserr("xla_host_end: open failed");
446 return;
447 }
448 if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
449 close(fd);
450 XlaFname = NULL;
451 syserr("xla_host_end: can't set lock");
452 return;
453 }
454 if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
455 close(fd);
456 XlaFname = NULL;
457 syserr("xla_host_end: can't read XlaFname");
458 return(TRUE);
459 }
460 strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
461 XlaHostName[sizeof(XlaHostName) -1] = 0;
462 i = strlen(name) - 1;
463 if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
464 for (i = 0; i < XlaNext; i++) {
465 if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
466 if (XlaRules[i].num > 0 && XlaRules[i].num-- == 1) {
467 if (XlaCtr[i]) XlaCtr[i]--;
468 lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
469 if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i]))
470 != sizeof(XlaCtr[i]))
471 XlaFname = NULL;
472 }
473 close(fd);
474 return;
475 }
476 }
477 close(fd);
478 }
479
480 /*
481 ** XLAALLEND -- Mark all connections as closed.
482 **
483 ** Generally due to an emergency exit.
484 **
485 ** Parameters:
486 ** name -- host name or IP# (string)
487 **
488 ** Returns:
489 ** none.
490 **
491 ** Side Effects:
492 ** Reads and writes a file, uses a lock.
493 ** And guess what: updates sendmail.la.
494 */
495
xla_all_end()496 xla_all_end()
497 {
498 int fd, i;
499
500 if (tTd(59, 1))
501 printf("xla_all_end:\n");
502 if (XlaFname == NULL || XlaPid != getpid()) return;
503 fd = open(XlaFname, O_RDWR, 0644);
504 if (fd == -1) {
505 XlaFname = NULL;
506 syserr("xla_all_end: open failed");
507 return;
508 }
509 if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
510 close(fd);
511 XlaFname = NULL;
512 syserr("xla_all_end: can't set lock");
513 return;
514 }
515 if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
516 close(fd);
517 XlaFname = NULL;
518 syserr("xla_all_end: can't read XlaFname");
519 return(TRUE);
520 }
521 for (i = 0; i < XlaNext; i++) {
522 if (XlaCtr[i] > 0 && XlaRules[i].num > 0) {
523 XlaCtr[i]--; XlaRules[i].num = 0;
524 }
525 }
526 lseek(fd, 0, SEEK_SET);
527 if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
528 XlaFname = NULL;
529 }
530 close(fd);
531 }
532 #endif /* XLA */
533