xref: /386bsd/usr/src/usr.sbin/sendmail/contrib/xla/xla.c (revision a2142627)
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