1    | /***************************************
2    |   $Revision: 1.18 $
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   | 
33   | #define AC_OK RX_OK
34   | #define AC_INVARG IP_INVARG
35   | 
36   | #define AC_IMPL
37   | #include <rxroutines.h>
38   | #include <erroutines.h>
39   | #include <access_control.h>
40   | #include "socket.h"
41   | #include "mysql_driver.h"
42   | #include <constants.h>
43   | #include <server.h>
44   | 
45   | #define AC_DECAY_TIME 600
46   | /* #define AC_DECAY_TIME 3600 */
47   | 
48   | 
49   | #define ACL_FORMAT        "%10d %10d %10d %10d %10d"
50   | #define ACL_HEADER  "%-20s %10s %10s %10s %10s %10s\n"
51   | 
52   | 
53   | #define ACC_FORMAT       "%4d    %4d    %4d    %4d    %6d    %6d    %6d"
54   | #define ACC_HEADER "%-20s %4s    %4s    %4s    %4s    %6s    %6s    %6s\n"
55   | 
56   | /* AC_to_string_header() */
57   | char *AC_to_string_header(void) 
58   | {
59   |   char *result_buf;
60   | 
61   |   dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );
62   |   
63   |   sprintf(result_buf, ACC_HEADER, "ip",
64   | 	  "conn", "pass", "deny", "qry", "pub", "priv", "bonus" );
65   | 
66   |   return result_buf;
67   | }
68   | 
69   | /* AC_to_string() */
70   | /*++++++++++++++++++++++++++++++++++++++
71   |   Show an access structure
72   | 
73   |   More:
74   |   +html+ <PRE>
75   |   Authors:
76   |         marek
77   |   +html+ </PRE><DL COMPACT>
78   |   +html+ <DT>Online References:
79   |   +html+ </UL></DL>
80   | 
81   |   ++++++++++++++++++++++++++++++++++++++*/
82   | char *AC_to_string(GList *leafptr)
83   | {
84   |   char *result_buf;
85   |   acc_st *a = leafptr->data;
86   | 
87   |   if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
88   |       /* do many bad things...*/
89   |       return NULL;
90   |     }
91   |   
92   |   if( a == NULL ) {
93   |     strcpy(result_buf, "DATA MISSING!");
94   |   }
95   |   else {
96   |     sprintf(result_buf,
97   | 	    
98   | 	    /*            "conn %4d  pass %4d  den %4d  qrs %4d  pub %5d  priv %5d  bonus %5d",*/
99   | 	    ACC_FORMAT,
100  |             a->connections,
101  | 	    a->addrpasses,
102  |             a->denials,
103  |             a->queries,     
104  |             a->public_objects,
105  |             a->private_objects,
106  |             a->private_bonus
107  |             );
108  |   }
109  |   
110  |   return result_buf;
111  | } /* AC_to_string() */
112  | 
113  | 
114  | /*++++++++++++++++++++++++++++++++++++++
115  |   Show credit (for logging of queries)
116  | 
117  |   More:
118  |   +html+ <PRE>
119  |   Authors:
120  |         marek
121  |   +html+ </PRE><DL COMPACT>
122  |   +html+ <DT>Online References:
123  |   +html+ </UL></DL>
124  | 
125  |   ++++++++++++++++++++++++++++++++++++++*/
126  | char *AC_credit_to_string(acc_st *a)
127  | {
128  |   char *result_buf;
129  |   
130  |   if( wr_malloc( (void **) &result_buf, 64) != UT_OK ) {
131  |       /* do many bad things...*/
132  |       return NULL;
133  |     }
134  |   
135  |   dieif( a == NULL );
136  | 
137  |   sprintf(result_buf,"%d+%d%s",
138  | 	  a->private_objects,
139  | 	  a->public_objects,
140  | 	  a->denials ? " **DENIED**" : ""
141  | 	  );
142  | 
143  |   return result_buf;
144  | } /* AC_credit_to_string */ 
145  | 
146  | 
147  | char *
148  | AC_acl_to_string_header(void)
149  | {
150  |   char *result_buf;
151  |   dieif( wr_malloc( (void **) &result_buf, 256) != UT_OK );
152  | 
153  |   sprintf(result_buf, ACL_HEADER, "ip",
154  | 	  "maxbonus", "maxdenials", "maxpublic", "ban", "trustpass" );
155  | 
156  |   return result_buf;
157  | }
158  | 
159  | 
160  | /* AC_acl_to_string() */
161  | /*++++++++++++++++++++++++++++++++++++++
162  |   Show an access control list structure
163  | 
164  |   More:
165  |   +html+ <PRE>
166  |   Authors:
167  |         marek
168  |   +html+ </PRE><DL COMPACT>
169  |   +html+ <DT>Online References:
170  |   +html+ </UL></DL>
171  | 
172  |   ++++++++++++++++++++++++++++++++++++++*/
173  | char *AC_acl_to_string(GList *leafptr)
174  | {
175  |   char *result_buf;
176  |   acl_st *a = leafptr->data;
177  | 
178  |   if( wr_malloc( (void **) &result_buf, 256) != UT_OK ) {
179  |       /* do many bad things...*/
180  |       return NULL;
181  |     }
182  |   
183  |   if( a != NULL ) {
184  |     sprintf(result_buf, ACL_FORMAT,
185  |             a->maxbonus,
186  |             a->maxdenials,
187  | 	    a->maxpublic,
188  |             a->deny,     
189  |             a->trustpass
190  |             );
191  |   }
192  |   else {
193  |     strcpy(result_buf, "DATA MISSING\n");
194  |   }
195  |   
196  |   return result_buf;
197  | } /* AC_acl_to_string() */
198  | 
199  | er_ret_t
200  | AC_findexless_acl_l(ip_prefix_t *prefix, acl_st *store_acl)
201  | {
202  |   GList       *datlist=NULL;
203  |   er_ret_t    ret_err;
204  |   rx_datref_t *datref;  
205  | 
206  |   if( (ret_err = RX_bin_search(RX_SRCH_EXLESS, 0, 0, act_acl, 
207  |                                prefix, &datlist, RX_ANS_ALL)
208  |        ) != RX_OK   ||  g_list_length(datlist) == 0 ) {
209  |     /* acl tree is not configured at all ! There always must be a
210  |        catch-all record with defaults */
211  |     die;
212  |   }
213  | 
214  |   datref = (rx_datref_t *)g_list_nth_data(datlist,0);
215  | 
216  |   *store_acl = * ((acl_st *)  datref->leafptr);
217  | 
218  |   wr_clear_list( &datlist );
219  | 
220  |   /* XXX checking tree consistency */
221  |   {
222  |     rx_treecheck_t errorfound;
223  |     er_ret_t err;
224  |     if( (err=RX_treecheck(act_acl, 1, &errorfound)) != RX_OK ) {
225  |       fprintf(stderr, "Nope! %d returned \n", err);
226  |       die;
227  |     }
228  |   }  
229  | 
230  |   return ret_err;
231  | }
232  | 
233  | er_ret_t
234  | AC_findcreate_acl_l(ip_prefix_t *prefix, acl_st **store_acl)
235  | {
236  |   GList       *datlist=NULL;
237  |   er_ret_t    ret_err;
238  |   acl_st      *newacl;
239  |   acl_st acl_copy;    
240  | 
241  |   if( NOERR(ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_acl, 
242  | 				    prefix, &datlist, RX_ANS_ALL)
243  | 	    )) {
244  |     
245  |     switch( g_list_length(datlist)) {
246  |     case 0:
247  |       dieif( wr_calloc((void **)&newacl, 1, sizeof(acl_st)) != UT_OK );
248  |       
249  |       /* make the new one inherit all parameters after the old one */
250  |       
251  |       AC_findexless_acl_l(prefix, &acl_copy);
252  | 
253  |       *newacl = acl_copy;
254  |       
255  |       /* link in */
256  |       RX_rt_node(RX_OPER_CRE, prefix, act_acl, (rx_dataleaf_t *)newacl);
257  |       break;
258  |     case 1:
259  |       {
260  | 	/* Uh-oh, the guy is already known ! (or special, in any case) */ 
261  | 	rx_datref_t *datref = (rx_datref_t *)g_list_nth_data(datlist,0);
262  | 	newacl = (acl_st *) datref->leafptr;
263  |       }
264  |       break;
265  |     default:
266  |       die;
267  |     }
268  |   } 
269  | 
270  |   /* free search results */
271  |   wr_clear_list( &datlist );
272  |   
273  |   /* store */
274  |   *store_acl = newacl;
275  |   return ret_err;
276  | }
277  | 
278  | /* 
279  |    finds exact prefix or creates area initialised to zeros + sets ptr to it.
280  | 
281  |    returns error code
282  | 
283  |    operates on the given tree
284  |    assumes tree locked 
285  |  */
286  | er_ret_t 
287  | AC_findcreate_account_l(rx_tree_t *tree, ip_prefix_t *prefix, 
288  | 			acc_st **acc_store)
289  | {
290  |   GList       *datlist=NULL;
291  |   er_ret_t    ret_err;
292  |   acc_st      *recacc;
293  | 
294  |   if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, tree, 
295  |                                prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
296  |     switch( g_list_length(datlist) ) {
297  |     case 0:
298  |       /* need to create a new accounting record */
299  |       if( (ret_err = wr_malloc( (void **)& recacc, sizeof(acc_st))) == UT_OK ) {
300  |         /*  counters = init to zeros */
301  |         memset( recacc, 0, sizeof(acc_st));
302  |         
303  |         /* attach. The recacc is to be treated as a dataleaf
304  |            (must use lower levels than RX_asc_*)
305  |         */
306  |         ret_err = RX_rt_node( RX_OPER_CRE, prefix, 
307  |                                act_runtime, (rx_dataleaf_t *)recacc );
308  |       }
309  |       break;
310  |     case 1:
311  |       {
312  |         rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
313  |         
314  |         /* OK, there is a record already */
315  |         recacc = (acc_st *) datref->leafptr;
316  |         
317  |       }
318  |       break;
319  |     default: die; /* there shouldn't be more than 1 entry per IP */
320  |     }
321  |   }
322  |     
323  |   wr_clear_list( &datlist );
324  |   
325  |   *acc_store = recacc;
326  |   
327  |   return ret_err;
328  | }
329  | 
330  | 
331  | /* AC_fetch_acc() */
332  | /*++++++++++++++++++++++++++++++++++++++
333  |   Finds the runtime accounting record for this IP, 
334  |   stores a copy of it in acc_store. 
335  | 
336  |   If not found, then it is created and initialised to zeros in findcreate()
337  | 
338  |   ++++++++++++++++++++++++++++++++++++++*/
339  | er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store)
340  | {
341  |   er_ret_t ret_err;
342  |   ip_prefix_t prefix;
343  |   acc_st *ac_ptr;
344  | 
345  |   prefix.ip = *addr;
346  |   prefix.bits = IP_sizebits(addr->space);
347  | 
348  |   TH_acquire_read_lock( &(act_runtime->rwlock) );
349  |   
350  |   ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
351  |   *acc_store = *ac_ptr;
352  | 
353  |   TH_release_read_lock( &(act_runtime->rwlock) );
354  | 
355  |   return ret_err;
356  | }/* AC_fetch_acc() */
357  | 
358  | /* AC_check_acl() */
359  | /*++++++++++++++++++++++++++++++++++++++
360  |   
361  |   AC_check_acl:
362  |   
363  |   search for this ip or less specific record in the access control tree
364  |   
365  |   if( bonus in combined runtime+connection accountings > max_bonus in acl)
366  |             set denial in the acl for this ip (create if needed)
367  |   if( combined denialcounter > max_denials in acl)
368  |             set the permanent ban in acl; save in SQL too
369  |   calculate credit if pointer provided
370  |   save the access record (ip if created or found/prefix otherwise) 
371  |             at *acl_store if provided
372  | 
373  |   any of the args except address can be NULL
374  | 
375  |   ++++++++++++++++++++++++++++++++++++++*/
376  | er_ret_t AC_check_acl( ip_addr_t *addr, 
377  |                        acc_st *credit_acc,
378  |                        acl_st *acl_store
379  |                        )
380  | {
381  |   ip_prefix_t prefix;
382  |   er_ret_t    ret_err;
383  |   acl_st      acl_record;
384  |   acc_st      run_acc;
385  | 
386  |   AC_fetch_acc( addr, &run_acc );
387  |   
388  |   prefix.ip = *addr;
389  |   prefix.bits = IP_sizebits(addr->space);
390  |   
391  |   /* lock the tree accordingly */
392  |   TH_acquire_read_lock( &(act_acl->rwlock) );  
393  |   
394  |   /* find an applicable record */
395  |   AC_findexless_acl_l(&prefix, &acl_record);
396  |   
397  |   /* calculate the credit if pointer given */
398  |   if( credit_acc ) {
399  |     memset( credit_acc, 0, sizeof(acc_st));
400  |     credit_acc->public_objects =                       /* -1 == unlimited */
401  |       acl_record.maxpublic - run_acc.public_objects;
402  |     credit_acc->private_objects =
403  |       acl_record.maxbonus - run_acc.private_bonus;
404  |   }
405  | 
406  |   /* copy the acl record if asked for it*/
407  |   if( acl_store ) {
408  |     *acl_store =  acl_record;
409  |   }
410  | 
411  |   /* release lock */
412  |   TH_release_read_lock( &(act_acl->rwlock) );
413  |   
414  |  
415  |   return ret_err;
416  | }
417  | 
418  | void AC_acc_addup(acc_st *a, acc_st *b, int minus)
419  | {
420  |   int mul = minus ? -1 : 1;
421  |   
422  |   /* add all counters from b to those in a */
423  |   a->connections     +=  mul * b->connections;   
424  |   a->addrpasses      +=  mul * b->addrpasses;  
425  |  
426  |   a->denials         +=  mul * b->denials;      
427  |   a->queries         +=  mul * b->queries;       
428  |   a->public_objects  +=  mul * b->public_objects;
429  |   a->private_objects +=  mul * b->private_objects;
430  |   a->private_bonus   +=  mul * b->private_bonus;
431  | }
432  | 
433  | 
434  | /* 
435  |    performs the commit on an accounting tree (locks them first)
436  |    stores a copy of the accounting record at rec_store
437  | */
438  | er_ret_t 
439  | AC_commit_credit(rx_tree_t *tree, ip_prefix_t *prefix, 
440  | 		 acc_st *acc_conn, acc_st *rec_store )
441  | {
442  |   acc_st      *accountrec;
443  |   er_ret_t    ret_err;
444  | 
445  | 
446  |   acc_conn->private_bonus = acc_conn->private_objects;
447  | 
448  |   TH_acquire_write_lock( &(tree->rwlock) );
449  | 
450  |   AC_findcreate_account_l(act_runtime, prefix, &accountrec);
451  |   
452  |   AC_acc_addup(accountrec, acc_conn, ACC_PLUS);
453  |   /* XXX checking tree consistency */
454  | 
455  |   {
456  |     rx_treecheck_t errorfound;
457  |     er_ret_t err;
458  |     if( (err=RX_treecheck(tree, 1, &errorfound)) != RX_OK ) {
459  |       fprintf(stderr, "Nope! %d returned \n", err);
460  |       die;
461  |     }
462  |   }  
463  | 
464  |   TH_release_write_lock( &(tree->rwlock) );
465  |  
466  |   *rec_store = *accountrec;
467  |   
468  |   return ret_err;
469  | }
470  | 
471  | /* insert/replace a record in the database */
472  | er_ret_t 
473  | AC_acl_sql(ip_prefix_t *prefix, acl_st *newacl, char *newcomment )
474  | {  
475  |   SQ_connection_t *sql_connection = NULL;
476  |   SQ_result_set_t *result;
477  |   SQ_row_t *row;
478  |   char *oldcomment;
479  |   char *query;
480  |   char querybuf[256];
481  |   
482  |   sql_connection = SQ_get_connection(CO_get_host(),
483  | 				     CO_get_database_port(),
484  | 				     "RIPADMIN",
485  | 				     CO_get_user(), 
486  | 				     CO_get_password() );
487  |   
488  |   /* get the old entry, extend it */
489  |   sprintf(querybuf, "SELECT comment FROM acl WHERE "
490  | 	  "prefix = %u AND prefix_length = %d", 
491  | 	  prefix->ip.words[0],
492  | 	  prefix->bits);
493  |   dieif( SQ_execute_query(sql_connection, querybuf, &result) == -1 );
494  |   
495  |   if( SQ_num_rows(result) == 1 ) {
496  |     dieif( (row = SQ_row_next(result)) == NULL);
497  |     oldcomment = SQ_get_column_string(result, row, 0);
498  |   }
499  |   else {
500  |     oldcomment = "";
501  |   }
502  | 
503  |   SQ_free_result(result);
504  |   
505  |   /* must hold the thing below (replace..blah blah blah) + text */
506  |   dieif( wr_malloc((void **)&query, 
507  | 		   strlen(oldcomment) + strlen(newcomment) + 256) != UT_OK );
508  |   
509  |   /* compose new entry and insert it */
510  |   sprintf(query, "REPLACE INTO acl VALUES(%u, %d, %d, %d, %d, %d, %d,"
511  | 	  "\"%s%s%s\")",
512  | 	  prefix->ip.words[0],
513  | 	  prefix->bits,
514  | 	  newacl->maxbonus,
515  | 	  newacl->maxpublic,
516  | 	  newacl->maxdenials,
517  | 	  newacl->deny,
518  | 	  newacl->trustpass,
519  | 	  oldcomment, 
520  | 	  strlen(oldcomment) > 0 ? "\n" : "",
521  | 	  newcomment
522  | 	  );
523  |   
524  |   SQ_execute_query(sql_connection, query, NULL);
525  |   SQ_close_connection(sql_connection);
526  |   
527  |   wr_free(query);
528  |   
529  |   return AC_OK;
530  | 
531  | }
532  | 
533  | 
534  | /* re/sets the permanent ban flag both in the acl tree in memory
535  |    and the sql table. The "text" is appended to the comment 
536  |    in the sql record (the expected cases are
537  |    - "automatic" in case the limit is exceeded and ban is set by s/w
538  |    - "manual"    in case it is (un)set from the config iface
539  | */
540  | er_ret_t
541  | AC_ban_set(ip_prefix_t *prefix, char *text, int denyflag)
542  | {
543  |   acl_st *treeacl;
544  |   char newcomment[256];
545  |   er_ret_t ret_err;
546  |   time_t  clock;
547  |   char timebuf[26];
548  |   
549  |   time(&clock);
550  |   ctime_r(&clock, timebuf);
551  | 
552  |   sprintf(newcomment,"%s permanent ban set to %d at %s", text, 
553  | 	  denyflag, timebuf);
554  |     
555  |   TH_acquire_write_lock( &(act_acl->rwlock) );  
556  | 
557  |   /* find a record in the tree */  
558  |   if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
559  |     treeacl->deny = denyflag;
560  |     ret_err = AC_acl_sql( prefix, treeacl, newcomment );
561  |   }
562  |   TH_release_write_lock( &(act_acl->rwlock) );
563  | 
564  |   return ret_err;
565  | }
566  | 
567  | er_ret_t
568  | AC_asc_ban_set(char *addrstr, char *text, int denyflag)
569  | {
570  |   er_ret_t ret_err;
571  |   GList *preflist = NULL;
572  |   ip_keytype_t key_type;
573  | 
574  |   if( (ret_err = IP_smart_conv(addrstr, 0, 0,
575  | 			       &preflist, IP_PLAIN, &key_type)) != IP_OK ) {
576  |     return ret_err;
577  |   }
578  |   
579  |   /* allow only one prefix */
580  |   /* The argument can be even a range, but must decompose into one prefix */
581  |   if(  NOERR(ret_err) && g_list_length( preflist ) != 1 ) {
582  |     ret_err = AC_INVARG;
583  |   }
584  |   
585  |   if( NOERR(ret_err) ) {
586  |     ret_err = AC_ban_set( (g_list_first(preflist)->data), text, denyflag);
587  |   }
588  | 
589  |   wr_clear_list( &preflist );
590  |   
591  |   return ret_err;
592  | }
593  | 
594  | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn, acl_st *acl_copy) {
595  |   /* 
596  |         lock runtime + minute accounting trees 
597  | 	-----------------------  XXX runtime only for the moment
598  |            find or create entries, 
599  |            increase accounting values by the values from passed acc
600  |            check values against acl, see if permanent ban applies
601  | 
602  |            reset the connection acc
603  |         unlock accounting trees
604  | 
605  |         if permanent ban - set it! :
606  |             lock acl
607  |             find/create IP in memory
608  |             set ban
609  |             find/create IP in SQL
610  |             copy old values (if any), set ban, append comment
611  |             unlock acl
612  |   */
613  |   
614  |   acc_st   account;
615  |   er_ret_t ret_err;
616  |   ip_prefix_t prefix;
617  | 
618  |   prefix.ip = *addr;
619  |   prefix.bits = IP_sizebits(addr->space);
620  |   
621  |   ret_err = AC_commit_credit(act_runtime, &prefix, acc_conn, &account);
622  |   /* XXX add more trees here */
623  |   
624  |   memset(acc_conn,0, sizeof(acc_st));
625  | 
626  |   /* set permanent ban if deserved  and if not set yet */
627  |   if( account.denials > acl_copy->maxdenials 
628  |       && acl_copy->deny == 0 
629  |       && NOERR(ret_err) ) {
630  |     
631  |     ret_err = AC_ban_set(&prefix, "Automatic", 1);
632  |   }
633  | 
634  |   return ret_err;
635  | }
636  | 
637  | er_ret_t AC_decay_hook(rx_node_t *node, int level, int nodecounter, void *con) {
638  |   acc_st *a = node->leaves_ptr->data;
639  |   
640  |   a->private_bonus *= 0.95;
641  | 
642  |   return RX_OK;
643  | } /* AC_decay_hook() */
644  | 
645  | 
646  | 
647  | /* 
648  |      This should be run as a detached thread.
649  | */
650  | er_ret_t AC_decay(void) {
651  |   er_ret_t ret_err;
652  | 
653  |   
654  |   while(CO_get_do_server()) {
655  | 
656  |     TH_acquire_write_lock( &(act_runtime->rwlock) );
657  | 
658  |     if( act_runtime->top_ptr != NULL ) {
659  |        rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
660  |                          RX_WALK_SKPGLU,  /* skip glue nodes */
661  |                          255, 0, 0, NULL, &ret_err);
662  |     }
663  | 
664  |     /* it should also be as smart as to delete nodes that have reached 
665  |        zero, otherwise the whole of memory will be filled.
666  |        Next release :-)
667  |     */
668  | 
669  |     TH_release_write_lock( &(act_runtime->rwlock) );
670  | 
671  |     printf("AC: decaying access tree. (Every %d seconds)\n", AC_DECAY_TIME);
672  | 
673  |     SV_sleep(LOCK_SHTDOWN, AC_DECAY_TIME);
674  |   }
675  | 
676  |   return ret_err;
677  | } /* AC_decay() */
678  | 
679  | er_ret_t AC_acc_load(void)
680  | {
681  |   SQ_connection_t *con=NULL;
682  |   SQ_result_set_t *result;
683  |   SQ_row_t *row;
684  |   er_ret_t ret_err = RX_OK;
685  | 
686  |   if( (con = SQ_get_connection(CO_get_host(), CO_get_database_port(), 
687  |                         "RIPADMIN", CO_get_user(), CO_get_password() )
688  |        ) == NULL ) {
689  |     fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
690  |     die;
691  |   }
692  |   
693  |   if( SQ_execute_query(con, "SELECT * FROM acl", &result) == -1 ) {
694  |       fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
695  |       die;
696  |   }
697  |   
698  |   TH_acquire_write_lock( &(act_acl->rwlock) );
699  | 
700  |   while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
701  |     ip_prefix_t mypref;
702  |     acl_st *newacl;
703  |     char *col[7];
704  |     unsigned myint;
705  |     int i;
706  | 
707  |     memset(&mypref, 0, sizeof(ip_prefix_t));
708  |     mypref.ip.space = IP_V4;
709  |     
710  |     if( (ret_err = wr_malloc( (void **)& newacl, sizeof(acl_st))
711  |          ) == UT_OK ) {
712  | 
713  |       for(i=0; i<7; i++) {
714  |         if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
715  |           die;
716  |         }
717  |       }
718  |       
719  |       /* prefix ip */
720  |       if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
721  |       
722  |       /* prefix length */
723  |       if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
724  |       
725  |       /* acl contents */
726  |       if( sscanf(col[2], "%u",  & (newacl->maxbonus)   ) < 1 ) { die; }
727  |       if( sscanf(col[3], "%u",  & (newacl->maxpublic)   ) < 1 ) { die; }
728  |       if( sscanf(col[4], "%hd", & (newacl->maxdenials) ) < 1 ) { die; }
729  |       
730  |       /* these are chars therefore cannot read directly */
731  |       if( sscanf(col[5], "%u", &myint              ) < 1 ) { die; }
732  |       else {
733  |         newacl->deny = myint;
734  |       }
735  |       if( sscanf(col[6], "%u", &myint  ) < 1 ) { die; }
736  |       else {
737  |         newacl->trustpass = myint;
738  |       }
739  |       
740  |       /* free space */
741  |       for(i=0; i<6; i++) {
742  | 	  wr_free(col[i]);
743  |       }
744  |       
745  |       
746  |       /* now add to the tree */
747  |       
748  |       ret_err = RX_rt_node( RX_OPER_CRE, &mypref, 
749  |                              act_acl, (rx_dataleaf_t *) newacl );
750  |     }
751  |   } /* while row */
752  | 
753  |   TH_release_write_lock( &(act_acl->rwlock) );
754  | 
755  |   SQ_free_result(result);
756  |   /* Close connection */
757  |   SQ_close_connection(con);
758  | 
759  |   
760  | 
761  |   return ret_err;
762  | }
763  | 
764  | er_ret_t AC_build(void) 
765  | {
766  |   /* create trees */
767  |   if (      RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
768  | 			RX_SUB_NONE, &act_runtime) != RX_OK
769  | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
770  | 			RX_SUB_NONE, &act_hour) != RX_OK
771  | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
772  | 			RX_SUB_NONE, &act_minute) != RX_OK
773  | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY, 
774  | 			RX_SUB_NONE, &act_acl) != RX_OK
775  |          )
776  |     die; /*can be changed to an error and handled ... some day */
777  | 
778  |   return RX_OK;
779  | }
780  | 
781  | er_ret_t AC_rxwalkhook_print(rx_node_t *node, 
782  |                              int level, int nodecounter, 
783  |                              void *con)
784  | {
785  |   char adstr[IP_ADDRSTR_MAX];
786  |   char line[1024];
787  |   char *dat;
788  |   
789  |   
790  |     if( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK ) {
791  |       die; /* program error. */
792  |     }
793  |     
794  |     sprintf(line, "%-20s %s\n", adstr, 
795  |             dat=AC_to_string( node->leaves_ptr ));
796  |     wr_free(dat);
797  |     
798  |     SK_cd_puts((sk_conn_st *)con, line);
799  |     return RX_OK;
800  | }
801  | 
802  | er_ret_t AC_rxwalkhook_print_acl(rx_node_t *node, 
803  |                              int level, int nodecounter, 
804  |                              void *con)
805  | {
806  |   char prefstr[IP_PREFSTR_MAX];
807  |   char line[1024];
808  |   char *dat;
809  |   
810  |   
811  |     if( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK ) {
812  |       die; /* program error. */
813  |     }
814  |     
815  |     sprintf(line, "%-20s %s\n", prefstr, 
816  |             dat=AC_acl_to_string( node->leaves_ptr ));
817  |     wr_free(dat);
818  |     
819  |     SK_cd_puts((sk_conn_st *)con, line);
820  |     return RX_OK;
821  | }
822  |