/* Copyright 1995 J"orn Rennecke */
#include "config.h"
#ifdef ACCESS_CONTROL
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include "common.h"
#include "comm.h"
#define MAX_MESSAGE_LENGTH 256
static struct access_address {
int32 addr, mask;
int32 hour_mask;
int32 wday_mask;
struct access_class *class;
struct access_address *next;
} *all_access_addresses = 0;
static struct access_class {
long id;
mp_int max_usage, usage;
struct access_class *next;
char message[8];
} *all_access_classes = 0;
static time_t last_read_time = 0;
extern void refresh_access_data(void (*)(struct in_addr*, long *));
static struct access_class *find_access_class(struct in_addr *full_addr) {
int32 addr;
struct access_address *aap;
time_t seconds;
struct tm *tm_p;
addr = full_addr->s_addr;
tm_p = 0;
for (aap = all_access_addresses; aap; aap = aap->next) {
#if DEBUG_ACCESS_CHECK
fprintf(stderr, "'%s', %ld %ld\n",
inet_ntoa(*(struct in_addr*)&aap->addr),
(long)aap->class->max_usage, (long)aap->class->usage);
#endif
if ((aap->addr ^ addr) & aap->mask)
continue;
if (aap->wday_mask >= 0) {
if (!tm_p) {
time(&seconds);
tm_p = localtime(&seconds);
#if DEBUG_ACCESS_CHECK
fprintf(stderr, "h:%d w:%d\n", tm_p->tm_hour, tm_p->tm_wday);
#endif
}
if ( !((1 << tm_p->tm_hour) & aap->hour_mask) )
continue;
if ( !((1 << tm_p->tm_wday) & aap->wday_mask) )
continue;
}
return aap->class;
}
return 0;
}
static void add_access_entry(struct in_addr *full_addr, long *idp) {
struct access_class *acp;
acp = find_access_class(full_addr);
if (acp) {
acp->usage++;
*idp = acp->id;
}
}
static void read_access_file() {
FILE *infp;
struct access_address *aap, *next_aap, **last;
struct access_class *acp, *next_acp;
char message[MAX_MESSAGE_LENGTH];
int i;
int32 addr, mask;
for (aap = all_access_addresses; aap; aap = next_aap) {
next_aap = aap->next;
free((char *)aap);
}
for (acp = all_access_classes; acp; acp = next_acp) {
next_acp = acp->next;
free((char *)acp);
}
all_access_classes = 0;
infp = fopen(ACCESS_FILE, "r");
last = &all_access_addresses;
if (infp) for(addr = mask = 0;;) {
long max_usage, class_id;
int first_hour, last_hour;
addr <<= 8;
mask <<= 8;
if (fscanf(infp, "%9[^.:\n]%[.:]", message, message+12) != 2 ||
*message == '#')
{
do {
i = fgetc(infp);
if (i == EOF)
goto file_end;
} while(i != '\n');
addr = mask = 0;
continue;
}
if (*message != '*') {
int j;
j = atoi(message);
if ((unsigned)j > 0xff)
break;
addr += j;
mask += 0xff;
}
if (message[12] == '.')
continue;
max_usage = 0;
message[0] = '\0';
i = fscanf(infp, "%ld:%ld:%d:%d:",
&class_id, &max_usage, &first_hour, &last_hour);
if (!i)
break;
aap = malloc(sizeof *aap);
if (!aap)
break;
*last = aap;
aap->addr = htonl(addr);
aap->mask = htonl(mask);
aap->wday_mask = -1;
if (i == 4) {
if (first_hour || last_hour) {
aap->wday_mask = 0x7f;
if (first_hour <= last_hour) {
aap->hour_mask = (2 << last_hour) - (1 << first_hour);
} else {
aap->hour_mask = -(1 << first_hour) + (2 << last_hour) - 1;
}
}
} else if (i == 2) {
char c, c2[2];
for (;;) {
c = 'm';
fscanf(infp, "%c %1[=]", &c, c2);
switch(c) {
case 'w':
{
int32 *maskp;
maskp = &aap->wday_mask;
goto get_mask;
case 'h':
maskp = &aap->hour_mask;
get_mask:
mask = 0;
do {
int j, k;
*c2 = '\0';
if (!fscanf(infp, "%d %1[-,:] ", &j, c2))
break;
if (*c2 == '-') {
k = 24;
fscanf(infp, "%d %1[,:] ", &k, c2);
if (j <= k) {
mask |= (2 << k) - (1 << j);
} else {
mask |= -(1 << j) + (2 << k) - 1;
}
} else {
mask |= 1 << j;
}
} while (*c2 == ',');
*maskp = mask;
aap->wday_mask &= 0x7f; /* make sure it's not negative */
continue;
}
default:
ungetc(c, infp);
case 'm':
break;
}
break;
}
}
fgets(message, sizeof message, infp);
for (acp = all_access_classes; acp; acp = acp->next) {
if (acp->id == class_id)
break;
}
i = strlen(message);
if (message[i-1] == '\n')
message[--i] = '\0';
if (!acp) {
acp =
malloc(sizeof *acp - sizeof acp->message + 1 + i);
if (!acp) {
free((char *)aap);
break;
}
acp->id = class_id;
acp->max_usage = max_usage == -1 ? MAXINT : max_usage;
acp->usage = 0;
strcpy(acp->message, message);
acp->next = all_access_classes;
all_access_classes = acp;
}
aap->class = acp;
last = &aap->next;
addr = mask = 0;
}
file_end:
*last = 0;
refresh_access_data(add_access_entry);
}
char *allow_host_access(full_addr, idp)
struct in_addr *full_addr;
long *idp;
{
struct stat statbuf;
struct access_class *acp;
if (!stat(ACCESS_FILE, &statbuf) && statbuf.st_mtime > last_read_time) {
last_read_time = statbuf.st_mtime;
read_access_file();
}
acp = find_access_class(full_addr);
if (acp) {
if (acp->usage >= acp->max_usage)
return acp->message;
acp->usage++;
*idp = acp->id;
return 0;
}
return "No matching entry";
}
void release_host_access(num)
long num;
{
struct access_class *acp;
#if DEBUG_ACCESS_CHECK
fprintf(stderr, "release_host_access %ld called.\n", num);
#endif
for (acp = all_access_classes; acp; acp = acp->next) {
if (acp->id != num)
continue;
acp->usage--;
break;
}
}
#endif /* ACCESS_RESTRICTED */