1    | /***************************************
2    |   $Revision: 1.40 $
3    | 
4    |   Access control module (ac) - access control for the query part
5    | 
6    |   Status: NOT REVIEWED, TESTED
7    |   
8    |   Design and implementation by: Marek Bukowy
9    |   
10   |   ******************/ /******************
11   |   Copyright (c) 1999                              RIPE NCC
12   |  
13   |   All Rights Reserved
14   |   
15   |   Permission to use, copy, modify, and distribute this software and its
16   |   documentation for any purpose and without fee is hereby granted,
17   |   provided that the above copyright notice appear in all copies and that
18   |   both that copyright notice and this permission notice appear in
19   |   supporting documentation, and that the name of the author not be
20   |   used in advertising or publicity pertaining to distribution of the
21   |   software without specific, written prior permission.
22   |   
23   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29   |   ***************************************/
30   | 
31   | /* 
32   |    test excercises:
33   | 
34   |    1. add a function to delete an entry from the acl table,
35   |       it should be called from the pc module.
36   |       
37   | */
38   | 
39   | #include <stdio.h>
40   | #include <glib.h>
41   | #include <string.h>
42   | 
43   | #define AC_IMPL
44   | #include "rxroutines.h"
45   | #include "erroutines.h"
46   | #include "access_control.h"
47   | #include "sk.h"
48   | #include "ta.h"
49   | #include "mysql_driver.h"
50   | #include "constants.h"
51   | #include "server.h"
52   | 
53   | #include "ca_configFns.h"
54   | #include "ca_dictionary.h"
55   | #include "ca_macros.h"
56   | #include "ca_srcAttribs.h"
57   | #include "timediff.h"
58   | 
59   | #include "math.h" /* exp() */
60   | 
61   | /* formats for printing the access control list entries */
62   | #define ACL_FORMAT        "%10d %10d %10d %10d %10d"
63   | #define ACL_HEADER  "%-20s %10s %10s %10s %10s %10s\n"
64   | 
65   | /* formats for printing the accounting entries */
66   | #define ACC_FORMAT       "%4d %4d %4d %4d %7d %7d %7d %7.1f %7.1f"
67   | #define ACC_HEADER "%-20s %4s %4s %4s %4s %7s %7s %7s %7s %7s\n"
68   | 
69   | 
70   | typedef struct {
71   |   double decay_factor;
72   |   unsigned newtotal;
73   |   GList *prunelist;
74   | } ac_decay_data_t;
75   | 
76   | /*++++++++++++++++++++++++++++++++++++++
77   |   ac_to_string_header:
78   | 
79   |   produce a header for the access stats printout  
80   | 
81   |   returns an allocated string
82   |   ++++++++++++++++++++++++++++++++++++++*/
83   | static
84   | char *ac_to_string_header(void) 
85   | {
86   |   char *result_buf;
87   | 
88   |   dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );
89   |   
90   |   sprintf(result_buf, ACC_HEADER, 
91   | 	  "ip", "conn", "pass", "deny", "qry", "refs", "priv_o", "pub_o", "priv_b","pub_b");
92   | 
93   |   return result_buf;
94   | }
95   | 
96   | /*++++++++++++++++++++++++++++++++++++++
97   |   ac_to_string:
98   | 
99   |   Show an access structure  
100  | 
101  |   returns an allocated string
102  |   ++++++++++++++++++++++++++++++++++++++*/
103  | static
104  | char *ac_to_string(GList *leafptr)
105  | {
106  |   char *result_buf;
107  |   acc_st *a = leafptr->data;
108  | 
109  |   if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
110  |     /* XXX generic malloc handler pending ...*/
111  |     return NULL;
112  |   }
113  |   
114  |   if( a == NULL ) {
115  |     strcpy(result_buf, "DATA MISSING!");
116  |   }
117  |   else {
118  |     sprintf(result_buf,  ACC_FORMAT,
119  |             a->connections,
120  | 	    a->addrpasses,
121  |             a->denials,
122  |             a->queries,     
123  | 	    a->referrals,
124  |             a->private_objects,
125  |             a->public_objects,
126  |             a->private_bonus,
127  | 	    a->public_bonus
128  |             );
129  |   }
130  |   
131  |   return result_buf;
132  | } /* ac_to_string() */
133  | 
134  | 
135  | /*++++++++++++++++++++++++++++++++++++++
136  |   AC_credit_to_string:
137  |  
138  |  Show credit used (for logging of queries)
139  |  
140  |  acc_st *a     - the credit structure
141  |  
142  |  returns an allocated string
143  |  ++++++++++++++++++++++++++++++++++++++*/
144  | char *AC_credit_to_string(acc_st *a)
145  | {
146  |   char *result_buf;
147  |   
148  |   if( wr_malloc( (void **) &result_buf, 64) != UT_OK ) {
149  |     /* XXX generic malloc handler pending ...*/
150  |     return NULL;
151  |   }
152  |   
153  |   dieif( a == NULL );
154  |   
155  |   sprintf(result_buf,"%d+%d+%d%s",
156  | 	  a->private_objects,
157  | 	  a->public_objects,
158  | 	  a->referrals,
159  | 	  a->denials ? " **DENIED**" : ""
160  | 	  );
161  |   
162  |   return result_buf;
163  | } /* AC_credit_to_string */ 
164  | 
165  | 
166  | /*+++++++++++++++++++++++++++++++++++++++
167  |   ac_acl_to_string_header:
168  | 
169  |   produce a header for the acl printout
170  | 
171  |   returns an allocated string
172  |   ++++++++++++++++++++++++++++++++++++++*/
173  | static char *
174  | ac_acl_to_string_header(void)
175  | {
176  |   char *result_buf;
177  |   dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );
178  | 
179  |   sprintf(result_buf, ACL_HEADER, "ip",
180  | 	  /* the names must match those in AC_ar_acl, so just take
181  | 	   them from there */
182  | 	  AC_ar_acl[AC_AR_MAXPRIVATE],
183  | 	  AC_ar_acl[AC_AR_MAXPUBLIC],
184  | 	  AC_ar_acl[AC_AR_MAXDENIALS],
185  | 	  AC_ar_acl[AC_AR_DENY],
186  | 	  AC_ar_acl[AC_AR_TRUSTPASS]
187  | 	  );
188  | 
189  | 
190  |   return result_buf;
191  | }
192  | 
193  | 
194  | 
195  | /*++++++++++++++++++++++++++++++++++++++
196  |   ac_acl_to_string:
197  | 
198  |   Show an access control list structure
199  | 
200  |   returns an allocated string
201  |   ++++++++++++++++++++++++++++++++++++++*/
202  | static
203  | char *ac_acl_to_string(GList *leafptr)
204  | {
205  |   char *result_buf;
206  |   acl_st *a = leafptr->data;
207  | 
208  |   if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
209  |     /* XXX generic malloc handler pending ...*/
210  |     return NULL;
211  |   }
212  |   
213  |   if( a != NULL ) {
214  |     sprintf(result_buf, ACL_FORMAT,
215  |             a->maxprivate,
216  | 	    a->maxpublic,  
217  | 	    a->maxdenials,
218  |             a->deny,     
219  |             a->trustpass
220  |             );
221  |   }
222  |   else {
223  |     strcpy(result_buf, "DATA MISSING\n");
224  |   }
225  |   
226  |   return result_buf;
227  | } /* ac_acl_to_string() */
228  | 
229  | 
230  | /*+++++++++++++++++++++++++++++++++++++++
231  |   ac_find_acl_l:
232  | 
233  |   find the exact or exact/less specific match for the given prefix in the acl tree.
234  | 
235  |   ip_prefix_t *prefix - prefix to look for
236  | 
237  |   acl_st *store_acl   - pointer to store the output
238  | 
239  |   returns error code from RX or OK
240  | 
241  |   MT-Note: assumes locked acl tree
242  |   ++++++++++++++++++++++++++++++++++++++*/
243  | static er_ret_t
244  | ac_find_acl_l(rx_srch_mt searchmode, ip_prefix_t *prefix, acl_st *store_acl)
245  | {
246  |   GList       *datlist=NULL;
247  |   er_ret_t    ret_err;
248  |   rx_datref_t *datref;  
249  | 
250  |   /* accept only RX_SRCH_EXLESS | RX_SRCH_EXACT modes */
251  |   dieif( searchmode != RX_SRCH_EXLESS && searchmode != RX_SRCH_EXACT);
252  | 
253  |   /* it must work */
254  |   dieif( (ret_err = RX_bin_search(searchmode, 0, 0, act_acl, 
255  |                                prefix, &datlist, RX_ANS_ALL)
256  |        ) != RX_OK );
257  |   /* In exless mode, something must be found or the acl tree is not 
258  |      configured at all ! 
259  |      There always must be a catch-all record with defaults */
260  |   dieif( searchmode == RX_SRCH_EXLESS && g_list_length(datlist) == 0 );
261  | 
262  | 
263  |   datref = (rx_datref_t *)g_list_nth_data(datlist,0);
264  | 
265  |   *store_acl = * ((acl_st *)  datref->leafptr);
266  | 
267  |   wr_clear_list( &datlist );
268  | 
269  | #if 0
270  |   /* XXX dbg checking tree consistency */
271  |   {
272  |     rx_treecheck_t errorfound;
273  |     er_ret_t err;
274  |     if( (err=RX_treecheck(act_acl, 1, &errorfound)) != RX_OK ) {
275  |       fprintf(stderr, "Nope! %d returned \n", err);
276  |       die;
277  |     }
278  |   }  
279  | #endif
280  | 
281  |   return ret_err;
282  | }
283  | /* ac_find_acl_l */
284  | 
285  | 
286  | /*+++++++++++++++++++++++++++++++++++++++
287  |   AC_findcreate_acl_l:
288  |   
289  |   find or create an entry for the given prefix in the acl tree.
290  | 
291  |   ip_prefix_t *prefix - prefix to look for 
292  | 
293  |   acl_st **store_acl  - pointer to store the ptr to the acl struct 
294  |                         (initialised to the values of the parent entry 
295  | 			if just created)
296  | 
297  |   returns error code from RX or OK
298  | 
299  |   MT-Note: assumes locked acl tree
300  |   ++++++++++++++++++++++++++++++++++++++*/
301  | er_ret_t
302  | AC_findcreate_acl_l(ip_prefix_t *prefix, acl_st **store_acl)
303  | {
304  |   GList       *datlist=NULL;
305  |   er_ret_t    ret_err;
306  |   acl_st      *newacl;
307  |   acl_st acl_copy;    
308  | 
309  |   if( NOERR(ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_acl, 
310  | 				    prefix, &datlist, RX_ANS_ALL)
311  | 	    )) {
312  |     
313  |     switch( g_list_length(datlist)) {
314  |     case 0:
315  |       dieif( wr_calloc((void **)&newacl, 1, sizeof(acl_st)) != UT_OK );
316  |       
317  |       /* make the new one inherit all parameters after the old one */
318  |       
319  |       ac_find_acl_l(RX_SRCH_EXLESS, prefix, &acl_copy);
320  | 
321  |       *newacl = acl_copy;
322  |       
323  |       /* link in */
324  |       rx_bin_node(RX_OPER_CRE, prefix, act_acl, (rx_dataleaf_t *)newacl);
325  |       break;
326  |     case 1:
327  |       {
328  | 	/* Uh-oh, the guy is already known ! (or special, in any case) */ 
329  | 	rx_datref_t *datref = (rx_datref_t *)g_list_nth_data(datlist,0);
330  | 	newacl = (acl_st *) datref->leafptr;
331  |       }
332  |       break;
333  |     default:
334  |       die;
335  |     }
336  |   } 
337  | 
338  |   /* free search results */
339  |   wr_clear_list( &datlist );
340  |   
341  |   /* store */
342  |   *store_acl = newacl;
343  |   return ret_err;
344  | }
345  | /* AC_findcreate_acl_l */
346  | 
347  | 
348  | /*+++++++++++++++++++++++++++++++++++++++
349  |   AC_findcreate_account_l:
350  |   
351  |   finds exact prefix in the accounting tree
352  |   or creates area initialised to zeros + sets ptr to it.
353  |   
354  |   rx_tree_t *tree     - the tree
355  | 
356  |   ip_prefix_t *prefix - prefix to look for 
357  | 
358  |   acc_st **store_acl  - pointer to store the ptr to the account struct 
359  | 
360  |   returns error code from RX or OK
361  | 
362  |   MT-Note: assumes locked accounting tree 
363  |   ++++++++++++++++++++++++++++++++++++++*/
364  | er_ret_t 
365  | AC_findcreate_account_l(rx_tree_t *tree, ip_prefix_t *prefix, 
366  | 			acc_st **acc_store)
367  | {
368  |   GList       *datlist=NULL;
369  |   er_ret_t    ret_err;
370  |   acc_st      *recacc;
371  | 
372  |   if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, tree, 
373  |                                prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
374  |     switch( g_list_length(datlist) ) {
375  |     case 0:
376  |       /* need to create a new accounting record */
377  |       if( (ret_err = wr_malloc( (void **)& recacc, sizeof(acc_st))) == UT_OK ) {
378  |         /*  counters = init to zeros */
379  |         memset( recacc, 0, sizeof(acc_st));
380  |         
381  |         /* attach. The recacc is to be treated as a dataleaf
382  |            (must use lower levels than RX_asc_*)
383  |         */
384  |         ret_err = rx_bin_node( RX_OPER_CRE, prefix, 
385  |                                act_runtime, (rx_dataleaf_t *)recacc );
386  |       }
387  |       break;
388  |     case 1:
389  |       {
390  |         rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
391  |         
392  |         /* OK, there is a record already */
393  |         recacc = (acc_st *) datref->leafptr;
394  |         
395  |       }
396  |       break;
397  |     default: die; /* there shouldn't be more than 1 entry per IP */
398  |     }
399  |   }
400  |     
401  |   wr_clear_list( &datlist );
402  |   
403  |   *acc_store = recacc;
404  | 
405  | #if 0
406  |   /* XXX dbg checking tree consistency */
407  |     if( act_runtime->top_ptr != NULL ) {
408  |       rx_treecheck_t errorfound;
409  |       er_ret_t err;
410  |       if( (err=RX_treecheck(act_runtime, 1, &errorfound)) != RX_OK ) {
411  | 	fprintf(stderr, "Nope! %d returned \n", errorfound);
412  | 	ER_dbg_va( FAC_AC, ASP_AC_DECAY,
413  | 		   "AC: checking access tree consistency: error %d", 
414  | 		   errorfound);
415  | 	die; /* access tree not consistent */
416  |       }
417  |     }
418  | #endif  
419  | 
420  |   return ret_err;
421  | }
422  | 
423  | 
424  | /*++++++++++++++++++++++++++++++++++++++
425  |   AC_fetch_acc:
426  | 
427  |   Finds the runtime accounting record for this IP, 
428  |   stores a copy of it in acc_store. 
429  |   If not found, then it is created and initialised to zeros in findcreate()
430  | 
431  |   ip_addr_t *addr  - address
432  | 
433  |   acc_st *acc_store - pointer to store the account struct 
434  | 
435  |   MT-Note: locks/unlocks the accounting tree
436  |   ++++++++++++++++++++++++++++++++++++++*/
437  | er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store)
438  | {
439  |   er_ret_t ret_err;
440  |   ip_prefix_t prefix;
441  |   acc_st *ac_ptr;
442  | 
443  |   prefix.ip = *addr;
444  |   prefix.bits = IP_sizebits(addr->space);
445  | 
446  |   TH_acquire_write_lock( &(act_runtime->rwlock) );
447  |   
448  |   ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
449  |   *acc_store = *ac_ptr;
450  | 
451  |   TH_release_write_lock( &(act_runtime->rwlock) );
452  | 
453  |   return ret_err;
454  | }/* AC_fetch_acc() */
455  | 
456  | 
457  | /*++++++++++++++++++++++++++++++++++++++  
458  |   AC_check_acl:
459  |   
460  |   search for this ip or less specific record in the access control tree
461  |   
462  |   if( bonus in combined runtime+connection accountings > max_bonus in acl)
463  |             set denial in the acl for this ip (create if needed)
464  |   if( combined denialcounter > max_denials in acl)
465  |             set the permanent ban in acl; save in SQL too
466  |   calculate credit if pointer provided
467  |   save the access record (ip if created or found/prefix otherwise) 
468  |             at *acl_store if provided
469  | 
470  |   ip_addr_t *addr  - address
471  | 
472  |   acc_st *acc_store - pointer to store the *credit* account struct 
473  | 
474  |   acl_st *acl_store - pointer to store the acl struct 
475  |   
476  |   any of the args except address can be NULL
477  | 
478  |   returns error code from RX or OK
479  | 
480  |   MT-Note: locks/unlocks the accounting tree
481  |   ++++++++++++++++++++++++++++++++++++++*/
482  | er_ret_t AC_check_acl( ip_addr_t *addr, 
483  |                        acc_st *credit_acc,
484  |                        acl_st *acl_store
485  |                        )
486  | {
487  |   ip_prefix_t prefix;
488  |   er_ret_t    ret_err = AC_OK;
489  |   acl_st      acl_record;
490  |   acc_st      run_acc;
491  | 
492  |   AC_fetch_acc( addr, &run_acc );
493  |   
494  |   prefix.ip = *addr;
495  |   prefix.bits = IP_sizebits(addr->space);
496  |   
497  |   /* lock the tree accordingly */
498  |   TH_acquire_read_lock( &(act_acl->rwlock) );  
499  |   
500  |   /* find an applicable record */
501  |   ac_find_acl_l(RX_SRCH_EXLESS, &prefix, &acl_record);
502  |   
503  |   /* calculate the credit if pointer given */
504  |   if( credit_acc ) {
505  |     memset( credit_acc, 0, sizeof(acc_st));
506  |     
507  |     /* credit = -1 if unlimited, otherwise credit = limit - bonus */
508  |     credit_acc->public_objects = 
509  |       ( acl_record.maxpublic == -1 ) 
510  |       ? -1 /* -1 == unlimited */
511  |       : (acl_record.maxpublic - run_acc.public_bonus);
512  |     
513  |     credit_acc->private_objects =
514  |       ( acl_record.maxprivate == -1 ) 
515  |       ? -1 /* -1 == unlimited */
516  |       : (acl_record.maxprivate - run_acc.private_bonus);
517  |   }
518  |   
519  |   /* copy the acl record if asked for it*/
520  |   if( acl_store ) {
521  |     *acl_store =  acl_record;
522  |   }
523  | 
524  |   /* release lock */
525  |   TH_release_read_lock( &(act_acl->rwlock) );
526  |   
527  |  
528  |   return ret_err;
529  | }
530  | 
531  | 
532  | 
533  | /*++++++++++++++++++++++++++++++++++++++  
534  |   AC_acc_addup:
535  | 
536  |   Add/subtract the values from one accounting structure to another
537  | 
538  |   acc_st *a   this one gets changed
539  | 
540  |   acc_st *b   this one provides the values to change a
541  | 
542  |   int minus   triggers subtraction if non-zero
543  | 
544  | +++++++++++++++++++++++++++++++++++++++*/
545  | void AC_acc_addup(acc_st *a, acc_st *b, int minus)
546  | {
547  |   int mul = minus ? -1 : 1;
548  |   
549  |   /* add all counters from b to those in a */
550  |   a->connections     +=  mul * b->connections;   
551  |   a->addrpasses      +=  mul * b->addrpasses;  
552  |  
553  |   a->denials         +=  mul * b->denials;      
554  |   a->queries         +=  mul * b->queries;       
555  |   a->referrals       +=  mul * b->referrals;
556  |   a->public_objects  +=  mul * b->public_objects;
557  |   a->private_objects +=  mul * b->private_objects;
558  |   a->private_bonus   +=  mul * b->private_bonus;
559  |   a->public_bonus    +=  mul * b->public_bonus;
560  | }/* AC_acc_addup */
561  | 
562  | /*++++++++++++++++++++++++++++++++++++++ 
563  |   AC_commit_credit_l:
564  | 
565  |   performs the commit on an accounting tree (locks them first)
566  |   stores a copy of the accounting record at rec_store
567  | 
568  |   Assumes locked tree.
569  | 
570  |   rx_tree_t *tree      - the tree
571  | 
572  |   ip_prefix_t *prefix  - prefix (usually a /32)
573  | 
574  |   acc_st *acc_conn     - credit used
575  | 
576  |   acc_st *rec_store    - pointer to store the account struct or NULL
577  | 
578  |   returns error code from AC_findcreate_account_l or OK
579  | 
580  |   MT-Note: locks/unlocks the accounting tree
581  | +++++++++++++++++++++++++++++++++++++++*/
582  | static
583  | er_ret_t 
584  | AC_commit_credit_l(rx_tree_t *tree, ip_prefix_t *prefix, 
585  | 		 acc_st *acc_conn, acc_st *rec_store )
586  | {
587  |   acc_st      *accountrec;
588  |   er_ret_t    ret_err;
589  | 
590  | 
591  |   acc_conn->private_bonus = acc_conn->private_objects;
592  |   acc_conn->public_bonus  = acc_conn->public_objects;
593  | 
594  |   
595  |   ret_err = AC_findcreate_account_l(act_runtime, prefix, &accountrec);
596  |   
597  |   if( NOERR(ret_err)) {
598  |     AC_acc_addup(accountrec, acc_conn, ACC_PLUS);
599  |   }
600  | 
601  |   if( rec_store ) {
602  |     *rec_store = *accountrec;
603  |   }
604  |   
605  |   return ret_err;
606  | }/* AC_commit_credit */
607  | 
608  | /*++++++++++++++++++++++++++++++++++++++  
609  |   AC_dbopen_admin:
610  | 
611  |   opens the ADMIN database and returns a pointer to the connection structure
612  |   (rationale: the opening process became a bit bloated and is done twice,
613  |   so I put it into a separate function)
614  | ++++++++++++++++++++++++++++++++++++++*/
615  | SQ_connection_t *
616  | AC_dbopen_admin(void)
617  | {
618  |   SQ_connection_t *con=NULL;
619  |   char *dbhost = ca_get_ripadminhost;
620  |   char *dbname = ca_get_ripadmintable;
621  |   char *dbuser = ca_get_ripadminuser;
622  |   char *dbpass = ca_get_ripadminpassword;
623  |   unsigned dbport = ca_get_ripadminport;
624  |   
625  |   if( (con = SQ_get_connection(dbhost, dbport, dbname, dbuser, dbpass) 
626  |        ) == NULL ) {
627  |     fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
628  |     die;
629  |   }
630  |   
631  |   free(dbhost);
632  |   free(dbname);
633  |   free(dbuser);
634  |   free(dbpass);
635  | 
636  |   return con;
637  | }
638  | 
639  | /*++++++++++++++++++++++++++++++++++++++  
640  |   AC_acl_sql:
641  | 
642  |   updates/creates a record for the given prefix in the acl table of 
643  |   the RIPADMIN database. Adds a comment.
644  | 
645  |   ip_prefix_t *prefix  - prefix
646  | 
647  |   acl_st *newacl       - new values to store in the database
648  | 
649  |   char *newcomment     - comment to be added (must not be NULL)
650  |   
651  |   placeholder: it may return an error code from SQ - as soon as sq 
652  |   implements common error scheme
653  | 
654  |  ++++++++++++++++++++++++++++++++++++++*/
655  | er_ret_t 
656  | AC_acl_sql(ip_prefix_t *prefix, acl_st *newacl, char *newcomment )
657  | {  
658  |   SQ_connection_t *sql_connection = NULL;
659  |   SQ_result_set_t *result;
660  |   SQ_row_t *row;
661  |   char *oldcomment;
662  |   char *query;
663  |   char querybuf[256];
664  | 
665  |   sql_connection = AC_dbopen_admin();
666  |   
667  |   /* get the old entry, extend it */
668  |   sprintf(querybuf, "SELECT comment FROM acl WHERE "
669  | 	  "prefix = %u AND prefix_length = %d", 
670  | 	  prefix->ip.words[0],
671  | 	  prefix->bits);
672  |   dieif( SQ_execute_query(sql_connection, querybuf, &result) == -1 );
673  |   
674  |   if( SQ_num_rows(result) == 1 ) {
675  |     dieif( (row = SQ_row_next(result)) == NULL);
676  |     oldcomment = SQ_get_column_string(result, row, 0);
677  |   }
678  |   else {
679  |     oldcomment = "";
680  |   }
681  | 
682  |   SQ_free_result(result);
683  |   
684  |   /* must hold the thing below (REPLACE..blah blah blah) + text */
685  |   dieif( wr_malloc((void **)&query, 
686  | 		   strlen(oldcomment) + strlen(newcomment) + 256) != UT_OK );
687  |   
688  |   /* compose new entry and insert it */
689  |   sprintf(query, "REPLACE INTO acl VALUES(%u, %d, %d, %d, %d, %d, %d,"
690  | 	  "\"%s%s%s\")",
691  | 	  prefix->ip.words[0],
692  | 	  prefix->bits,
693  | 	  newacl->maxprivate,
694  | 	  newacl->maxpublic,
695  | 	  newacl->maxdenials,
696  | 	  newacl->deny,
697  | 	  newacl->trustpass,
698  | 	  oldcomment, 
699  | 	  strlen(oldcomment) > 0 ? "\n" : "",
700  | 	  newcomment
701  | 	  );
702  |   
703  |   SQ_execute_query(sql_connection, query, NULL);
704  |   SQ_close_connection(sql_connection);
705  |   
706  |   wr_free(query);
707  |   
708  |   return AC_OK;
709  | 
710  | }/* AC_acl_sql */
711  | 
712  | /*++++++++++++++++++++++++++++++++++++++ 
713  |   AC_ban_set:
714  |   
715  |   re/sets the permanent ban flag both in the acl tree in memory
716  |   and the sql table. The "text" is appended to the comment 
717  |   in the sql record (the expected cases are
718  |   - "automatic" in case the limit is exceeded and ban is set by s/w
719  |   - "manual"    in case it is (un)set from the config iface
720  | 
721  |   ip_prefix_t *prefix   - prefix 
722  | 
723  |   char *text            - usually "automatic" or "manual"  
724  | 
725  |   int denyflag          - new value of the denyflag (ban)
726  |   
727  |   returns error code from AC_acl_sql or OK
728  |   +++++++++++++++++++++++++++++++++++++++*/
729  | er_ret_t
730  | AC_ban_set(ip_prefix_t *prefix, char *text, int denyflag)
731  | {
732  |   acl_st *treeacl;
733  |   char newcomment[256];
734  |   er_ret_t ret_err;
735  |   time_t  clock;
736  |   char timebuf[26];
737  |   char prefstr[IP_PREFSTR_MAX];
738  |   
739  |   time(&clock);
740  |   ctime_r(&clock, timebuf);
741  | 
742  |   sprintf(newcomment,"%s permanent ban set to %d at %s", text, 
743  | 	  denyflag, timebuf);
744  | 
745  |   if( IP_pref_b2a(prefix, prefstr, IP_PREFSTR_MAX) != IP_OK ) {
746  |     die; /* program error - this is already converted so must be OK */
747  |   }
748  |   
749  |   ER_inf_va( FAC_AC, ASP_AC_I_PERMBAN, 
750  | 	     "%s permanent ban set to %d for %s", text, denyflag, prefstr );
751  |     
752  |   TH_acquire_write_lock( &(act_acl->rwlock) );  
753  | 
754  |   /* find a record in the tree */  
755  |   if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
756  |     treeacl->deny = denyflag;
757  |     ret_err = AC_acl_sql( prefix, treeacl, newcomment );
758  |   }
759  |   TH_release_write_lock( &(act_acl->rwlock) );
760  | 
761  |   return ret_err;
762  | }/* AC_ban_set */
763  | 
764  | 
765  | /*++++++++++++++++++++++++++++++++++++++ 
766  |   AC_asc_ban_set:
767  |   
768  |   sets ban on text address/range. Parses the text address/range/prefix 
769  |   and then calls AC_ban_set on that prefix. 
770  |   
771  |   Precondition: if the key is a range, it must decompose into one prefix 
772  |   
773  |   returns error code from IP_smart_conv, AC_ban_set or 
774  |   AC_INVARG if range composed
775  |   +++++++++++++++++++++++++++++++++++++++*/
776  | er_ret_t
777  | AC_asc_ban_set(char *addrstr, char *text, int denyflag)
778  | {
779  |   er_ret_t ret_err;
780  |   GList *preflist = NULL;
781  |   ip_keytype_t key_type;
782  | 
783  |   if( (ret_err = IP_smart_conv(addrstr, 0, 0,
784  | 			       &preflist, IP_PLAIN, &key_type)) != IP_OK ) {
785  |     return ret_err;
786  |   }
787  |   
788  |   /* allow only one prefix */
789  |   /* The argument can be even a range, but must decompose into one prefix */
790  |   if(  NOERR(ret_err) && g_list_length( preflist ) != 1 ) {
791  |     ret_err = AC_INVARG;
792  |   }
793  |   
794  |   if( NOERR(ret_err) ) {
795  |     ret_err = AC_ban_set( (g_list_first(preflist)->data), text, denyflag);
796  |   }
797  | 
798  |   wr_clear_list( &preflist );
799  |   
800  |   return ret_err;
801  | }/* AC_asc_ban_set */
802  | 
803  | /*++++++++++++++++++++++++++++++++++++++ 
804  |   AC_asc_all_set:
805  | 
806  |   take ascii prefix and find/create a new entry, inheriting all parameters
807  |   and then set them according to the array of args.
808  | 
809  | +*/
810  | er_ret_t
811  | AC_asc_all_set(ip_prefix_t *prefix, char *comment, char * array[])
812  | {
813  |   er_ret_t ret_err;
814  |   acl_st *treeacl;
815  |   int i;
816  | 
817  |   TH_acquire_write_lock( &(act_acl->rwlock) );  
818  | 
819  |   /* find/create a record in the tree */  
820  |   if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
821  |    
822  |     /* update it from the array */
823  |     for(i=0; i<AC_AR_SIZE; i++) {
824  |       if(array[i] != NULL) { /* set only those that have been specified */
825  | 	int val,k;
826  | 	
827  | 	if( (k=sscanf( array[i], "%d", &val)) < 1 ) {
828  | 	  ret_err = AC_INVARG;
829  | 	  break; /* quit the for */
830  | 	}
831  | 	
832  | 	/* otherwise, the value makes sense. Put it in the structure. */
833  | 	switch(i) {
834  | 	case AC_AR_MAXPRIVATE: treeacl->maxprivate = val; break;
835  | 	case AC_AR_MAXPUBLIC:  treeacl->maxpublic  = val; break;
836  | 	case AC_AR_MAXDENIALS: treeacl->maxdenials = val; break;
837  | 	case AC_AR_DENY:       treeacl->deny       = val; break;
838  | 	case AC_AR_TRUSTPASS:  treeacl->trustpass  = val; break;
839  | 	} /* switch */
840  |       } /* if array[i] not null */
841  |     } /* for each array element */
842  | 
843  |     if( NOERR(ret_err) ) { /* protect against AC_INVARG */
844  |       ret_err = AC_acl_sql( prefix, treeacl, comment );
845  |     }
846  |   } /* if find/create OK */
847  |   
848  |   TH_release_write_lock( &(act_acl->rwlock) );
849  |   
850  |   return ret_err;
851  | }
852  | 
853  | 
854  | /*++++++++++++++++++++++++++++++++++++++ 
855  | AC_asc_acl_command_set:
856  | 
857  |   parse a command and set acl options for an entry.
858  |   command syntax:
859  | 
860  |   <prefix> option=value,option=value,option=value...
861  | 
862  |   where <option> is defined in AC_ar_acl[] array, value is an integer
863  | 
864  |   char *command  text of the command. 
865  |                  Syntax: ip[/prefixlength] column=value,column=value...
866  |                  Column names as in acl display. Unset columns are inherited.
867  | 
868  |   char *comment  text to be added to the acl record's comment column.
869  | 
870  |   ++++++++++++++++++++++++++++++++++++++*/
871  | 
872  | er_ret_t
873  | AC_asc_acl_command_set( char *command, char *comment )
874  | {
875  |   ip_prefix_t *prefix;
876  |   char *eop, *eoc, *value;
877  |   char *array[AC_AR_SIZE];
878  |   er_ret_t ret_err = AC_OK;
879  |   GList *preflist = NULL;
880  |   ip_keytype_t key_type;
881  | 
882  |   char *copy = strdup(command);
883  |   char *addrstr = copy;
884  |   eoc = strchr(copy, '\0'); /* points to the end of it */
885  |   
886  |   memset(array, 0 ,sizeof(array));
887  | 
888  |   /* first comes the prefix. Find the space after it
889  |      and break the string there.
890  |   */
891  |   if( (eop = strchr(copy,' ')) == NULL) {
892  |     ret_err = AC_INVARG;
893  |   }
894  | 
895  |   if( NOERR(ret_err) ) { 
896  |     *eop++ = 0;
897  |   
898  |     /* now eop points to the rest of the string (if any). Take options.
899  |      */
900  |     while( eop != eoc && ret_err == AC_OK) {
901  |       char *sp;
902  | 
903  |       /* give getsubopt chunks with no spaces */
904  |       if( (sp = strchr(eop, ' ')) != NULL ) {
905  | 	*sp=0;
906  |       }
907  |       
908  |       while( *eop != '\0' ) {
909  | 	int k = getsubopt(&eop, AC_ar_acl, &value);
910  | 	if( k < 0 ) {
911  | 	  ret_err = AC_INVARG;
912  | 	  break;
913  | 	}
914  | 	
915  | 	array[k] = value;
916  |       }
917  |       
918  |       if( eop != eoc ) { /*getsubopt finished but did not consume all string*/
919  | 	eop ++;            /* must have been a space. advance one */
920  |       }
921  |     }
922  |   }
923  |     
924  |   /* convert the prefix */
925  |   if(  NOERR(ret_err) ) {
926  |     ret_err = IP_smart_conv(addrstr, 0, 0, &preflist, IP_PLAIN, &key_type);
927  |     
928  |     /* allow only one prefix */
929  |     /* The argument can be even a range, but must decompose into one prefix */
930  |     if(  NOERR(ret_err) && g_list_length( preflist ) == 1 ) {
931  |       prefix = (g_list_first(preflist)->data);
932  |     }
933  |     else {
934  |       ret_err = AC_INVARG;
935  |     }
936  |   }
937  |   
938  |   /* perform changes */
939  |   if(  NOERR(ret_err) ) {
940  |     ret_err = AC_asc_all_set(prefix, comment, array);
941  |   }
942  | 
943  |   wr_clear_list( &preflist );
944  |   free(copy);
945  | 
946  |   return ret_err;
947  | }/* AC_asc_acl_command_set */
948  | 
949  | 
950  | /*++++++++++++++++++++++++++++++++++++++ 
951  |   AC_asc_set_nodeny:
952  | 
953  |   reset the deny counter in the access tree to 0 (after reenabling).
954  | 
955  |   Operates on the runtime access tree. 
956  | 
957  |   char *ip      text IP (ip only, not prefix or range).
958  |   +++++++++++++++++++++++++++++++++++++++*/
959  | er_ret_t AC_asc_set_nodeny(char *ip)
960  | {
961  |   ip_prefix_t  prefix;
962  |   er_ret_t     ret_err;
963  |   acc_st *ac_ptr;
964  | 
965  |   ret_err = IP_addr_e2b( &(prefix.ip), ip );
966  |   prefix.bits = IP_sizebits(prefix.ip.space);
967  |   
968  |   if( NOERR(ret_err)) {
969  |     TH_acquire_write_lock( &(act_runtime->rwlock) );
970  |     
971  |     ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
972  |     if( NOERR(ret_err)) {
973  |       ac_ptr->denials = 0;
974  |     }
975  |     
976  |     TH_release_write_lock( &(act_runtime->rwlock) );
977  |   }
978  | 
979  |   return ret_err;
980  | }
981  | 
982  | 
983  | 
984  | /*++++++++++++++++++++++++++++++++++++++ 
985  |   AC_commit:
986  | 
987  |   commits the credit into all accounting trees, (XXX: only one at the moment)
988  |   checks the limits and sets automatic ban if limit exceeded.
989  | 
990  |   ip_addr_t *addr  - user's address
991  | 
992  |   acc_st *acc_conn - credit used
993  | 
994  |   acl_st *acl_copy - pointer to store a copy of the acl
995  | 
996  |   returns error code from AC_commit_credit or AC_ban_set or OK.
997  | 
998  |   outline:
999  |         lock runtime + minute accounting trees 
1000 | 	-----------------------  XXX runtime only for the moment
1001 |            find or create entries, 
1002 |            increase accounting values by the values from passed acc
1003 |            check values against acl, see if permanent ban applies
1004 | 
1005 |            reset the connection acc
1006 |         unlock accounting trees
1007 | 
1008 |         if permanent ban - set it! :
1009 |             lock acl
1010 |             find/create IP in memory
1011 |             set ban
1012 |             find/create IP in SQL
1013 |             copy old values (if any), set ban, append comment
1014 |             unlock acl
1015 | 
1016 |  +++++++++++++++++++++++++++++++++++++++*/
1017 | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn, acl_st *acl_copy) { 
1018 |   acc_st   account;
1019 |   er_ret_t ret_err;
1020 |   ip_prefix_t prefix;
1021 | 
1022 |   prefix.ip = *addr;
1023 |   prefix.bits = IP_sizebits(addr->space);
1024 | 
1025 |   TH_acquire_write_lock( &(act_runtime->rwlock) );
1026 |   ret_err = AC_commit_credit_l(act_runtime, &prefix, acc_conn, &account);
1027 |   TH_release_write_lock( &(act_runtime->rwlock) );
1028 |  /* XXX add more trees here */
1029 | 
1030 |   memset(acc_conn,0, sizeof(acc_st));
1031 | 
1032 |   /* set permanent ban if deserved  and if not set yet */
1033 |   if( account.denials > acl_copy->maxdenials 
1034 |       && acl_copy->deny == 0 
1035 |       && NOERR(ret_err) ) {
1036 |     
1037 |     ret_err = AC_ban_set(&prefix, "Automatic", 1);
1038 |   }
1039 | 
1040 |   return ret_err;
1041 | } /* AC_commit */
1042 | 
1043 | 
1044 |  
1045 | /*++++++++++++++++++++++++++++++++++++++
1046 |   
1047 |  
1048 | unsigned AC_prune     deletes the entries listed in the prunelist
1049 |                     (this cannot be done from within the rx_walk_tree,
1050 | 		    because the walk would be confused).
1051 | 		    Returns number of nodes deleted.
1052 | 
1053 | GList *prunelist  list of pointers to nodes that should be deleted.
1054 |                   the prefixes actually are allocated in the node
1055 | 		  structures, so they must not be dereferenced after 
1056 | 		  they are freed here.
1057 | 
1058 |   ++++++++++++++++++++++++++++++++++++++*/
1059 | unsigned AC_prune(GList *prunelist)
1060 | {
1061 |   GList *pitem;
1062 |   char prstr[IP_PREFSTR_MAX];
1063 |   unsigned count = 0;
1064 |   acc_st accu; /* to accumulate the accounting of deleted nodes */
1065 |   ip_prefix_t globalpref;
1066 | 
1067 |   memset( &accu, 0, sizeof(accu));
1068 |   
1069 |   for( pitem = g_list_first(prunelist);
1070 |        pitem != NULL;
1071 |        pitem = g_list_next(pitem)) {
1072 |     
1073 |     rx_node_t   *nodeptr = (rx_node_t *) pitem->data;
1074 |     ip_prefix_t *prefptr = &(nodeptr->prefix);
1075 |     acc_st      *nodeacc = nodeptr->leaves_ptr->data;
1076 |     
1077 |     AC_acc_addup(&accu, nodeacc, ACC_PLUS); /* transfer the account */
1078 | 
1079 |     dieif( IP_pref_b2a( prefptr, prstr, IP_PREFSTR_MAX ) != IP_OK );
1080 |     ER_dbg_va( FAC_AC, ASP_AC_PRUNE_DET, "AC_prune: entry %s", prstr );
1081 |     
1082 |     /* delete the node. Assume there's one and only one dataleaf */
1083 |     rx_bin_node( RX_OPER_DEL, prefptr, act_runtime, (void *)nodeacc );
1084 |     count ++;
1085 |   }
1086 | 
1087 |   /* store the accumulated account at 0/0 */
1088 |   dieif( !NOERR (IP_pref_a2b( &globalpref, "0/0" )));
1089 |   AC_commit_credit_l(act_runtime, &globalpref, &accu, NULL);
1090 | 
1091 |   return count;
1092 | }
1093 | 
1094 | 
1095 | 
1096 | /*++++++++++++++++++++++++++++++++++++++ 
1097 |   AC_decay_hook:
1098 | 
1099 |   action performed on a single account node during decay (diminishing the
1100 |   bonus). Conforms to rx_walk_tree interface, therefore some of the 
1101 |   arguments do not apply and are not used.
1102 | 
1103 |   rx_node_t *node  - pointer to the node of the radix tree
1104 | 
1105 |   int level        - not used
1106 | 
1107 |   int nodecounter  - not used
1108 | 
1109 |   void *con        - in real life: (double *) - points to the decay factor.
1110 | 
1111 |   returns always OK
1112 | +++++++++++++++++++++++++++++++++++++++*/
1113 | er_ret_t AC_decay_hook(rx_node_t *node, int level, 
1114 | 		       int nodecounter, void *con)
1115 | {
1116 |   acc_st *a = node->leaves_ptr->data;
1117 |   ac_decay_data_t *dec_dat_p = (ac_decay_data_t *)con;
1118 |   double factor = dec_dat_p->decay_factor;
1119 |   double bpr, bpu;
1120 | 
1121 |   bpr = a->private_bonus;
1122 |   bpu = a->public_bonus;
1123 | 
1124 |   a->private_bonus *= factor;
1125 |   a->public_bonus  *= factor;
1126 | 
1127 |   /* XXX pending: if bonus is close to zero and the node did not hit 
1128 |      its limit, and it's not an address-passing node
1129 |      then add it to the list of nodes for deletion */
1130 |   
1131 |   ER_dbg_va( FAC_AC, ASP_AC_PRUNE_DET, 
1132 | 	     "%5.2f / %5.2f   * %5.2f  -> %5.2f / %5.2f ",
1133 | 	     bpr, bpu, factor, a->private_bonus, a->public_bonus);
1134 | 
1135 |   if(    a->private_bonus < 0.5  
1136 |       && a->public_bonus  < 0.5 
1137 |       && a->denials == 0          
1138 |       && a->addrpasses == 0 ) {
1139 |     dec_dat_p->prunelist = g_list_append(dec_dat_p->prunelist, node);
1140 |   }
1141 | 
1142 |   /* process accounting - add all queries to the total counter */
1143 |   dec_dat_p->newtotal += a->queries;
1144 | 
1145 |   return RX_OK;
1146 | } /* AC_decay_hook() */
1147 | 
1148 | 
1149 | 
1150 | /*++++++++++++++++++++++++++++++++++++++
1151 |   AC_decay:
1152 |   
1153 |   Every AC_DECAY_TIME goes through the accounting tree(s) and decays the 
1154 |   bonus values.
1155 |   
1156 |   returns always OK
1157 | 
1158 |   MT-Note  This should be run as a detached thread.
1159 |   +++++++++++++++++++++++++++++++++++++++*/
1160 | er_ret_t AC_decay(void) {
1161 |   er_ret_t ret_err = AC_OK;
1162 |   ac_decay_data_t dec_dat;
1163 |   ut_timer_t begintime, endtime;
1164 |   unsigned pruned;
1165 |   float elapsed, rate, exactinterval;
1166 |   unsigned oldtotal = 0;
1167 |   unsigned increase;
1168 |   unsigned count;
1169 | 
1170 |   TA_add(0, "decay");
1171 | 
1172 |   UT_timeget( &endtime );
1173 |   
1174 |   /* XXX uses CO_get_do_server() to see when to exit the program.
1175 |      this will change */
1176 |   while(CO_get_do_server()) {
1177 |     UT_timeget( &begintime );
1178 |     exactinterval =  UT_timediff( &endtime, &begintime ); /* old endtime */
1179 |     
1180 |     /* those values can be changed in runtime - so recalculate 
1181 |        the decay factor vefore each pass */
1182 |     dieif( ca_get_ac_decay_halflife == 0 );
1183 | 
1184 |     dec_dat.prunelist = NULL;  
1185 |     /* the decay factor of 
1186 |        f(t) = exp(-a*t) 
1187 |        a = -ln(0.5) / t     
1188 |        so for T being the half-life period and v being the sampling interval
1189 |        used as the unit of time
1190 |        a = -ln(0.5) / T;
1191 |        f(t+x) = exp(-a(t+x)) = f(t)*f(x) = f(t)*exp(-ax) = 
1192 |        = f(t)*exp(ln(0.5)*v/T)
1193 |        so you multiply the previous value by exp(ln(0.5)*v/T)
1194 |     */
1195 |     dec_dat.decay_factor =  
1196 |       exp ( -0.693147180559945 * exactinterval / ca_get_ac_decay_halflife) ;
1197 |     dec_dat.newtotal = 0;
1198 | 
1199 |     TH_acquire_write_lock( &(act_runtime->rwlock) );
1200 | 
1201 |     if( act_runtime->top_ptr != NULL ) {
1202 |       count = rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
1203 | 			   RX_WALK_SKPGLU,  /* skip glue nodes */
1204 | 			   255, 0, 0, &dec_dat, &ret_err);
1205 |     }
1206 |     else {
1207 |       count = 0;
1208 |     }
1209 | 
1210 |     /* it should also be as smart as to delete nodes that have reached 
1211 |        zero, otherwise the whole of memory will be filled.
1212 |        Next release :-)
1213 |     */
1214 |     
1215 |     pruned = AC_prune( dec_dat.prunelist ); 
1216 |     g_list_free( dec_dat.prunelist ); 
1217 | 
1218 | #if 0
1219 |     /* XXX dbg checking tree consistency */
1220 |     if( act_runtime->top_ptr != NULL ) {
1221 |       rx_treecheck_t errorfound;
1222 |       er_ret_t err;
1223 |       if( (err=RX_treecheck(act_runtime, 1, &errorfound)) != RX_OK ) {
1224 | 	fprintf(stderr, "Nope! %d returned \n", err);
1225 | 	ER_dbg_va( FAC_AC, ASP_AC_DECAY,
1226 | 		   "AC: checking access tree consistency: error %d", err);
1227 | 	die; /* access tree not consistent */
1228 |       }
1229 |     }
1230 | #endif
1231 |   
1232 |     TH_release_write_lock( &(act_runtime->rwlock) );
1233 | 
1234 |     UT_timeget(&endtime);
1235 |     
1236 |     elapsed = UT_timediff( &begintime, &endtime);
1237 |       
1238 |     ER_dbg_va( FAC_AC, ASP_AC_DECAY,
1239 | 	      "AC_decay: Pruned %d of %d nodes. Took %5.3fs. Runs every %ds.", 
1240 | 	       pruned, count, elapsed, ca_get_ac_decay_interval);  
1241 | 
1242 |     /* number/rate of queries within the last <interval> */ 
1243 |     {
1244 |       char actbuf[32];
1245 |       
1246 |       increase = dec_dat.newtotal - oldtotal;
1247 |       rate = increase / exactinterval;
1248 | 
1249 |       sprintf(actbuf, "%.2f q/s in %.1fs", rate, exactinterval);
1250 |       TA_setactivity(actbuf);
1251 |       
1252 |       oldtotal = dec_dat.newtotal;
1253 |     }
1254 |     
1255 |     SV_sleep(ca_get_ac_decay_interval);
1256 |   } /* while */
1257 | 
1258 |   TA_delete();
1259 |   
1260 |   return ret_err;
1261 | } /* AC_decay() */
1262 | 
1263 | 
1264 | /*++++++++++++++++++++++++++++++++++++++ 
1265 |   AC_acc_load:
1266 | 
1267 |   loads the acl access tree from the acl table of the RIPADMIN database.
1268 |   (takes port/host/user/password from the config module).
1269 |   
1270 |   bails out if encounters problems with the database (logs to stderr).
1271 | 
1272 |   returns error code from RX_bin_node or wr_malloc.
1273 |   ++++++++++++++++++++++++++++++++++++++*/
1274 | er_ret_t AC_acc_load(void)
1275 | {
1276 |   SQ_connection_t *con=NULL;
1277 |   SQ_result_set_t *result;
1278 |   SQ_row_t *row;
1279 |   er_ret_t ret_err = RX_OK;
1280 | 
1281 |   con = AC_dbopen_admin();
1282 | 
1283 |   if( SQ_execute_query(con, "SELECT * FROM acl", &result) == -1 ) {
1284 |       fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
1285 |       die;
1286 |   }
1287 |   
1288 |   TH_acquire_write_lock( &(act_acl->rwlock) );
1289 | 
1290 |   while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
1291 |     ip_prefix_t mypref;
1292 |     acl_st *newacl;
1293 |  #define NUMELEM (7)
1294 |     char *col[NUMELEM];
1295 |     unsigned myint, i;
1296 | 
1297 |     memset(&mypref, 0, sizeof(ip_prefix_t));
1298 |     mypref.ip.space = IP_V4;
1299 |     
1300 |     if( (ret_err = wr_malloc( (void **)& newacl, sizeof(acl_st))
1301 |          ) == UT_OK ) {
1302 | 
1303 |       for(i=0; i<NUMELEM; i++) {
1304 |         if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
1305 |           die;
1306 |         }
1307 |       }
1308 |       
1309 |       /* prefix ip */
1310 |       if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
1311 |       
1312 |       /* prefix length */
1313 |       if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
1314 |       
1315 |       /* acl contents */
1316 |       if( sscanf(col[2], "%u",  & (newacl->maxprivate)  ) < 1 ) { die; }
1317 |       if( sscanf(col[3], "%u",  & (newacl->maxpublic)   ) < 1 ) { die; }
1318 |       if( sscanf(col[4], "%hd", & (newacl->maxdenials)  ) < 1 ) { die; }
1319 |       
1320 |       /* these are chars therefore cannot read directly */
1321 |       if( sscanf(col[5], "%u", &myint              ) < 1 ) { die; }
1322 |       else {
1323 |         newacl->deny = myint;
1324 |       }
1325 |       if( sscanf(col[6], "%u", &myint  ) < 1 ) { die; }
1326 |       else {
1327 |         newacl->trustpass = myint;
1328 |       }
1329 |       
1330 |       /* free space */
1331 |       for(i=0; i<NUMELEM; i++) {
1332 | 	  wr_free(col[i]);
1333 |       }
1334 |       
1335 |       /* now add to the tree */      
1336 |       ret_err = rx_bin_node( RX_OPER_CRE, &mypref, 
1337 |                              act_acl, (rx_dataleaf_t *) newacl );
1338 |     }
1339 |   } /* while row */
1340 | 
1341 |   TH_release_write_lock( &(act_acl->rwlock) );
1342 | 
1343 |   SQ_free_result(result);
1344 |   /* Close connection */
1345 |   SQ_close_connection(con);
1346 | 
1347 |   return ret_err;
1348 | } /* AC_acc_load */
1349 | 
1350 | 
1351 | 
1352 | /*++++++++++++++++++++++++++++++++++++++ 
1353 |   AC_build:
1354 | 
1355 |   creates empty trees for accounting/acl.
1356 |   
1357 |   returns error code from RX_tree_cre or OK.
1358 |   (XXX): just now only bails out when encounters problems.
1359 |   ++++++++++++++++++++++++++++++++++++++*/
1360 | er_ret_t AC_build(void) 
1361 | {
1362 |   /* create trees */
1363 |   if (      RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
1364 | 			RX_SUB_NONE, &act_runtime) != RX_OK
1365 | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
1366 | 			RX_SUB_NONE, &act_hour) != RX_OK
1367 | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
1368 | 			RX_SUB_NONE, &act_minute) != RX_OK
1369 | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
1370 | 			RX_SUB_NONE, &act_acl) != RX_OK
1371 |          )
1372 |     die; /*can be changed to an error and handled ... some day */
1373 | 
1374 |   return RX_OK;
1375 | }
1376 | 
1377 | /*++++++++++++++++++++++++++++++++++++++ 
1378 |   ac_rxwalkhook_print:
1379 | 
1380 |   action performed on a single account node 
1381 |   when listing the contents of the access tree: format and print the
1382 |   data from this node.
1383 | 
1384 |   Conforms to rx_walk_tree interface, therefore some of the 
1385 |   arguments do not apply and are not used.
1386 |   
1387 |   rx_node_t *node  - pointer to the node of the radix tree
1388 | 
1389 |   int level        - not used
1390 | 
1391 |   int nodecounter  - not used
1392 | 
1393 |   void *con        - pointer to the target string (prints to it)
1394 |   
1395 |   returns always OK 
1396 | +++++++++++++++++++++++++++++++++++++++*/
1397 | static
1398 | er_ret_t ac_rxwalkhook_print(rx_node_t *node, 
1399 |                              int level, int nodecounter, 
1400 |                              void *outvoid)
1401 | {
1402 |   char adstr[IP_ADDRSTR_MAX];
1403 |   char *dat;
1404 |   GString *output = outvoid;
1405 |   
1406 |   dieif( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK );
1407 |   /* program error. */
1408 |   
1409 |   dat = ac_to_string( node->leaves_ptr );
1410 |   g_string_sprintfa(output, "%-20s %s\n", adstr, dat );
1411 |   wr_free(dat);
1412 |   
1413 |   return RX_OK;
1414 | } /* ac_rxwalkhook_print */
1415 | 
1416 | 
1417 | /*++++++++++++++++++++++++++++++++++++++
1418 |   This function displays the access table to the given connection.
1419 | 
1420 |   unsigned AC_print_access    Returns the number of nodes traversed
1421 | 
1422 |   GString *output             target string
1423 |   ++++++++++++++++++++++++++++++++++++++*/
1424 | unsigned AC_print_access(GString *output)
1425 | {
1426 |   int cnt = 0;
1427 |   er_ret_t err; 
1428 | 
1429 |   if( act_runtime->top_ptr != NULL ) {
1430 |     char *header = ac_to_string_header();
1431 |     
1432 |     /* print header */
1433 |     g_string_append(output, header);
1434 |     wr_free(header);
1435 |     
1436 |     cnt = rx_walk_tree(act_runtime->top_ptr, ac_rxwalkhook_print, 
1437 | 		       RX_WALK_SKPGLU,  /* print no glue nodes */
1438 | 		       255, 0, 0, output, &err);
1439 |   }
1440 |   
1441 |   return cnt;
1442 | } /* show_access() */
1443 | 
1444 | 
1445 | 
1446 | /*++++++++++++++++++++++++++++++++++++++
1447 |   ac_rxwalkhook_print_acl:
1448 |   
1449 |   action performed on a single account node 
1450 |   when listing the contents of the acl tree: format and print the
1451 |   data from this node.
1452 | 
1453 |   Conforms to rx_walk_tree interface, therefore some of the 
1454 |   arguments do not apply and are not used.
1455 |   
1456 |   rx_node_t *node  - pointer to the node of the radix tree
1457 | 
1458 |   int level        - not used
1459 | 
1460 |   int nodecounter  - not used
1461 | 
1462 |   void *con        - pointer to the target string (prints to it)
1463 | 
1464 |   returns always OK 
1465 |   +++++++++++++++++++++++++++++++++++++++*/
1466 | static
1467 | er_ret_t ac_rxwalkhook_print_acl(rx_node_t *node, 
1468 |                              int level, int nodecounter, 
1469 |                              void *outvoid)
1470 | {
1471 |   char prefstr[IP_PREFSTR_MAX];
1472 |   char *dat;
1473 |   GString *output = outvoid;
1474 |   
1475 |   dieif( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK );
1476 |   
1477 |   dat = ac_acl_to_string( node->leaves_ptr );
1478 |   g_string_sprintfa(output, "%-20s %s\n", prefstr, dat );
1479 |   wr_free(dat);
1480 |   
1481 |   return RX_OK;
1482 | }/* ac_rxwalkhook_print_acl */
1483 | 
1484 | 
1485 | /*++++++++++++++++++++++++++++++++++++++
1486 |   This function writes the acl (access control) table to the given
1487 |   Gstring (auto-expandable)
1488 | 
1489 |   unsigned AC_print_acl     Returns the number of nodes traversed
1490 | 
1491 |   GString *output           target string
1492 |   ++++++++++++++++++++++++++++++++++++++*/
1493 | unsigned AC_print_acl(GString *output)
1494 | {
1495 |   /* Administrator wishes to show access control list. */  
1496 |   int cnt = 0;
1497 |   er_ret_t err; 
1498 | 
1499 |   if( act_acl->top_ptr != NULL ) {
1500 |     char *header = ac_acl_to_string_header();
1501 |     
1502 |     /* print header */
1503 |     g_string_append(output, header);
1504 |     wr_free(header);
1505 | 
1506 |     cnt = rx_walk_tree(act_acl->top_ptr, ac_rxwalkhook_print_acl, 
1507 | 		       RX_WALK_SKPGLU,  /* print no glue nodes */
1508 | 		       255, 0, 0, output, &err);
1509 |   }
1510 | 
1511 |   return cnt;
1512 | }
1513 | 
1514 | 
1515 | /*++++++++++++++++++++++++++++++++++++++
1516 |   AC_count_object:
1517 | 
1518 |   accounts an objects in the credit accordingly to its type, 
1519 |   or sets denial if the limit is defined and the credit is exceeded.
1520 | 
1521 |   acc_st    *acc_credit     pointer to the credit structure (gets modified)
1522 | 
1523 |   acl_st    *acl            acl, contains the limits for private/public objects
1524 | 
1525 |   int private               indicates if the object type is private
1526 |   ++++++++++++++++++++++++++++++++++++++*/
1527 | void
1528 | AC_count_object( acc_st    *acc_credit, 
1529 | 		 acl_st    *acl,
1530 | 		 int private )
1531 | {
1532 |   if( private ) { 
1533 |     if( acc_credit->private_objects <= 0 && acl->maxprivate != -1 ) {
1534 |       /* must be negative - will be subtracted */
1535 |       acc_credit->denials = -1;
1536 |     } else {
1537 |       acc_credit->private_objects --;
1538 |     }
1539 |   }
1540 |   else {
1541 |     if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) {
1542 |       acc_credit->denials = -1;
1543 |     } else {
1544 |       acc_credit->public_objects --;
1545 |     }
1546 |   }
1547 | } /* AC_count_object */
1548 | 
1549 | 
1550 | /* AC_credit_isdenied */
1551 | /*++++++++++++++++++++++++++++++++++++++
1552 |   
1553 |   checks the denied flag in credit (-1 or 1 means denied)
1554 |   
1555 |   int 
1556 |   AC_credit_isdenied     returns 1 if denied, 0 otherwise
1557 | 
1558 |   acc_st    *acc_credit    pointer to the credit structure
1559 |   ++++++++++++++++++++++++++++++++++++++*/
1560 | int 
1561 | AC_credit_isdenied(acc_st    *acc_credit)
1562 | {
1563 |   return (acc_credit->denials != 0);
1564 | } /* AC_credit_isdenied */
1565 |   
1566 | 
1567 | /* AC_get_higher_limit */
1568 | /*++++++++++++++++++++++++++++++++++++++
1569 | 
1570 |   returns the higher number of the two acl limits: maxprivate & maxpublic 
1571 |   corrected w.r.t the current credit left,
1572 |   or unlimited if any of them is 'unlimited'.
1573 | 
1574 |   int AC_get_higher_limit       returns the higher limit   
1575 | 
1576 |   acc_st    *acc_credit         current credit left
1577 |   
1578 |   acl_st    *acl                acl for that user
1579 | ++++++++++++++++++++++++++++++++++++++*/
1580 | int
1581 | AC_get_higher_limit(acc_st    *acc_credit, 
1582 | 		    acl_st    *acl)
1583 | {
1584 |   if( acl->maxprivate == -1 || acl->maxpublic == -1 ) {
1585 |     return -1;
1586 |   }
1587 |   else {
1588 |     int a = acc_credit->private_objects;
1589 |     int b = acc_credit->public_objects;
1590 | 
1591 |     return (a > b ? a : b);
1592 |   }
1593 | }/* AC_get_higher_limit */