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