/*
 * $Id: alist.c,v 1.3 1999/01/26 16:36:11 labovit Exp $
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netdb.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <array.h>
#include <New.h>
#include <hash.h>
#include <mrt.h>

static LINKED_LIST *access_list[MAX_ALIST];


int
add_access_list (int num, int permit, prefix_t *prefix, prefix_t *wildcard, 
		 int exact, int refine) {

   condition_t *condition;

   if (num < 0 || num >= MAX_ALIST)
	 return (-1);
   if (access_list[num] == NULL) {
      access_list[num] = LL_Create (0);
   }
   condition = New (condition_t);
   condition->permit = permit;
   /* expects Ref_Prefix can handle even a case of prefix == NULL */
   condition->prefix = Ref_Prefix (prefix);
   condition->wildcard = Ref_Prefix (wildcard);
   condition->exact = exact;
   condition->refine = refine;
   LL_Add (access_list[num], condition);
   return (1);
}


int 
remove_access_list (int num, int permit, prefix_t *prefix, prefix_t *wildcard,
		    int exact, int refine) {
  condition_t *condition;

  if (num < 0 || num >= MAX_ALIST)
    return (-1);
  if (access_list[num] == NULL) {
    return (-1);
  }

   LL_Iterate (access_list[num], condition) {
     if (permit != condition->permit)
	continue;
     if (prefix == NULL && condition->prefix == NULL) {
	/* OK, both are all */
     }
     else if (prefix && condition->prefix) {
	if (!prefix_compare (prefix, condition->prefix))
	    continue;
	if (wildcard == NULL && condition->wildcard == NULL) {
	    /* OK */
	}
	else if (wildcard && condition->wildcard) {
	    if (!prefix_compare (wildcard, condition->wildcard))
	        continue;
	}
	else {
	    continue;
	}
        if (exact != condition->exact)
	   continue;
        if (refine != condition->refine)
	   continue;
     }
     else {
	/* one is prefix, the other is all */
	continue;
     }

       Deref_Prefix (condition->prefix);
       Deref_Prefix (condition->wildcard);
       LL_Remove (access_list[num], condition);
       return (1);
   }

   return (-1);
}


int
del_access_list (int num) {

   if (num < 0 || num >= MAX_ALIST)
	 return (-1);
    if (access_list[num] == NULL) {
        return (-1);
    }
    Delete (access_list[num]);
    access_list[num] = NULL;
    return (1);
}


/*
 * return 1 if permit, 0 if denied, -1 if not matched
 */
int 
apply_condition (condition_t *condition, prefix_t *prefix)
{
      assert (condition);

      if (condition->prefix == NULL) /* all */
	  return (condition->permit);

	  /* family the same */
      if ((prefix->family == condition->prefix->family) &&
	  /* refine */
	  ((condition->refine && prefix->bitlen > condition->prefix->bitlen) ||
	  /* no refine or exact */
          ((!condition->refine || condition->exact) &&
              prefix->bitlen == condition->prefix->bitlen)) &&
	  /* prefix compare up to bitlen with wildcard */
          byte_compare (prefix_tochar (prefix), 
                        prefix_tochar (condition->prefix),
                        condition->prefix->bitlen, 
			(condition->wildcard)? 
			    prefix_tochar (condition->wildcard): NULL))
          return (condition->permit);
      return (-1);
}


/*
 * return 1 if permit, 0 otherwise
 */
int apply_access_list (int num, prefix_t *prefix) {

   condition_t *condition;

   if (num < 0 || num >= MAX_ALIST)
     return (0);
   if (num == 0)
      return (1); /* cisco feature */
   if (access_list[num] == NULL) {
      /* I'm not sure how cisco works for undefined access lists */
     return (0); /* assuming deny for now */
   }

   LL_Iterate (access_list[num], condition) {
      int value;

      if ((value = apply_condition (condition, prefix)) >= 0)
	  return (value);
   }
   /* deny */
   return (0);
}


char *
access_list_toa2 (int num) {
    condition_t *condition;
    char *cp, *tag;
    char *buff;
  
    THREAD_SPECIFIC_STRAGE (buff);
    cp = buff;

    if (access_list[num] == NULL)
        return (NULL);

    LL_Iterate (access_list[num], condition) {
        if (condition->permit) 
	    tag = "permit";
        else
	    tag = "deny";

        if (condition->prefix == NULL) {
            sprintf (cp, "access-list %d %s all\r\n", num, tag);
	    cp += strlen (cp);
	}
        else {
            sprintf (cp, "access-list %d %s %s", num, tag, 
		     prefix_toax (condition->prefix));
	    cp += strlen (cp);

	    if (condition->wildcard) {
		sprintf (cp, " %s", prefix_toa (condition->wildcard));
	        cp += strlen (cp);
	    }
            if (condition->exact) {
	        strcpy (cp, " exact");
	        cp += strlen (cp);
	    }
            if (condition->refine) {
	        strcpy (cp, " refine");
	        cp += strlen (cp);
	    }
	    strcpy (cp, "\r\n");
	    cp += strlen (cp);
	}
    }
    return (buff);
}


/* this will be obsolete */
char *access_list_toa () {
  char *cp, *tmp;
  int i;
  
  tmp = NewArray (char, 1024*4); /* XXX */
  cp = tmp;

  for (i=0; i < MAX_ALIST; i++) {
    if (access_list[i] == NULL) continue;
    sprintf (cp, "%s", access_list_toa2 (i));
    cp += strlen (cp);
  }
  return (tmp);
}
