cfg.c 5.95 KB
/* Copyright (C) 1998,2001 Free Software Foundation, Inc.

   This file is part of GNU Inetutils.

   GNU Inetutils is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GNU Inetutils is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Inetutils; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA. */

#include "comsat.h"
#include <argcv.h>

typedef struct netdef netdef_t;

struct netdef
{
  netdef_t *next;
  unsigned int ipaddr;
  unsigned int netmask;
};

#define ACT_ALLOW  0
#define ACT_DENY   1

typedef struct acl acl_t;

struct acl
{
  acl_t *next;
  netdef_t *netlist;
  int action;
};


acl_t *acl_head, *acl_tail;

#define DOTTED_QUAD_LEN 16

static int
read_address (char **line_ptr, char *ptr)
{
  char *startp = *line_ptr;
  char *endp;
  int dotcount = 0;

  for (endp = startp; *endp; endp++, ptr++)
    if (!(isdigit (*endp) || *endp == '.'))
      break;
    else if (endp < startp + DOTTED_QUAD_LEN)
      {
	if (*endp == '.')
	  dotcount++;
	*ptr = *endp;
      }
    else
      break;
  *line_ptr = endp;
  *ptr = 0;
  return dotcount;
}

static netdef_t *
netdef_parse (char *str)
{
  unsigned int ipaddr, netmask;
  netdef_t *netdef;
  char ipbuf[DOTTED_QUAD_LEN+1];

  if (strcmp (str, "any") == 0)
    {
      ipaddr = 0;
      netmask = 0;
    }
  else
    {
      read_address (&str, ipbuf);
      ipaddr = inet_addr (ipbuf);
      if (ipaddr == INADDR_NONE)
	return NULL;
      if (*str == 0)
	netmask = 0xfffffffful;
      else if (*str != '/')
	return NULL;
      else
	{
	  str++;
	  if (read_address (&str, ipbuf) == 0)
	    {
	      /* netmask length */
	      unsigned int len = strtoul (ipbuf, NULL, 0);
	      if (len > 32)
		return NULL;
	      netmask = 0xfffffffful >> (32-len);
	      netmask <<= (32-len);
	    }
	  else
	    netmask = inet_network (ipbuf);
	  netmask = htonl (netmask);
	}
    }

  netdef = malloc (sizeof *netdef);
  if (!netdef)
    {
      syslog (LOG_ERR, "out of memory");
      exit (1);
    }

  netdef->next = NULL;
  netdef->ipaddr = ipaddr;
  netdef->netmask = netmask;

  return netdef;
}

void
read_config (const char *config_file)
{
  FILE *fp;
  int line;
  char buf[128];
  char *ptr;

  if (!config_file)
    return;

  fp = fopen (config_file, "r");
  if (!fp)
    {
      syslog (LOG_ERR, "can't open config file %s: %m", config_file);
      return;
    }

  line = 0;
  while ((ptr = fgets (buf, sizeof buf, fp)))
    {
      int len, i;
      int argc;
      char **argv;
      int  action;
      netdef_t *head, *tail;
      acl_t *acl;

      line++;
      len = strlen (ptr);
      if (len > 0 && ptr[len-1] == '\n')
	ptr[--len] = 0;

      while (*ptr && isspace (*ptr))
	ptr++;
      if (!*ptr || *ptr == '#')
	continue;

      argcv_get (ptr, "", NULL, &argc, &argv);
      if (argc < 2)
	{
	  syslog (LOG_ERR, "%s:%d: too few fields", config_file, line);
	  argcv_free (argc, argv);
	  continue;
	}

      if (strcmp (argv[0], "allow-biffrc") == 0)
	{
	  if (strcmp (argv[1], "yes") == 0)
	    allow_biffrc = 1;
	  else if (strcmp (argv[1], "no") == 0)
	    allow_biffrc = 0;
	  else
	    syslog (LOG_ERR, "%s:%d: yes or no expected", config_file, line);
	}
      else if (strcmp (argv[0], "max-requests") == 0)
	maxrequests = strtoul (argv[1], NULL, 0);
      else if (strcmp (argv[0], "request-control-interval") == 0)
	request_control_interval = strtoul (argv[1], NULL, 0);
      else if (strcmp (argv[0], "overflow-control-interval") == 0)
	overflow_control_interval = strtoul (argv[1], NULL, 0);
      else if (strcmp (argv[0], "overflow-delay-time") == 0)
	overflow_delay_time = strtoul (argv[1], NULL, 0);
      else if (strcmp (argv[0], "max-lines") == 0)
	maxlines = strtoul (argv[1], NULL, 0);
      else if (strcmp (argv[0], "acl") == 0)
	{
	  if (strcmp (argv[1], "allow") == 0)
	    action = ACT_ALLOW;
	  else if (strcmp (argv[1], "deny") == 0)
	    action = ACT_DENY;
	  else
	    {
	      syslog (LOG_ERR, "%s:%d: unknown keyword", config_file, line);
	      argcv_free (argc, argv);
	      continue;
	    }

	  head = tail = NULL;
	  for (i = 2; i < argc; i++)
	    {
	      netdef_t *cur = netdef_parse (argv[i]);
	      if (!cur)
		{
		  syslog (LOG_ERR, "%s:%d: can't parse netdef: %s",
			  config_file, line, argv[i]);
		  continue;
		}
	      if (!tail)
		head = cur;
	      else
		tail->next = cur;
	      tail = cur;
	    }

	  argcv_free (argc, argv);

	  acl = malloc (sizeof *acl);
	  if (!acl)
	    {
	      syslog (LOG_CRIT, "out of memory");
	      exit (1);
	    }
	  acl->next = NULL;
	  acl->action = action;
	  acl->netlist = head;

	  if (!acl_tail)
	    acl_head = acl;
	  else
	    acl_tail->next = acl;
	  acl_tail = acl;
	}
    }
  fclose (fp);
}

static void
netdef_free (netdef_t *netdef)
{
  netdef_t *next;

  while (netdef)
    {
      next = netdef->next;
      free (netdef);
      netdef = next;
    }
}

static void
acl_free (acl_t *acl)
{
  acl_t *next;

  while (acl)
    {
      next = acl->next;
      netdef_free (acl->netlist);
      free (acl);
      acl = next;
    }
}

/*NOTE: currently unused. */
static void
discard_acl (acl_t *mark)
{
  if (mark)
    {
      acl_free (mark->next);
      acl_tail = mark;
      acl_tail->next = NULL;
    }
  else
    acl_head = acl_tail = NULL;
}

int
acl_match (struct sockaddr_in *sa_in)
{
  acl_t *acl;
  unsigned int ip;

  ip = sa_in->sin_addr.s_addr;
  for (acl = acl_head; acl; acl = acl->next)
    {
      netdef_t *net;

      for (net = acl->netlist; net; net = net->next)
	{
	  if (net->ipaddr == (ip & net->netmask))
	    return acl->action;
	}
    }
  return ACT_ALLOW;
}