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