/***************************************
  $Revision: 1.20 $

  Access control module (ac) - access control for the query part

  Status: NOT REVIEWED, TESTED
  
  Design and implementation by: Marek Bukowy
  
  ******************/ /******************
  Copyright (c) 1999                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/
#include <stdio.h>
#include <glib.h>

#define AC_OK RX_OK
#define AC_INVARG IP_INVARG

#define AC_IMPL
#include <rxroutines.h>
#include <erroutines.h>
#include <access_control.h>
#include "socket.h"
#include "mysql_driver.h"
#include <constants.h>
#include <server.h>

#define AC_DECAY_TIME 600

/* formats for printing the access control list entries */
#define ACL_FORMAT        "%10d %10d %10d %10d %10d"
#define ACL_HEADER  "%-20s %10s %10s %10s %10s %10s\n"

/* formats for printing the accounting entries */
#define ACC_FORMAT       "%4d    %4d    %4d    %4d    %6d    %6d    %6d"
#define ACC_HEADER "%-20s %4s    %4s    %4s    %4s    %6s    %6s    %6s\n"


/*++++++++++++++++++++++++++++++++++++++
  AC_to_string_header:

  produce a header for the access stats printout  

  returns an allocated string
  ++++++++++++++++++++++++++++++++++++++*/
char *AC_to_string_header(void) 
{
  char *result_buf;

  dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );
  
  sprintf(result_buf, ACC_HEADER, 
	  "ip", "conn", "pass", "deny", "qry", "pub", "priv", "bonus" );

  return result_buf;
}

/*++++++++++++++++++++++++++++++++++++++
  AC_to_string:

  Show an access structure  

  returns an allocated string
  ++++++++++++++++++++++++++++++++++++++*/
char *AC_to_string(GList *leafptr)
{
  char *result_buf;
  acc_st *a = leafptr->data;

  if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
    /* XXX generic malloc handler pending ...*/
    return NULL;
  }
  
  if( a == NULL ) {
    strcpy(result_buf, "DATA MISSING!");
  }
  else {
    sprintf(result_buf,  ACC_FORMAT,
            a->connections,
	    a->addrpasses,
            a->denials,
            a->queries,     
            a->public_objects,
            a->private_objects,
            a->private_bonus
            );
  }
  
  return result_buf;
} /* AC_to_string() */


/*++++++++++++++++++++++++++++++++++++++
  AC_credit_to_string:
 
 Show credit used (for logging of queries)
 
 acc_st *a     - the credit structure
 
 returns an allocated string
 ++++++++++++++++++++++++++++++++++++++*/
char *AC_credit_to_string(acc_st *a)
{
  char *result_buf;
  
  if( wr_malloc( (void **) &result_buf, 64) != UT_OK ) {
    /* XXX generic malloc handler pending ...*/
    return NULL;
  }
  
  dieif( a == NULL );
  
  sprintf(result_buf,"%d+%d%s",
	  a->private_objects,
	  a->public_objects,
	  a->denials ? " **DENIED**" : ""
	  );
  
  return result_buf;
} /* AC_credit_to_string */ 


/*+++++++++++++++++++++++++++++++++++++++
  AC_acl_to_string_header:

  produce a header for the acl printout

  returns an allocated string
  ++++++++++++++++++++++++++++++++++++++*/
char *
AC_acl_to_string_header(void)
{
  char *result_buf;
  dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );

  sprintf(result_buf, ACL_HEADER, "ip",
	  "maxbonus", "maxdenials", "maxpublic", "ban", "trustpass" );

  return result_buf;
}



/*++++++++++++++++++++++++++++++++++++++
  AC_acl_to_string:

  Show an access control list structure

  returns an allocated string
  ++++++++++++++++++++++++++++++++++++++*/
char *AC_acl_to_string(GList *leafptr)
{
  char *result_buf;
  acl_st *a = leafptr->data;

  if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
    /* XXX generic malloc handler pending ...*/
    return NULL;
  }
  
  if( a != NULL ) {
    sprintf(result_buf, ACL_FORMAT,
            a->maxbonus,
            a->maxdenials,
	    a->maxpublic,
            a->deny,     
            a->trustpass
            );
  }
  else {
    strcpy(result_buf, "DATA MISSING\n");
  }
  
  return result_buf;
} /* AC_acl_to_string() */


/*+++++++++++++++++++++++++++++++++++++++
  AC_findexless_acl_l:

  find the exact or less specific match for the given prefix in the acl tree.

  ip_prefix_t *prefix - prefix to look for
  acl_st *store_acl   - pointer to store the output

  returns error code from RX or OK

  MT-Note: assumes locked acl tree
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t
AC_findexless_acl_l(ip_prefix_t *prefix, acl_st *store_acl)
{
  GList       *datlist=NULL;
  er_ret_t    ret_err;
  rx_datref_t *datref;  

  if( (ret_err = RX_bin_search(RX_SRCH_EXLESS, 0, 0, act_acl, 
                               prefix, &datlist, RX_ANS_ALL)
       ) != RX_OK   ||  g_list_length(datlist) == 0 ) {
    /* acl tree is not configured at all ! There always must be a
       catch-all record with defaults */
    die;
  }

  datref = (rx_datref_t *)g_list_nth_data(datlist,0);

  *store_acl = * ((acl_st *)  datref->leafptr);

  wr_clear_list( &datlist );

  /* XXX dbg checking tree consistency */
  {
    rx_treecheck_t errorfound;
    er_ret_t err;
    if( (err=RX_treecheck(act_acl, 1, &errorfound)) != RX_OK ) {
      fprintf(stderr, "Nope! %d returned \n", err);
      die;
    }
  }  

  return ret_err;
}
/* AC_findexless_acl_l */


/*+++++++++++++++++++++++++++++++++++++++
  AC_findcreate_acl_l:
  
  find or create an entry for the given prefix in the acl tree.

  ip_prefix_t *prefix - prefix to look for 
  acl_st **store_acl  - pointer to store the ptr to the acl struct 
                        (initialised to the values of the parent entry 
			if just created)

  returns error code from RX or OK

  MT-Note: assumes locked acl tree
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t
AC_findcreate_acl_l(ip_prefix_t *prefix, acl_st **store_acl)
{
  GList       *datlist=NULL;
  er_ret_t    ret_err;
  acl_st      *newacl;
  acl_st acl_copy;    

  if( NOERR(ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_acl, 
				    prefix, &datlist, RX_ANS_ALL)
	    )) {
    
    switch( g_list_length(datlist)) {
    case 0:
      dieif( wr_calloc((void **)&newacl, 1, sizeof(acl_st)) != UT_OK );
      
      /* make the new one inherit all parameters after the old one */
      
      AC_findexless_acl_l(prefix, &acl_copy);

      *newacl = acl_copy;
      
      /* link in */
      rx_bin_node(RX_OPER_CRE, prefix, act_acl, (rx_dataleaf_t *)newacl);
      break;
    case 1:
      {
	/* Uh-oh, the guy is already known ! (or special, in any case) */ 
	rx_datref_t *datref = (rx_datref_t *)g_list_nth_data(datlist,0);
	newacl = (acl_st *) datref->leafptr;
      }
      break;
    default:
      die;
    }
  } 

  /* free search results */
  wr_clear_list( &datlist );
  
  /* store */
  *store_acl = newacl;
  return ret_err;
}
/* AC_findcreate_acl_l */


/*+++++++++++++++++++++++++++++++++++++++
  AC_findcreate_account_l:
  
  finds exact prefix in the accounting tree
  or creates area initialised to zeros + sets ptr to it.
  
  rx_tree_t *tree     - the tree
  ip_prefix_t *prefix - prefix to look for 
  acc_st **store_acl  - pointer to store the ptr to the account struct 

  returns error code from RX or OK

  MT-Note: assumes locked accounting tree 
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t 
AC_findcreate_account_l(rx_tree_t *tree, ip_prefix_t *prefix, 
			acc_st **acc_store)
{
  GList       *datlist=NULL;
  er_ret_t    ret_err;
  acc_st      *recacc;

  if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, tree, 
                               prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
    switch( g_list_length(datlist) ) {
    case 0:
      /* need to create a new accounting record */
      if( (ret_err = wr_malloc( (void **)& recacc, sizeof(acc_st))) == UT_OK ) {
        /*  counters = init to zeros */
        memset( recacc, 0, sizeof(acc_st));
        
        /* attach. The recacc is to be treated as a dataleaf
           (must use lower levels than RX_asc_*)
        */
        ret_err = rx_bin_node( RX_OPER_CRE, prefix, 
                               act_runtime, (rx_dataleaf_t *)recacc );
      }
      break;
    case 1:
      {
        rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
        
        /* OK, there is a record already */
        recacc = (acc_st *) datref->leafptr;
        
      }
      break;
    default: die; /* there shouldn't be more than 1 entry per IP */
    }
  }
    
  wr_clear_list( &datlist );
  
  *acc_store = recacc;
  
  return ret_err;
}


/*++++++++++++++++++++++++++++++++++++++
  AC_fetch_acc:

  Finds the runtime accounting record for this IP, 
  stores a copy of it in acc_store. 
  If not found, then it is created and initialised to zeros in findcreate()

  ip_addr_t *addr  - address
  acc_st *acc_store - pointer to store the account struct 

  MT-Note: locks/unlocks the accounting tree
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store)
{
  er_ret_t ret_err;
  ip_prefix_t prefix;
  acc_st *ac_ptr;

  prefix.ip = *addr;
  prefix.bits = IP_sizebits(addr->space);

  TH_acquire_read_lock( &(act_runtime->rwlock) );
  
  ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
  *acc_store = *ac_ptr;

  TH_release_read_lock( &(act_runtime->rwlock) );

  return ret_err;
}/* AC_fetch_acc() */


/*++++++++++++++++++++++++++++++++++++++  
  AC_check_acl:
  
  search for this ip or less specific record in the access control tree
  
  if( bonus in combined runtime+connection accountings > max_bonus in acl)
            set denial in the acl for this ip (create if needed)
  if( combined denialcounter > max_denials in acl)
            set the permanent ban in acl; save in SQL too
  calculate credit if pointer provided
  save the access record (ip if created or found/prefix otherwise) 
            at *acl_store if provided

  ip_addr_t *addr  - address
  acc_st *acc_store - pointer to store the *credit* account struct 
  acl_st *acl_store - pointer to store the acl struct 
  
  any of the args except address can be NULL

  returns error code from RX or OK

  MT-Note: locks/unlocks the accounting tree
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_check_acl( ip_addr_t *addr, 
                       acc_st *credit_acc,
                       acl_st *acl_store
                       )
{
  ip_prefix_t prefix;
  er_ret_t    ret_err;
  acl_st      acl_record;
  acc_st      run_acc;

  AC_fetch_acc( addr, &run_acc );
  
  prefix.ip = *addr;
  prefix.bits = IP_sizebits(addr->space);
  
  /* lock the tree accordingly */
  TH_acquire_read_lock( &(act_acl->rwlock) );  
  
  /* find an applicable record */
  AC_findexless_acl_l(&prefix, &acl_record);
  
  /* calculate the credit if pointer given */
  if( credit_acc ) {
    memset( credit_acc, 0, sizeof(acc_st));
    credit_acc->public_objects =                       /* -1 == unlimited */
      acl_record.maxpublic - run_acc.public_objects;
    credit_acc->private_objects =
      acl_record.maxbonus - run_acc.private_bonus;
  }

  /* copy the acl record if asked for it*/
  if( acl_store ) {
    *acl_store =  acl_record;
  }

  /* release lock */
  TH_release_read_lock( &(act_acl->rwlock) );
  
 
  return ret_err;
}



/*++++++++++++++++++++++++++++++++++++++  
  AC_acc_addup:

  Add/subtract the values from one accounting structure to another

  acc_st *a  - this one gets changed
  acc_st *b  - this one provides the values to change a
  int minus  - triggers subtraction if non-zero

+++++++++++++++++++++++++++++++++++++++*/
void AC_acc_addup(acc_st *a, acc_st *b, int minus)
{
  int mul = minus ? -1 : 1;
  
  /* add all counters from b to those in a */
  a->connections     +=  mul * b->connections;   
  a->addrpasses      +=  mul * b->addrpasses;  
 
  a->denials         +=  mul * b->denials;      
  a->queries         +=  mul * b->queries;       
  a->public_objects  +=  mul * b->public_objects;
  a->private_objects +=  mul * b->private_objects;
  a->private_bonus   +=  mul * b->private_bonus;
}/* AC_acc_addup */

/*++++++++++++++++++++++++++++++++++++++ 
  AC_commit_credit:

  performs the commit on an accounting tree (locks them first)
  stores a copy of the accounting record at rec_store

  rx_tree_t *tree      - the tree
  ip_prefix_t *prefix  - prefix (usually a /32)
  acc_st *acc_conn     - credit used
  acc_st *rec_store    - pointer to store the account struct 

  returns error code from AC_findcreate_account_l or OK

  MT-Note: locks/unlocks the accounting tree
+++++++++++++++++++++++++++++++++++++++*/
er_ret_t 
AC_commit_credit(rx_tree_t *tree, ip_prefix_t *prefix, 
		 acc_st *acc_conn, acc_st *rec_store )
{
  acc_st      *accountrec;
  er_ret_t    ret_err;


  acc_conn->private_bonus = acc_conn->private_objects;

  TH_acquire_write_lock( &(tree->rwlock) );

  ret_err = AC_findcreate_account_l(act_runtime, prefix, &accountrec);
  
  if( NOERR(ret_err)) {
    AC_acc_addup(accountrec, acc_conn, ACC_PLUS);
  }

  TH_release_write_lock( &(tree->rwlock) );
 
  *rec_store = *accountrec;
  
  return ret_err;
}/* AC_commit_credit */



/*++++++++++++++++++++++++++++++++++++++  
  AC_acl_sql:

  updates/creates a record for the given prefix in the acl table of 
  the RIPADMIN database. Adds a comment.

  ip_prefix_t *prefix  - prefix
  acl_st *newacl       - new values to store in the database
  char *newcomment     - comment to be added (must not be NULL)
  
  placeholder: it may return an error code from SQ - as soon as sq 
  implements common error scheme

 ++++++++++++++++++++++++++++++++++++++*/
er_ret_t 
AC_acl_sql(ip_prefix_t *prefix, acl_st *newacl, char *newcomment )
{  
  SQ_connection_t *sql_connection = NULL;
  SQ_result_set_t *result;
  SQ_row_t *row;
  char *oldcomment;
  char *query;
  char querybuf[256];
  
  sql_connection = SQ_get_connection(CO_get_host(),
				     CO_get_database_port(),
				     "RIPADMIN",
				     CO_get_user(), 
				     CO_get_password() );
  
  /* get the old entry, extend it */
  sprintf(querybuf, "SELECT comment FROM acl WHERE "
	  "prefix = %u AND prefix_length = %d", 
	  prefix->ip.words[0],
	  prefix->bits);
  dieif( SQ_execute_query(sql_connection, querybuf, &result) == -1 );
  
  if( SQ_num_rows(result) == 1 ) {
    dieif( (row = SQ_row_next(result)) == NULL);
    oldcomment = SQ_get_column_string(result, row, 0);
  }
  else {
    oldcomment = "";
  }

  SQ_free_result(result);
  
  /* must hold the thing below (REPLACE..blah blah blah) + text */
  dieif( wr_malloc((void **)&query, 
		   strlen(oldcomment) + strlen(newcomment) + 256) != UT_OK );
  
  /* compose new entry and insert it */
  sprintf(query, "REPLACE INTO acl VALUES(%u, %d, %d, %d, %d, %d, %d,"
	  "\"%s%s%s\")",
	  prefix->ip.words[0],
	  prefix->bits,
	  newacl->maxbonus,
	  newacl->maxpublic,
	  newacl->maxdenials,
	  newacl->deny,
	  newacl->trustpass,
	  oldcomment, 
	  strlen(oldcomment) > 0 ? "\n" : "",
	  newcomment
	  );
  
  SQ_execute_query(sql_connection, query, NULL);
  SQ_close_connection(sql_connection);
  
  wr_free(query);
  
  return AC_OK;

}/* AC_acl_sql */

/*++++++++++++++++++++++++++++++++++++++ 
  AC_ban_set:
  
  re/sets the permanent ban flag both in the acl tree in memory
  and the sql table. The "text" is appended to the comment 
  in the sql record (the expected cases are
  - "automatic" in case the limit is exceeded and ban is set by s/w
  - "manual"    in case it is (un)set from the config iface

  ip_prefix_t *prefix   - prefix 
  char *text            - usually "automatic" or "manual"  
  int denyflag          - new value of the denyflag (ban)
  
  returns error code from AC_acl_sql or OK
  +++++++++++++++++++++++++++++++++++++++*/
er_ret_t
AC_ban_set(ip_prefix_t *prefix, char *text, int denyflag)
{
  acl_st *treeacl;
  char newcomment[256];
  er_ret_t ret_err;
  time_t  clock;
  char timebuf[26];
  
  time(&clock);
  ctime_r(&clock, timebuf);

  sprintf(newcomment,"%s permanent ban set to %d at %s", text, 
	  denyflag, timebuf);
    
  TH_acquire_write_lock( &(act_acl->rwlock) );  

  /* find a record in the tree */  
  if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
    treeacl->deny = denyflag;
    ret_err = AC_acl_sql( prefix, treeacl, newcomment );
  }
  TH_release_write_lock( &(act_acl->rwlock) );

  return ret_err;
}/* AC_ban_set */


/*++++++++++++++++++++++++++++++++++++++ 
  AC_asc_ban_set:
  
  sets ban on text address/range. Parses the text address/range/prefix 
  and then calls AC_ban_set on that prefix. 
  
  Precondition: if the key is a range, it must decompose into one prefix 
  
  returns error code from IP_smart_conv, AC_ban_set or 
  AC_INVARG if range composed
  +++++++++++++++++++++++++++++++++++++++*/
er_ret_t
AC_asc_ban_set(char *addrstr, char *text, int denyflag)
{
  er_ret_t ret_err;
  GList *preflist = NULL;
  ip_keytype_t key_type;

  if( (ret_err = IP_smart_conv(addrstr, 0, 0,
			       &preflist, IP_PLAIN, &key_type)) != IP_OK ) {
    return ret_err;
  }
  
  /* allow only one prefix */
  /* The argument can be even a range, but must decompose into one prefix */
  if(  NOERR(ret_err) && g_list_length( preflist ) != 1 ) {
    ret_err = AC_INVARG;
  }
  
  if( NOERR(ret_err) ) {
    ret_err = AC_ban_set( (g_list_first(preflist)->data), text, denyflag);
  }

  wr_clear_list( &preflist );
  
  return ret_err;
}/* AC_asc_ban_set */


/*++++++++++++++++++++++++++++++++++++++ 
  AC_commit:

  commits the credit into all accounting trees, (XXX: only one at the moment)
  checks the limits and sets automatic ban if limit exceeded.

  ip_addr_t *addr  - user's address
  acc_st *acc_conn - credit used
  acl_st *acl_copy - pointer to store a copy of the acl

  returns error code from AC_commit_credit or AC_ban_set or OK.

  outline:
        lock runtime + minute accounting trees 
	-----------------------  XXX runtime only for the moment
           find or create entries, 
           increase accounting values by the values from passed acc
           check values against acl, see if permanent ban applies

           reset the connection acc
        unlock accounting trees

        if permanent ban - set it! :
            lock acl
            find/create IP in memory
            set ban
            find/create IP in SQL
            copy old values (if any), set ban, append comment
            unlock acl

 +++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn, acl_st *acl_copy) { 
  acc_st   account;
  er_ret_t ret_err;
  ip_prefix_t prefix;

  prefix.ip = *addr;
  prefix.bits = IP_sizebits(addr->space);
  
  ret_err = AC_commit_credit(act_runtime, &prefix, acc_conn, &account);
  /* XXX add more trees here */
  
  memset(acc_conn,0, sizeof(acc_st));

  /* set permanent ban if deserved  and if not set yet */
  if( account.denials > acl_copy->maxdenials 
      && acl_copy->deny == 0 
      && NOERR(ret_err) ) {
    
    ret_err = AC_ban_set(&prefix, "Automatic", 1);
  }

  return ret_err;
} /* AC_commit */


/*++++++++++++++++++++++++++++++++++++++ 
  AC_decay_hook:

  action performed on a single account node during decay (diminishing the
  bonus). Conforms to rx_walk_tree interface, therefore some of the 
  arguments do not apply and are not used.

  rx_node_t *node  - pointer to the node of the radix tree
  int level        - n/a
  int nodecounter  - n/a
  void *con        - n/a

  returns always OK
+++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_decay_hook(rx_node_t *node, int level, int nodecounter, void *con)
{
  acc_st *a = node->leaves_ptr->data;
  
  a->private_bonus *= 0.95;

  return RX_OK;
} /* AC_decay_hook() */



/*++++++++++++++++++++++++++++++++++++++
  AC_decay:
  
  Every AC_DECAY_TIME goes through the accounting tree(s) and decays the 
  bonus values.
  
  returns always OK

  MT-Note  This should be run as a detached thread.
  +++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_decay(void) {
  er_ret_t ret_err;

  
  while(CO_get_do_server()) {

    TH_acquire_write_lock( &(act_runtime->rwlock) );

    if( act_runtime->top_ptr != NULL ) {
       rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
                         RX_WALK_SKPGLU,  /* skip glue nodes */
                         255, 0, 0, NULL, &ret_err);
    }

    /* it should also be as smart as to delete nodes that have reached 
       zero, otherwise the whole of memory will be filled.
       Next release :-)
    */

    TH_release_write_lock( &(act_runtime->rwlock) );

    printf("AC: decaying access tree. (Every %d seconds)\n", AC_DECAY_TIME);

    SV_sleep(LOCK_SHTDOWN, AC_DECAY_TIME);
  }

  return ret_err;
} /* AC_decay() */


/*++++++++++++++++++++++++++++++++++++++ 
  AC_acc_load:

  loads the acl access tree from the acl table of the RIPADMIN database.
  (takes port/host/user/password from the config module).
  
  bails out if encounters problems with the database (logs to stderr).

  returns error code from RX_bin_node or wr_malloc.
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_acc_load(void)
{
  SQ_connection_t *con=NULL;
  SQ_result_set_t *result;
  SQ_row_t *row;
  er_ret_t ret_err = RX_OK;

  if( (con = SQ_get_connection(CO_get_host(), CO_get_database_port(), 
                        "RIPADMIN", CO_get_user(), CO_get_password() )
       ) == NULL ) {
    fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
    die;
  }
  
  if( SQ_execute_query(con, "SELECT * FROM acl", &result) == -1 ) {
      fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
      die;
  }
  
  TH_acquire_write_lock( &(act_acl->rwlock) );

  while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
    ip_prefix_t mypref;
    acl_st *newacl;
    char *col[7];
    unsigned myint;
    int i;

    memset(&mypref, 0, sizeof(ip_prefix_t));
    mypref.ip.space = IP_V4;
    
    if( (ret_err = wr_malloc( (void **)& newacl, sizeof(acl_st))
         ) == UT_OK ) {

      for(i=0; i<7; i++) {
        if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
          die;
        }
      }
      
      /* prefix ip */
      if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
      
      /* prefix length */
      if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
      
      /* acl contents */
      if( sscanf(col[2], "%u",  & (newacl->maxbonus)   ) < 1 ) { die; }
      if( sscanf(col[3], "%u",  & (newacl->maxpublic)   ) < 1 ) { die; }
      if( sscanf(col[4], "%hd", & (newacl->maxdenials) ) < 1 ) { die; }
      
      /* these are chars therefore cannot read directly */
      if( sscanf(col[5], "%u", &myint              ) < 1 ) { die; }
      else {
        newacl->deny = myint;
      }
      if( sscanf(col[6], "%u", &myint  ) < 1 ) { die; }
      else {
        newacl->trustpass = myint;
      }
      
      /* free space */
      for(i=0; i<6; i++) {
	  wr_free(col[i]);
      }
      
      /* now add to the tree */      
      ret_err = rx_bin_node( RX_OPER_CRE, &mypref, 
                             act_acl, (rx_dataleaf_t *) newacl );
    }
  } /* while row */

  TH_release_write_lock( &(act_acl->rwlock) );

  SQ_free_result(result);
  /* Close connection */
  SQ_close_connection(con);

  return ret_err;
} /* AC_acc_load */



/*++++++++++++++++++++++++++++++++++++++ 
  AC_build:

  creates empty trees for accounting/acl.
  
  returns error code from RX_tree_cre or OK.
  (XXX): just now only bails out when encounters problems.
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_build(void) 
{
  /* create trees */
  if (      RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
			RX_SUB_NONE, &act_runtime) != RX_OK
	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
			RX_SUB_NONE, &act_hour) != RX_OK
	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
			RX_SUB_NONE, &act_minute) != RX_OK
	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
			RX_SUB_NONE, &act_acl) != RX_OK
         )
    die; /*can be changed to an error and handled ... some day */

  return RX_OK;
}

/*++++++++++++++++++++++++++++++++++++++ 
  AC_rxwalkhook_print:

  action performed on a single account node 
  when listing the contents of the access tree: format and print the
  data from this node.

  Conforms to rx_walk_tree interface, therefore some of the 
  arguments do not apply and are not used.
  
  rx_node_t *node  - pointer to the node of the radix tree
  int level        - n/a
  int nodecounter  - n/a
  void *con        - pointer to the connection structure (prints to it)
  
  returns always OK 
+++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_rxwalkhook_print(rx_node_t *node, 
                             int level, int nodecounter, 
                             void *con)
{
  char adstr[IP_ADDRSTR_MAX];
  char line[1024];
  char *dat;
  
  
    if( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK ) {
      die; /* program error. */
    }
    
    sprintf(line, "%-20s %s\n", adstr, 
            dat=AC_to_string( node->leaves_ptr ));
    wr_free(dat);
    
    SK_cd_puts((sk_conn_st *)con, line);
    return RX_OK;
} /* AC_rxwalkhook_print */


/*++++++++++++++++++++++++++++++++++++++
  AC_rxwalkhook_print_acl:
  
  action performed on a single account node 
  when listing the contents of the acl tree: format and print the
  data from this node.

  Conforms to rx_walk_tree interface, therefore some of the 
  arguments do not apply and are not used.
  
  rx_node_t *node  - pointer to the node of the radix tree
  int level        - n/a
  int nodecounter  - n/a
  void *con        - pointer to the connection structure (prints to it)

  returns always OK 
  +++++++++++++++++++++++++++++++++++++++*/
er_ret_t AC_rxwalkhook_print_acl(rx_node_t *node, 
                             int level, int nodecounter, 
                             void *con)
{
  char prefstr[IP_PREFSTR_MAX];
  char line[1024];
  char *dat;
  
  
    if( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK ) {
      die; /* program error. */
    }
    
    sprintf(line, "%-20s %s\n", prefstr, 
            dat=AC_acl_to_string( node->leaves_ptr ));
    wr_free(dat);
    
    SK_cd_puts((sk_conn_st *)con, line);
    return RX_OK;
}/* AC_rxwalkhook_print_acl */

