NetBSD/usr.sbin/sendmail/contrib/xla/xla.c

533 lines
12 KiB
C

/*
* (C) Copyright 1993-94, Herve Schauer Consultants
*
* This module written by Christophe.Wolfhugel@hsc-sec.fr
* is to be used under the same conditions and terms (and absence
* or warranties) than the other modules of the Sendmail package.
*
* ABSOLUTELY NO WARRANTY. USE AT YOUR OWN RISKS.
*
* Version: 940417, applied a patch from Paul Graham <pjg@acsu.buffalo.edu>
* (lockfile syntax in xla.c did not reflect anymore the
* new one used by sendmail, now fine).
*
*/
#ifdef XLA
#ifndef MAXLARULES
# define MAXLARULES 20
#endif
# include "sendmail.h"
typedef struct {
short queue; /* # of connexions to have queueing */
short reject; /* # of conn. to reject */
short num; /* # of increments this process */
char *mask; /* Mask (domain) */
} XLARULE;
char *XlaFname; /* Work file name */
char XlaHostName[1024]; /* Temporary */
int XlaNext; /* # of XlaRules */
pid_t XlaPid; /* Pid updating the tables */
XLARULE XlaRules[MAXLARULES]; /* The rules themselves */
short XlaCtr[MAXLARULES]; /* Counter (for work file only) */
extern bool lockfile();
/*
** XLAMATCH -- Matches a fnmatch like expression.
**
** Parameters:
** mask -- mask to match the string too;
** name -- string.
**
** Mask can either be a plain string or a simplified fnmatch like mask:
** *.string or string.*
** No other alternatives are accepted.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
bool
XlaMatch(mask, name)
char *mask, *name;
{
int l1, l2;
l1 = strlen(mask); l2 = strlen(name);
if (l1 == 1 && mask[0] == '*') return(TRUE);
if (mask[0] == '*' && mask[1] == '.') {
if (l2 < (l1 - 2)) return(FALSE);
if (strcasecmp(&mask[2], name) == 0) return(TRUE);
if (strcasecmp(&mask[1], name + l2 - l1 + 1) == 0) return(TRUE);
return(FALSE);
}
if (l1 < 3) return(FALSE);
if (mask[l1 -1] == '*') {
if (l2 < l1 - 1) return(FALSE);
if (strncasecmp(mask, name, l1 - 1) == 0) return(TRUE);
return(FALSE);
}
if (strcasecmp(mask, name) == 0) return(TRUE);
return(FALSE);
}
/*
** XLAZERO -- Zeroes the used variables
**
** Just initializes some variables, called once at sendmail
** startup.
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
xla_zero()
{
if (tTd(59, 1)) {
printf("xla_zero\n");
}
XlaFname = NULL;
XlaNext = 0;
XlaPid = 0;
memset((char *) &XlaRules[0], 0, sizeof(XLARULE) * MAXLARULES);
}
/*
** XLAINIT -- initialized extended load average stuff
**
** This routine handles the L lines appearing in the configuration
** file.
**
** L/etc/sendmail.la indicates the working file
** Lmask #1 #2 Xtended LA to apply to mask
** #1 = Queueing # of connections
** #2 = Reject connections.
**
** Parameters:
** line -- the cf file line to parse.
**
** Returns:
** none.
**
** Side Effects:
** Builds several internal tables.
*/
xla_init(line)
char *line;
{
char *s;
if (tTd(59, 1)) {
printf("xla_init line: %s\n", line);
}
if (XlaFname == NULL && *line == '/') { /* Work file name */
XlaFname = newstr(line);
if (tTd(59, 10))
printf("xla_init: fname = %s\n", XlaFname);
return;
}
if (XlaNext == MAXLARULES) {
syserr("too many xla rules defined (%d max)", MAXLARULES);
return;
}
s = strtok(line, " \t");
if (s == NULL) {
syserr("xla: line unparseable");
return;
}
XlaRules[XlaNext].mask = newstr(s);
s = strtok(NULL, " \t");
if (s == NULL) {
syserr("xla: line unparseable");
return;
}
XlaRules[XlaNext].queue = atoi(s);
s = strtok(NULL, " \t");
if (s == NULL) {
syserr("xla: line unparseable");
return;
}
XlaRules[XlaNext].reject = atoi(s);
if (tTd(59, 10))
printf("xla_init: rule #%d = %s q=%d r=%d\n", XlaNext,
XlaRules[XlaNext].mask,
XlaRules[XlaNext].queue, XlaRules[XlaNext].reject);
XlaNext++;
}
/*
** XLACREATEFILE -- Create the working file
**
** Tries to create the working file, called each time sendmail is
** invoked with the -bd option.
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** Creates the working file (sendmail.la) and zeroes it.
*/
xla_create_file()
{
int fd, i;
if (tTd(59, 1))
printf("xla_create_file:\n");
if (XlaFname == NULL) return;
fd = open(XlaFname, O_RDWR|O_CREAT, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_create_file: open failed");
return;
}
if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_create_file: can't set lock");
return;
}
if (ftruncate(fd, 0) == -1) {
close(fd);
XlaFname = NULL;
syserr("xla_create_file: can't truncate XlaFname");
return;
}
if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
XlaFname == NULL;
syserr("xla_create_file: can't write XlaFname");
}
close(fd);
}
/*
** XLASMTPOK -- Checks if all slots are in use
**
** Check is there are still some slots available for an SMTP
** connection.
**
** Parameters:
** none.
**
** Returns:
** TRUE -- slots are available;
** FALSE -- no more slots.
**
** Side Effects:
** Reads a file, uses a lock and updates sendmail.la if a slot
** is free for use.
*/
bool
xla_smtp_ok()
{
int fd, i;
if (tTd(59, 1))
printf("xla_smtp_ok:\n");
if (XlaFname == NULL) return(TRUE);
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_smtp_ok: open failed");
return(TRUE);
}
if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_smtp_ok: can't set lock");
return(TRUE);
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_smtp_ok: can't read XlaFname");
return(TRUE);
}
close(fd);
for (i = 0; i < XlaNext; i++) {
if (XlaCtr[i] < XlaRules[i].reject)
return(TRUE);
}
return(FALSE);
}
/*
** XLAHOSTOK -- Can we accept a connection from this host
**
** Check the quota for the indicated host
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** TRUE -- we can accept the connection;
** FALSE -- we must refuse the connection.1
**
** Side Effects:
** Reads and writes a file, uses a lock and still updates
** sendmail.la is a slot gets assigned.
*/
bool
xla_host_ok(name)
char *name;
{
int fd, i;
if (tTd(59, 1))
printf("xla_host_ok:\n");
if (XlaFname == NULL) return(TRUE);
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_host_ok: open failed");
return(TRUE);
}
XlaPid = getpid();
if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_host_ok: can't set lock");
return(TRUE);
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_smtp_ok: can't read XlaFname");
return(TRUE);
}
strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
XlaHostName[sizeof(XlaHostName) -1] = 0;
i = strlen(name) - 1;
if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
for (i = 0; i < XlaNext; i++) {
if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
if (XlaCtr[i] < XlaRules[i].reject) {
if (XlaRules[i].num++ == 0) {
XlaCtr[i]++;
lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
XlaFname = NULL;
}
close(fd);
return(TRUE);
}
close(fd);
return(FALSE);
}
}
close(fd);
return(TRUE);
}
/*
** XLANOQUEUEOK -- Can we sent this message to the remote host
**
** Check if we can send to the remote host
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** TRUE -- we can send the message to the remote site;
** FALSE -- we can't connect the remote host, queue.
**
** Side Effects:
** Reads and writes a file, uses a lock.
** And still updates the sendmail.la file.
*/
bool
xla_noqueue_ok(name)
char *name;
{
int fd, i;
if (tTd(59, 1))
printf("xla_noqueue_ok:\n");
if (XlaFname == NULL) return(TRUE);
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_noqueue_ok: open failed");
return(TRUE);
}
if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_noqueue_ok: can't set lock");
return(TRUE);
}
XlaPid = getpid();
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_noqueue_ok: can't read XlaFname");
return(TRUE);
}
strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
XlaHostName[sizeof(XlaHostName) -1] = 0;
i = strlen(name) - 1;
if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
for (i = 0; i < XlaNext; i++) {
if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
if (XlaCtr[i] < XlaRules[i].queue) {
if (XlaRules[i].num++ == 0) {
XlaCtr[i]++;
lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
XlaFname = NULL;
}
close(fd);
return(TRUE);
}
close(fd);
return(FALSE);
}
}
close(fd);
return(TRUE);
}
/*
** XLAHOSTEND -- Notice that a connection is terminated.
**
** Updates the counters to reflect the end of an SMTP session
** (in or outgoing).
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** none.
**
** Side Effects:
** Reads and writes a file, uses a lock.
** And still updates sendmail.la.
*/
xla_host_end(name)
char *name;
{
int fd, i;
if (tTd(59, 1))
printf("xla_host_end:\n");
if (XlaFname == NULL || XlaPid != getpid()) return;
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_host_end: open failed");
return;
}
if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_host_end: can't set lock");
return;
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_host_end: can't read XlaFname");
return(TRUE);
}
strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
XlaHostName[sizeof(XlaHostName) -1] = 0;
i = strlen(name) - 1;
if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
for (i = 0; i < XlaNext; i++) {
if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
if (XlaRules[i].num > 0 && XlaRules[i].num-- == 1) {
if (XlaCtr[i]) XlaCtr[i]--;
lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i]))
!= sizeof(XlaCtr[i]))
XlaFname = NULL;
}
close(fd);
return;
}
}
close(fd);
}
/*
** XLAALLEND -- Mark all connections as closed.
**
** Generally due to an emergency exit.
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** none.
**
** Side Effects:
** Reads and writes a file, uses a lock.
** And guess what: updates sendmail.la.
*/
xla_all_end()
{
int fd, i;
if (tTd(59, 1))
printf("xla_all_end:\n");
if (XlaFname == NULL || XlaPid != getpid()) return;
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_all_end: open failed");
return;
}
if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_all_end: can't set lock");
return;
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_all_end: can't read XlaFname");
return(TRUE);
}
for (i = 0; i < XlaNext; i++) {
if (XlaCtr[i] > 0 && XlaRules[i].num > 0) {
XlaCtr[i]--; XlaRules[i].num = 0;
}
}
lseek(fd, 0, SEEK_SET);
if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
XlaFname = NULL;
}
close(fd);
}
#endif /* XLA */