1 | /*************************************** 2 | $Revision: 1.9 $ 3 | 4 | Radix payload (rp) - user level functions for storing data in radix trees 5 | 6 | rp_search = search the loaded radix trees using an ascii key 7 | 8 | Motto: "And all that for inetnums..." 9 | 10 | Status: NOT REVIEWED, TESTED 11 | 12 | Design and implementation by: Marek Bukowy 13 | 14 | ******************/ /****************** 15 | Copyright (c) 1999 RIPE NCC 16 | 17 | All Rights Reserved 18 | 19 | Permission to use, copy, modify, and distribute this software and its 20 | documentation for any purpose and without fee is hereby granted, 21 | provided that the above copyright notice appear in all copies and that 22 | both that copyright notice and this permission notice appear in 23 | supporting documentation, and that the name of the author not be 24 | used in advertising or publicity pertaining to distribution of the 25 | software without specific, written prior permission. 26 | 27 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 28 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 29 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 30 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 31 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 32 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 33 | ***************************************/ 34 | 35 | 36 | #include <rp.h> 37 | 38 | static 39 | void 40 | rp_exclude_datlink(GList **datlist, GList *element) 41 | { 42 | /* remove element from list(becomes a self-consistent list) */ 43 | *datlist = g_list_remove_link(*datlist, element); 44 | 45 | /* free it and the payload */ 46 | wr_clear_list( &element ); 47 | } 48 | 49 | 50 | /**************************************************************************/ 51 | /*+++++++++++ 52 | helper: 53 | this routine goes through the list of prefixes and performs a bin_search 54 | on each of them; attaches the results to datlist. 55 | +++++++++++*/ 56 | static 57 | er_ret_t 58 | rp_preflist_search ( 59 | rx_srch_mt search_mode, 60 | int par_a, 61 | int par_b, 62 | rx_tree_t *mytree, 63 | GList **preflist, 64 | GList **datlist 65 | ) 66 | 67 | { 68 | char prefstr[IP_PREFSTR_MAX]; 69 | GList *qitem; 70 | ip_prefix_t *querypref; 71 | er_ret_t err; 72 | 73 | for( qitem = g_list_first(*preflist); 74 | qitem != NULL; 75 | qitem = g_list_next(qitem)) { 76 | 77 | querypref = qitem->data; 78 | 79 | if( IP_pref_b2a( querypref, prefstr, IP_PREFSTR_MAX) != IP_OK ) { 80 | die; 81 | } 82 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 83 | "rx_preflist_search: mode %d (%s) (par %d) for %s", 84 | search_mode, RX_text_srch_mode(search_mode), par_a, prefstr); 85 | 86 | if (mytree->num_nodes > 0) { 87 | err = RX_bin_search( search_mode, par_a, par_b, mytree, querypref, 88 | datlist, RX_ANS_ALL); 89 | if( err != RX_OK ) { 90 | return err; 91 | } 92 | } 93 | } 94 | 95 | return RX_OK; 96 | } 97 | 98 | /*++++ 99 | this is a helper: goes through a datlist and returns the smallest 100 | size of a range 101 | 102 | works for IPv4 only 103 | +++*/ 104 | static 105 | ip_rangesize_t 106 | rp_find_smallest_span( GList *datlist ) { 107 | ip_rangesize_t min_span, span; 108 | GList *ditem; 109 | 110 | min_span = 0xffffffff; /* XXX IPv4 only!!!!*/ 111 | 112 | /* go through the list and find the shortest range. */ 113 | for(ditem = g_list_first(datlist); 114 | ditem != NULL; 115 | ditem = g_list_next(ditem)) { 116 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 117 | 118 | span = IP_rang_span( & refptr->leafptr->iprange); 119 | 120 | if( span < min_span ) { 121 | min_span = span; 122 | } 123 | } 124 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 125 | "rp_find_smallest_span: minimal span is %d", min_span); 126 | 127 | return min_span; 128 | } 129 | 130 | 131 | 132 | /* helper for the inetnum/exless search - for this one a hash of pairs 133 | (leafptr,occurences) must be maintained. 134 | 135 | This routine increments the counter for a leafptr, creating a new 136 | pair if this leafptr was not referenced before. 137 | 138 | */ 139 | static 140 | int rp_leaf_occ_inc(GHashTable *hash, rx_dataleaf_t *leafptr) 141 | { 142 | /* one little trick: store the number of occurences 143 | as cast (void *) */ 144 | int val; 145 | 146 | val = (int) g_hash_table_lookup(hash, leafptr); 147 | /* 0 if it's not known yet. anyway: put it in the hash (value==key) */ 148 | 149 | g_hash_table_insert(hash, leafptr, (void *) ++val); 150 | 151 | return val; 152 | } 153 | 154 | /* exclude exact match - not to be merged with preselction :-( */ 155 | static void 156 | rp_exclude_exact_match( GList **datlist, ip_range_t *testrang) 157 | { 158 | GList *ditem, *newitem; 159 | 160 | ditem = g_list_first(*datlist); 161 | 162 | while( ditem != NULL ) { 163 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 164 | 165 | newitem = g_list_next(ditem); 166 | 167 | if( memcmp( & refptr->leafptr->iprange, 168 | testrang, sizeof(ip_range_t)) == 0 ) { 169 | rp_exclude_datlink(datlist, ditem); 170 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 171 | "process_datlist: discarded an exact match"); 172 | } 173 | ditem = newitem; 174 | } /* while */ 175 | } 176 | 177 | static int 178 | rp_find_longest_prefix(GList **datlist) 179 | { 180 | GList *ditem; 181 | int max_pref=0; 182 | 183 | for(ditem = g_list_first(*datlist); 184 | ditem != NULL; 185 | ditem = g_list_next(ditem)) { 186 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 187 | 188 | if( refptr->leafptr->preflen > max_pref ) { 189 | max_pref = refptr->leafptr->preflen; 190 | } 191 | } 192 | 193 | return max_pref; 194 | } 195 | /*+ rp_asc_process_datlist() - helper for 196 | 197 | 198 | /*+ rp_asc_process_datlist() - helper for RP_asc_search() 199 | 200 | fetches the copies of objects from the radix tree into datlist 201 | 202 | ASSUMES LOCKED TREE 203 | 204 | the behaviour for a default inetnum (range) query is: 205 | do an exact match; 206 | if it fails, do an exless match on the encompassing prefix 207 | for routes(prefixes): 208 | do an exless match 209 | 210 | So if it's the default search mode on an inetnum tree, 211 | and the key is a range, 212 | then an exact search is performed on one of the composing prefixes. 213 | 214 | Then the resulting data leaves are checked for exact matching with 215 | the range queried for. 216 | Any dataleaves that do not match are discarded, and if none are left, 217 | the procedure falls back to searching for the encompassing prefix. 218 | (calculated in the smart_conv routine). 219 | Add the dataleaf copies to the list of answers, 220 | taking span into account 221 | +*/ 222 | static 223 | er_ret_t 224 | rp_asc_process_datlist( 225 | rx_srch_mt search_mode, 226 | int par_a, 227 | rx_fam_t fam_id, 228 | int prefnumber, 229 | GList **datlist, 230 | ip_range_t *testrang, 231 | int *hits 232 | ) 233 | { 234 | ip_rangesize_t min_span=0, span; 235 | int max_pref = -1; 236 | GList *ditem, *newitem; 237 | GHashTable *lohash = g_hash_table_new(NULL, NULL); 238 | 239 | /* in MORE and LESS(1) search exact match must not be displayed */ 240 | if ( search_mode == RX_SRCH_MORE 241 | || ( search_mode == RX_SRCH_LESS && par_a == 1 ) ) { 242 | rp_exclude_exact_match(datlist, testrang); 243 | } 244 | 245 | /* Preselection moved to processing, only span calculation done here * 246 | * 247 | 248 | EXLESS and LESS(1) search: the smallest span must be found, 249 | but if the less spec node is not the same for all composing prefixes, 250 | it means it's not really this one. 251 | 252 | we check that by the number of references to this node is less than 253 | the number of composing prefixes 254 | 255 | We do the same for the less specific search - a node must be less 256 | specific to all prefixes. 257 | 258 | if the number of references is not enough, then return no hits, 259 | another try will be made, this time with one, encompassing prefix. 260 | */ 261 | 262 | if ( (search_mode == RX_SRCH_EXLESS ) 263 | || ( search_mode == RX_SRCH_LESS && par_a == 1 ) ) { 264 | /* span works only for IP_V4. We use it only for inetnums, 265 | although RT/v4 would work too */ 266 | if( testrang->begin.space == IP_V4 && 267 | fam_id == RX_FAM_IN ) { 268 | min_span = rp_find_smallest_span(*datlist); 269 | } 270 | else { 271 | /* in IPv6 and RT trees in general, we can obtain the same 272 | result by selecting the longest prefix */ 273 | max_pref = rp_find_longest_prefix(datlist); 274 | } 275 | } 276 | 277 | /* Process the dataleaf copies and add to the list of answers. */ 278 | ditem = g_list_first(*datlist); 279 | while(ditem != NULL) { 280 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 281 | int exclude = 0; 282 | 283 | if(search_mode == RX_SRCH_EXLESS || search_mode == RX_SRCH_LESS ) { 284 | 285 | /* min_span defined <=> EXLESS or LESS(1) search of INETNUMS: 286 | the smallest span must be returned */ 287 | if( !exclude && min_span != 0 288 | && (span = IP_rang_span( &refptr->leafptr->iprange))!=min_span) { 289 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 290 | "process_datlist: (EX)LESS: discarded object with span %d", span); 291 | exclude = 1; 292 | } 293 | /* max_pref defined <=> EXLESS search of INETNUMS or LESS(1) of RT: 294 | */ 295 | if( !exclude && max_pref >= 0 296 | && refptr->leafptr->preflen < max_pref ) { 297 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 298 | "process_datlist: (EX)LESS: discarded object with preflen %d", 299 | refptr->leafptr->preflen); 300 | exclude = 1; 301 | } 302 | 303 | /* number of occurences */ 304 | /* XXX this will go when the old algorithm goes */ 305 | if( !exclude 306 | && prefnumber > 1 ) { /* do not check if all will be approved */ 307 | 308 | if( rp_leaf_occ_inc(lohash, refptr->leafptr) < prefnumber ) { 309 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 310 | "process_datlist: (EX)LESS: leafptr %x not enough",refptr->leafptr); 311 | exclude = 1; 312 | } 313 | else { 314 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 315 | "process_datlist: (EX)LESS: leafptr %x GOOD enough",refptr->leafptr); 316 | } 317 | } 318 | } 319 | else if( search_mode == RX_SRCH_EXACT ) { 320 | /* EXACT search - discard if the range does not match */ 321 | if( memcmp( & refptr->leafptr->iprange, 322 | testrang, sizeof(ip_range_t)) != 0) { 323 | 324 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 325 | "process_datlist: EXACT; discarded a mismatch"); 326 | exclude = 1; 327 | } /* EXACT match */ 328 | } 329 | else if( search_mode == RX_SRCH_MORE ) { 330 | /* MORE: exclude if not fully contained in the search term */ 331 | if( ! (IP_addr_in_rang(&refptr->leafptr->iprange.begin, testrang ) 332 | && IP_addr_in_rang(&refptr->leafptr->iprange.end, testrang ))) { 333 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 334 | "process_datlist: MORE; discarded a not-fully contained one"); 335 | exclude = 1; 336 | } 337 | } 338 | 339 | 340 | /* get next item now, before the current gets deleted */ 341 | newitem = g_list_next(ditem); 342 | if( exclude ) { 343 | /* get rid of it */ 344 | rp_exclude_datlink(datlist, ditem); 345 | } 346 | else { 347 | /* OK, so we ACCEPT these results*/ 348 | /* uniqueness ensured in copy_results */ 349 | (*hits)++; 350 | } 351 | ditem = newitem; 352 | } /* while ditem */ 353 | 354 | /* wr_clear_list(&lolist); */ 355 | g_hash_table_destroy(lohash); 356 | return RX_OK; 357 | } 358 | 359 | /* 360 | rp_mod_preflist() is a helper function for rp_asc_search(). 361 | 362 | modifies the list of prefixes to search for, 363 | 364 | special treatment for inetnum/exact: 365 | + a range that is equivalent to the search key (which may be a prefix) 366 | is made, to be used later for comparisons 367 | 368 | special treatment for inetnum/exless/composed: 369 | + the first pass mode is set to exact (otherwise to search_mode) 370 | 371 | a few optimisations are made: 372 | + for a route/composed_range/exact : the search is nuked 373 | + for an inetnum/composed_range/(exless|exact) : the list is truncated 374 | to one prefix, because in an exact search, it must be there anyway, 375 | and for the exless, the smallest encompassing one must match 376 | 377 | 378 | */ 379 | 380 | static 381 | er_ret_t 382 | rp_mod_preflist( 383 | rx_srch_mt search_mode, 384 | char *key, 385 | ip_keytype_t key_type, 386 | rx_fam_t fam_id, 387 | GList **preflist, 388 | ip_range_t *testrang, 389 | rx_srch_mt *first_pass_mode 390 | ) 391 | { 392 | int prefcount; 393 | 394 | prefcount = g_list_length(*preflist); 395 | 396 | /* EXACT search of a route tree for a composed range makes no sense */ 397 | if( fam_id == RX_FAM_RT && search_mode == RX_SRCH_EXACT 398 | && key_type == IPK_RANGE && prefcount > 1 ) { 399 | /* abort search - i.e. clear the preflist*/ 400 | 401 | wr_clear_list( preflist); 402 | 403 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 404 | "rp_mod_preflist: route/exact/composed - preflist cleared"); 405 | } 406 | 407 | /*+ inetnum / exact|exless specific : 408 | optimise: (composed range) 409 | 410 | perform a separate first pass, with just one exact search on one of 411 | the composing prefixes - the object must be found if it's in the 412 | database. 413 | 414 | So a little cheat: remove all but one prefixes from preflist 415 | and force a different search mode 416 | +*/ 417 | if( fam_id == RX_FAM_IN 418 | && (search_mode == RX_SRCH_EXLESS || search_mode == RX_SRCH_EXACT) 419 | && key_type == IPK_RANGE && prefcount > 1 ) { 420 | 421 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 422 | "rp_mod_preflist: inet/ex***/composed - first pass EXACT forced"); 423 | 424 | *first_pass_mode = RX_SRCH_EXACT; 425 | } /* inetnum / exact|exless specific */ 426 | 427 | /* exact: set range so a comparison can be performed */ 428 | /* must succeed after smart_conv succeeded */ 429 | IP_smart_range(key, testrang, IP_EXPN, &key_type); 430 | 431 | return RX_OK; 432 | } 433 | /**************************************************************************/ 434 | 435 | /*+ appends the element pointed to by datref to finallist +*/ 436 | static 437 | er_ret_t 438 | rp_asc_append_datref(rx_datref_t *refptr, GList **finallist) 439 | { 440 | er_ret_t err; 441 | rx_datcpy_t *datcpy; 442 | void *dataptr; 443 | 444 | /* OK, so we ACCEPT this result. Copy it.*/ 445 | 446 | if( (err=wr_calloc( (void **)& datcpy, 1, sizeof(rx_datcpy_t))) != UT_OK) { 447 | return err; /* die;*/ 448 | } 449 | 450 | datcpy->leafcpy = *(refptr->leafptr); 451 | 452 | /* copy the immediate data too. Set the ptr.*/ 453 | 454 | if( (err=wr_calloc( (void **) & dataptr, 1, refptr->leafptr->data_len)) 455 | != UT_OK) { 456 | return err; /* die;*/ 457 | } 458 | memcpy(dataptr, refptr->leafptr->data_ptr, refptr->leafptr->data_len); 459 | 460 | datcpy->leafcpy.data_ptr = dataptr; 461 | 462 | *finallist = g_list_prepend(*finallist, datcpy); 463 | 464 | /* XXX this wouldn't work in access_control */ 465 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DATA, 466 | "rp_asc_append 'ed: %s", dataptr); 467 | 468 | return RX_OK; 469 | } 470 | 471 | /*+ goes through datlist (list of references "datref") and add copies of 472 | leaves referenced to the finallist 473 | 474 | maintains its own uniqhash which holds pointers to copied dataleaves. 475 | 476 | modifies: finallist 477 | 478 | returns: error from wr_malloc 479 | 480 | +*/ 481 | static 482 | er_ret_t 483 | rp_srch_copyresults(GList *datlist, 484 | GList **finallist, 485 | int maxcount) 486 | { 487 | er_ret_t err; 488 | GList *ditem, *uitem; 489 | GHashTable *uniqhash = g_hash_table_new(NULL, NULL); /* defaults */ 490 | int count = 0; 491 | 492 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, "srch_copyresults"); 493 | 494 | /* copy dataleaves pointed to by entries from the datlist 495 | only once (check uniqueness in the hash table) */ 496 | for(ditem = g_list_first(datlist); 497 | ditem != NULL; 498 | ditem = g_list_next(ditem)) { 499 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 500 | rx_dataleaf_t *ansptr = refptr->leafptr; 501 | 502 | /* search for every ansptr (dataleaf pointer) in uniqhash */ 503 | if( g_hash_table_lookup(uniqhash, ansptr) == NULL ) { 504 | 505 | /* it's not known yet. OK: put it in the hash (value==key) */ 506 | g_hash_table_insert(uniqhash, ansptr, ansptr); 507 | 508 | /* and copy the dataleaf */ 509 | if( !NOERR(err = rp_asc_append_datref(refptr, finallist)) ) { 510 | return err; 511 | } 512 | } 513 | 514 | /* check the limit on number of objects if defined ( >0) */ 515 | count++; 516 | if( maxcount > 0 && count > maxcount ) { 517 | break; 518 | } 519 | 520 | } /* foreach (datlist) */ 521 | 522 | g_hash_table_destroy(uniqhash); /* elements are still linked to through datlist */ 523 | 524 | return RP_OK; 525 | } 526 | 527 | static 528 | void 529 | rp_begend_preselection(GList **datlist, rx_fam_t fam_id, ip_range_t *testrang) 530 | { 531 | GList *ditem, *newitem; 532 | 533 | ditem = g_list_first(*datlist); 534 | 535 | while( ditem != NULL ) { 536 | rx_datref_t *refptr = (rx_datref_t *) (ditem->data); 537 | newitem = g_list_next(ditem); 538 | 539 | /* the test is indentical for route & inetnum trees */ 540 | if( IP_addr_in_rang(&testrang->end, &refptr->leafptr->iprange) == 0 ) { 541 | 542 | rp_exclude_datlink(datlist, ditem); 543 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, 544 | "process_datlist: discarded an uncovering leafptr %x", 545 | refptr->leafptr); 546 | 547 | } 548 | ditem = newitem; 549 | } /* while */ 550 | } 551 | 552 | /*+++++++++++++++ 553 | translates a query into a binary prefix (or prefixes, if range). 554 | for registry+space (or if they are zero, for all 555 | registries/spaces) 556 | finds tree 557 | calls RX_bin_search (returning node copies). 558 | will not put duplicate entries (composed inetnums). 559 | returns some sort of error code :-) 560 | 561 | Cuts the number of answers from RX_bin_search down to max_count, 562 | but since some of the answers may have been "normalized" in the 563 | underlying functions (multiple occurences removed), 564 | the result is _at_most_ max_count. 565 | 566 | appends to a given list of data blocks (not nodes!) 567 | 568 | The EXLESS search on inetnum tree should return the shortest range 569 | that was found, by means of comparing span (size) of the range. 570 | If there are more of size equal to the smallest one, they are also 571 | returned. 572 | 573 | returns RX_OK or a code from an underlying function 574 | ++++++++++++*/ 575 | er_ret_t 576 | RP_asc_search ( 577 | rx_srch_mt search_mode, 578 | int par_a, 579 | int par_b, 580 | char *key, /*+ search term: (string) prefix/range/IP +*/ 581 | rp_regid_t reg_id, 582 | rp_attr_t attr, /*+ extra tree id (within the same reg/spc/fam +*/ 583 | GList **finallist, /*+ answers go here, please +*/ 584 | int max_count /*+ max # of answers. RX_ALLANS == unlimited +*/ 585 | ) 586 | { 587 | GList *preflist = NULL; 588 | GList *datlist = NULL; 589 | 590 | er_ret_t err; 591 | ip_range_t testrang; 592 | int locked = 0; 593 | rx_srch_mt first_pass_mode = search_mode; 594 | ip_keytype_t key_type; 595 | ip_space_t spc_id; 596 | rx_fam_t fam_id = RP_attr2fam( attr ); 597 | rx_tree_t *mytree; 598 | int hits=0; 599 | int par_a_lyse; 600 | 601 | /* abort on error (but unlock the tree) */ 602 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN, 603 | "RP_asc_search: query %s : mode %d (%s) (par %d) for %s", 604 | DF_get_attribute_name(attr), 605 | search_mode, RX_text_srch_mode(search_mode), par_a, key); 606 | 607 | /* parse the key */ 608 | if( ( err = IP_smart_conv(key, 0, 0, 609 | &preflist, IP_EXPN, &key_type)) != IP_OK ) { 610 | /* XXX operational trouble (UT_*) or invalid key (IP_INVARG)*/ 611 | return err; 612 | } 613 | 614 | /* 1. find the tree */ 615 | if( NOERR(err) ) { 616 | spc_id = IP_pref_b2_space( g_list_first(preflist)->data ); 617 | err = RP_tree_get( &mytree, reg_id, spc_id, attr ); 618 | } 619 | 620 | if( ! NOERR(err) ) { 621 | return err; 622 | } 623 | 624 | /* 2. lock the tree */ 625 | TH_acquire_read_lock( &(mytree->rwlock) ); 626 | locked = 1; 627 | 628 | 629 | /* XXX what an awful hack!!! 630 | In LESS(1) lookup, we have to provide more data so that something 631 | remains after the exact match is discarded. 632 | */ 633 | par_a_lyse = (search_mode == RX_SRCH_LESS && par_a == 1 ) ? 255 : par_a; 634 | 635 | /* 0. check the list of prefixes to search for */ 636 | err = rp_mod_preflist(search_mode, key, key_type, fam_id, 637 | &preflist, &testrang, &first_pass_mode); 638 | 639 | /* a special first pass EXACT is needed for inetnums */ 640 | if( first_pass_mode != search_mode ) { 641 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN, 642 | "RP_asc_search: doing pass 0 with mode %d", first_pass_mode ); /* 3. do the first pass */ 643 | err = rp_preflist_search ( first_pass_mode, par_a, par_b, 644 | mytree, &preflist, &datlist); 645 | } 646 | if( NOERR(err) ) { 647 | /* 4. process the data pointers obtained from the search */ 648 | err = rp_asc_process_datlist( first_pass_mode, par_a, fam_id, 649 | g_list_length(preflist), &datlist, 650 | &testrang, &hits ); 651 | } 652 | 653 | if( hits == 0 ) { 654 | /* clear datlist from discarded elements */ 655 | wr_clear_list( &datlist ); 656 | /* reuse the preflist */ 657 | 658 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN, 659 | "rp_asc_search: doing pass 1 with mode %d", search_mode); 660 | /* 3. perform the real search on all prefixes the query was converted to */ 661 | err = rp_preflist_search ( search_mode, par_a_lyse, par_b, 662 | mytree, &preflist, &datlist); 663 | 664 | if( NOERR(err) ) { 665 | /* 4. process the data pointers obtained from the search */ 666 | err = rp_asc_process_datlist( search_mode, par_a, fam_id, 667 | g_list_length(preflist), &datlist, 668 | &testrang, &hits ); 669 | } 670 | } 671 | 672 | if( NOERR(err) ) { 673 | /* 5. an inetnum/composed/exless was forced to exact in the first go. 674 | So if the exact did not match yet, an encompassing prefix must 675 | be searched in exless mode */ 676 | if( hits == 0 ) { 677 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN, 678 | "rp_asc_search: doing pass 2 with mode %d", search_mode); 679 | 680 | /* clean old lists */ 681 | wr_clear_list( &preflist ); 682 | wr_clear_list( &datlist ); 683 | 684 | /* make a new prefix list with the encompassing prefix only */ 685 | dieif ( IP_smart_conv(key, 0, 1, 686 | &preflist, IP_EXPN, &key_type) != IP_OK); 687 | 688 | /* search again, this time with the real search_mode */ 689 | err = rp_preflist_search ( search_mode, par_a, par_b, 690 | mytree, &preflist, &datlist); 691 | 692 | if( err == RX_OK ) { 693 | /* process the data pointers obtained from the search */ 694 | err = rp_asc_process_datlist( search_mode, par_a, fam_id, 695 | g_list_length(preflist), &datlist, 696 | &testrang, &hits ); 697 | } 698 | } 699 | } 700 | 701 | if( NOERR(err) ) { 702 | err = rp_srch_copyresults(datlist, finallist, max_count); /* and uniq */ 703 | } 704 | 705 | if( locked ) { 706 | /* 100. unlock the tree */ 707 | TH_release_read_lock( &(mytree->rwlock) ); 708 | } 709 | 710 | /* clean up */ 711 | wr_clear_list( &preflist ); 712 | wr_clear_list( &datlist ); 713 | 714 | return err; 715 | } 716 | 717 | 718 | 719 | 720 | er_ret_t 721 | RP_new_asc_search ( 722 | rx_srch_mt search_mode, 723 | int par_a, 724 | int par_b, 725 | char *key, /*+ search term: (string) prefix/range/IP +*/ 726 | rp_regid_t reg_id, 727 | rp_attr_t attr, /*+ extra tree id (within the same reg/spc/fam +*/ 728 | GList **finallist, /*+ answers go here, please +*/ 729 | int max_count /*+ max # of answers. RX_ALLANS == unlimited +*/ 730 | ) 731 | { 732 | GList *preflist = NULL; 733 | GList *datlist = NULL; 734 | 735 | er_ret_t err; 736 | ip_range_t testrang; 737 | int locked = 0; 738 | ip_keytype_t key_type; 739 | ip_space_t spc_id; 740 | rx_fam_t fam_id = RP_attr2fam( attr ); 741 | rx_tree_t *mytree; 742 | int hits=0; 743 | unsigned begin,end; 744 | ip_prefix_t beginpref; 745 | 746 | 747 | /* abort on error (but unlock the tree) */ 748 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN, 749 | "RP_NEW_asc_search: query %s : mode %d (%s) (par %d) for %s", 750 | DF_get_attribute_name(attr), 751 | search_mode, RX_text_srch_mode(search_mode), par_a, key); 752 | 753 | 754 | /* parse the key into a prefix list */ 755 | if( ( err = IP_smart_conv(key, 0, 0, 756 | &preflist, IP_EXPN, &key_type)) != IP_OK ) { 757 | /* XXX operational trouble (UT_*) or invalid key (IP_INVARG)*/ 758 | return err; 759 | } 760 | 761 | /* set the test values */ 762 | IP_smart_range(key, &testrang, IP_EXPN, &key_type); 763 | 764 | /* find the tree */ 765 | if( NOERR(err) ) { 766 | spc_id = IP_pref_b2_space( g_list_first(preflist)->data ); 767 | if( ! NOERR(err = RP_tree_get( &mytree, reg_id, spc_id, attr ))) { 768 | return err; 769 | } 770 | } 771 | /* the point of no return: now we lock the tree. From here, even if errors 772 | occur, we still go through all procedure to unlock the tree at the end */ 773 | 774 | /* lock the tree */ 775 | TH_acquire_read_lock( &(mytree->rwlock) ); 776 | locked = 1; 777 | 778 | /* Collection: this procedure is used for some search_modes only */ 779 | if( search_mode == RX_SRCH_EXLESS 780 | || search_mode == RX_SRCH_LESS 781 | || search_mode == RX_SRCH_EXACT ) { 782 | 783 | /* 1. compose a /32(/128) prefix for beginning of range */ 784 | beginpref.ip = testrang.begin; 785 | beginpref.bits = IP_sizebits(spc_id); 786 | 787 | /* 2. dataleaves collection: look up the beginning prefix in LESS(255) mode */ 788 | if( NOERR(err) ) { 789 | err = RX_bin_search( RX_SRCH_LESS, 255, 0, mytree, &beginpref, 790 | &datlist, RX_ANS_ALL); 791 | } 792 | 793 | /* 3. preselection: exclude those that do not include end of range 794 | */ 795 | if( NOERR(err) ) { 796 | rp_begend_preselection(&datlist, fam_id, &testrang); 797 | } 798 | 799 | } /* if exless|less|exact */ 800 | else { 801 | /* MORE */ 802 | 803 | /* standard collection using the traditional method: 804 | repeat the search for all prefixes and join results */ 805 | 806 | if( NOERR(err) ) { 807 | err = rp_preflist_search ( search_mode, par_a, par_b, 808 | mytree, &preflist, &datlist); 809 | } 810 | } /* collection */ 811 | 812 | ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN, 813 | "RP_NEW_asc_search: collected %d references ", 814 | g_list_length(datlist)); 815 | 816 | 817 | /* 5. processing - using the same processing function */ 818 | if( NOERR(err) ) { 819 | err = rp_asc_process_datlist( search_mode, par_a, fam_id, 820 | 1, /* one occurence is enough */ 821 | &datlist, 822 | &testrang, &hits ); 823 | } 824 | 825 | /* 6. copy results */ 826 | if( NOERR(err) ) { 827 | err = rp_srch_copyresults(datlist, finallist, max_count); /* and uniq */ 828 | } 829 | 830 | if( locked ) { 831 | /* 100. unlock the tree */ 832 | TH_release_read_lock( &(mytree->rwlock) ); 833 | } 834 | 835 | /* clean up */ 836 | wr_clear_list( &preflist ); 837 | wr_clear_list( &datlist ); 838 | 839 | /* NOTE if error occured, finallist may be partly filled in. */ 840 | return err; 841 | } 842 |