/*
 *  policy.h  -- ZMailer's smtpserver's runtime address acceptance
 *               policy database mechanisms.
 *
 *  By Matti Aarnio <mea@nic.funet.fi> after the model of
 *  Gabor Kiss's <kissg@sztaki.hu> first edition, which
 *  did require a router running in parallel to resolve
 *  each and all of the SMTP session's address processings.
 *  (Uhh... ... the load..)
 *
 */

#include "hostenv.h"

#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h> /* For AF_**** */
#include <stdio.h>

#include "policy.h"

static int parse_gen_policy ();
static int parse_ip_policy ();

/* These are subroutines for the policy compiler, these
   are NOT subroutines for the SMTPSERVER to use  */

/* XX:XX:XX! ASSUMPTION: 8 BITS PER CHAR! */


void mask_ip_bits(ipnum, width, maxwidth)
unsigned char *ipnum;
int width, maxwidth;
{
	int i, bytewidth, bytemaxwidth;

	bytemaxwidth = maxwidth  >> 3; /* multiple of 8 */
	bytewidth    = (width+7) >> 3;

	/* All full zero bytes... */
	for (i = bytewidth; i < bytemaxwidth; ++i)
	  ipnum[i] = 0;

	/* Now the remaining byte */
	i = 8 - (width & 7); /* Modulo 8 */

	bytewidth = width >> 3;
	if (i != 8) {
	  /* Not exactly multiple-of-byte-byte-width operand to be masked */
	  /* For 'width=31' we get now 'bytewidth=3', and 'i=1'           */
	  /* For 'width=25' we get now 'bytewidth=3', and 'i=7'           */
	  ipnum[bytewidth] &= (0xFF << i);
	}
}


static int
parse_ip_policy(pbuf,str,wstr)
struct policy *pbuf;
char *str, *wstr;
{
	char *s;
	struct policy_ipv4 *ip4 = (struct policy_ipv4 *) pbuf;
	struct policy_ipv6 *ip6 = (struct policy_ipv6 *) pbuf;

	if (*str == '[') {
	  ++str;
	  s = strchr(str,']');
	  if (!s) return -1;
	  *s = 0;
	  if (strncasecmp(str,"ipv6.",5)==0)
	    str += 5;
	  return parse_ip_policy(pbuf, str, wstr);
	}
	if (strchr(str,':') != NULL) { /* IPv6 */
#ifdef AF_INET6
	  if (inet_pton(AF_INET6, str, ip6->ipnum) <= 0)
	    return -2;
	  ip6->width = atoi(wstr);
	  if (ip6->width > 128 || ip6->width < 0)
	    return -3;
	  mask_ip_bits(ip6->ipnum, ip6->width, 128);
	  ip6->len  = 19;
	  ip6->type = P_K_IPv6;
	  return 0;
#else
	  return -1;
#endif
	}
	if (inet_pton(AF_INET, str, ip4->ipnum) <= 0)
	  return -4;
	ip4->width = atoi(wstr);
	if (ip4->width > 32 || ip4->width < 0)
	  return -5;
	mask_ip_bits(ip4->ipnum,ip4->width, 32);
	ip4->len = 7;
	ip4->type = P_K_IPv4;
	return 0;
}

static int
parse_gen_policy(pbuf,str,type)
struct policy *pbuf;
char *str;
int type;
{
	int len = strlen(str) +1; /* Terminating \000 included! */

	memcpy(pbuf->data, str, len);
	pbuf->len  = len + 2;
	pbuf->type = type;
}


/*
 * int parsepolicykey(struct policy *pbuf, char *inpstr)
 *
 * Return 0, when parse is successfull
 */
int
parsepolicykey(pbuf, str)
struct policy *pbuf;
char *str;
{
  char *s = strchr(str, '/');

  if (s != NULL) {
    *s++ = 0;
    return parse_ip_policy(pbuf, str, s);
  }
  if (*str == '_')
    return parse_gen_policy(pbuf, str, P_K_TAG);
  return   parse_gen_policy(pbuf, str, P_K_DOMAIN);
}

int
parseattributepair(abuf, str1, str2)
struct attribute *abuf;
char *str1, *str2;
{
	abuf->len = 2 + strlen(str2) + 1;
	strcpy(abuf->data,str2);

	if (strcmp(str1,"=")==0)
	  abuf->attrib = P_A_ALIAS;
	else if (strcmp(str1,"rejectnet")==0)
	  abuf->attrib = P_A_REJECTNET;
	else if (strcmp(str1,"rejectsource")==0)
	  abuf->attrib = P_A_REJECTSOURCE;
	else if (strcmp(str1,"relaycustomer")==0)
	  abuf->attrib = P_A_RELAYCUSTOMER;
	else if (strcmp(str1,"relaycustnet")==0)
	  abuf->attrib = P_A_RELAYCUSTNET;
	else if (strcmp(str1,"relaytarget")==0)
	  abuf->attrib = P_A_RELAYTARGET;
	else if (strcmp(str1,"freeze")==0)
	  abuf->attrib = P_A_ACCEPTbutFREEZE;
	else
	  return -1;
	return 0;
}
