Source File modules/rp/rp_search.c

    1    | /***************************************
    2    |   $Revision: 1.10 $
    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  |
    196  |
    197  | /*+ rp_asc_process_datlist() - helper for RP_asc_search()
    198  |
    199  |   fetches the copies of objects from the radix tree into datlist
    200  |
    201  |      ASSUMES LOCKED TREE
    202  |
    203  |      the behaviour for a default inetnum (range) query is:
    204  |        do an exact match;
    205  |        if it fails, do an exless match on the encompassing prefix
    206  |      for routes(prefixes):
    207  |        do an exless match
    208  |
    209  |      So if it's the default search mode on an inetnum tree,
    210  |      and the key is a range,
    211  |      then an exact search is performed on one of the composing prefixes.
    212  |
    213  |      Then the resulting data leaves are checked for exact matching with
    214  |      the range queried for.
    215  |      Any dataleaves that do not match are discarded, and if none are left,
    216  |      the procedure falls back to searching for the encompassing prefix.
    217  |      (calculated in the smart_conv routine).
    218  |      Add the dataleaf copies to the list of answers,
    219  |      taking span into account
    220  | +*/
    221  | static
    222  | er_ret_t
    223  | rp_asc_process_datlist(
    224  | 		       rx_srch_mt search_mode,
    225  | 		       int        par_a,
    226  | 		       rx_fam_t   fam_id,
    227  | 		       int        prefnumber,
    228  | 		       GList    **datlist,
    229  | 		       ip_range_t *testrang,
    230  | 		       int       *hits
    231  | 		       )
    232  | {
    233  |   ip_rangesize_t  min_span=0, span;
    234  |   int max_pref = -1;
    235  |   GList    *ditem, *newitem;
    236  |   GHashTable *lohash = g_hash_table_new(NULL, NULL);
    237  |
    238  |   /* in MORE and LESS(1) search exact match must not be displayed */
    239  |   if ( search_mode == RX_SRCH_MORE
    240  |        || ( search_mode == RX_SRCH_LESS && par_a == 1 ) ) {
    241  |     rp_exclude_exact_match(datlist, testrang);
    242  |   }
    243  |
    244  |   /* Preselection moved to processing, only span calculation done here *
    245  |    *
    246  |
    247  |    EXLESS and LESS(1) search: the smallest span must be found,
    248  |    but if the less spec node is not the same for all composing prefixes,
    249  |    it means it's not really this one.
    250  |
    251  |    we check that by the number of references to this node is less than
    252  |    the number of composing prefixes
    253  |
    254  |    We do the same for the less specific search - a node must be less
    255  |    specific to all prefixes.
    256  |
    257  |    if the number of references is  not enough, then return no hits,
    258  |    another try will be made, this time with one, encompassing prefix.
    259  |   */
    260  |
    261  |   if ( (search_mode == RX_SRCH_EXLESS )
    262  |        || ( search_mode == RX_SRCH_LESS && par_a == 1 ) )  {
    263  |     /* span works only for IP_V4. We use it only for inetnums,
    264  |        although RT/v4 would work too */
    265  |     if( testrang->begin.space == IP_V4 &&
    266  | 	fam_id == RX_FAM_IN ) {
    267  |       min_span = rp_find_smallest_span(*datlist);
    268  |     }
    269  |     else {
    270  |       /* in IPv6 and RT trees in general,  we can obtain the same
    271  | 	 result by selecting the longest prefix */
    272  |       max_pref = rp_find_longest_prefix(datlist);
    273  |     }
    274  |   }
    275  |
    276  |   /* Process the dataleaf copies and add to the list of answers. */
    277  |   ditem = g_list_first(*datlist);
    278  |   while(ditem != NULL) {
    279  |     rx_datref_t *refptr = (rx_datref_t *) (ditem->data);
    280  |     int exclude = 0;
    281  |
    282  |     if(search_mode == RX_SRCH_EXLESS || search_mode == RX_SRCH_LESS ) {
    283  |
    284  |       /* min_span defined <=> EXLESS or LESS(1) search of INETNUMS:
    285  | 	 the smallest span must be returned */
    286  |       if( !exclude && min_span != 0
    287  | 	  && (span = IP_rang_span( &refptr->leafptr->iprange))!=min_span) {
    288  | 	ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    289  | 		  "process_datlist: (EX)LESS: discarded object with span %d", span);
    290  | 	exclude = 1;
    291  |       }
    292  |       /* max_pref defined <=> EXLESS search of INETNUMS or LESS(1) of RT:
    293  |        */
    294  |       if( !exclude && max_pref >= 0
    295  | 	  && refptr->leafptr->preflen < max_pref ) {
    296  | 	ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    297  | 		  "process_datlist: (EX)LESS: discarded object with preflen %d",
    298  | 		  refptr->leafptr->preflen);
    299  | 	exclude = 1;
    300  |       }
    301  |
    302  |       /* number of occurences */
    303  |       /* XXX this will go when the old algorithm goes */
    304  |       if( !exclude
    305  | 	  && prefnumber > 1 ) { /* do not check if all will be approved */
    306  |
    307  | 	if( rp_leaf_occ_inc(lohash, refptr->leafptr) < prefnumber ) {
    308  | 	  ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    309  | 		    "process_datlist: (EX)LESS: leafptr %x not enough",refptr->leafptr);
    310  | 	  exclude = 1;
    311  | 	}
    312  | 	else {
    313  | 	  ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    314  | 		    "process_datlist: (EX)LESS: leafptr %x GOOD enough",refptr->leafptr);
    315  | 	}
    316  |       }
    317  |     }
    318  |     else if( search_mode ==  RX_SRCH_EXACT ) {
    319  |       /* EXACT search - discard if the range does not match */
    320  |       if( memcmp( & refptr->leafptr->iprange,
    321  | 		  testrang, sizeof(ip_range_t)) != 0) {
    322  |
    323  | 	ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    324  | 		  "process_datlist: EXACT; discarded a mismatch");
    325  | 	exclude = 1;
    326  |       } /*  EXACT match */
    327  |     }
    328  |     else if( search_mode ==  RX_SRCH_MORE ) {
    329  |       /* MORE: exclude if not fully contained in the search term */
    330  |       if( ! (IP_addr_in_rang(&refptr->leafptr->iprange.begin, testrang )
    331  | 	  && IP_addr_in_rang(&refptr->leafptr->iprange.end, testrang ))) {
    332  | 	ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    333  | 		  "process_datlist: MORE; discarded a not-fully contained one");
    334  | 	exclude = 1;
    335  |       }
    336  |     }
    337  |
    338  |
    339  |     /* get next item now, before the current gets deleted */
    340  |     newitem = g_list_next(ditem);
    341  |     if( exclude ) {
    342  |       /* get rid of it */
    343  |       rp_exclude_datlink(datlist, ditem);
    344  |     }
    345  |     else {
    346  |       /* OK, so we ACCEPT these results*/
    347  |       /* uniqueness ensured in copy_results */
    348  |       (*hits)++;
    349  |     }
    350  |     ditem = newitem;
    351  |   } /* while ditem */
    352  |
    353  |   /* wr_clear_list(&lolist); */
    354  |   g_hash_table_destroy(lohash);
    355  |   return RX_OK;
    356  | }
    357  |
    358  | /*
    359  |    rp_mod_preflist() is a helper function for rp_asc_search().
    360  |
    361  |    modifies the list of prefixes to search for,
    362  |
    363  |    special treatment for inetnum/exact:
    364  |    + a range that is equivalent to the search key (which may be a prefix)
    365  |      is made, to be used later for comparisons
    366  |
    367  |    special treatment for inetnum/exless/composed:
    368  |    + the first pass mode is set to exact (otherwise to search_mode)
    369  |
    370  |    a few optimisations are made:
    371  |    + for a route/composed_range/exact : the search is nuked
    372  |    + for an inetnum/composed_range/(exless|exact) : the list is truncated
    373  |      to one prefix, because in an exact search, it must be there anyway,
    374  |      and for the exless, the smallest encompassing one must match
    375  |
    376  |
    377  |   */
    378  |
    379  | static
    380  | er_ret_t
    381  | rp_mod_preflist(
    382  |                rx_srch_mt search_mode,
    383  |                char *key,
    384  | 	       ip_keytype_t key_type,
    385  |                rx_fam_t   fam_id,
    386  |                GList **preflist,
    387  | 	       ip_range_t *testrang,
    388  | 	       rx_srch_mt *first_pass_mode
    389  | 	       )
    390  | {
    391  |   int prefcount;
    392  |
    393  |   prefcount = g_list_length(*preflist);
    394  |
    395  |   /* EXACT search of a route tree for a composed range makes no sense */
    396  |   if( fam_id == RX_FAM_RT && search_mode == RX_SRCH_EXACT
    397  |       && key_type == IPK_RANGE && prefcount > 1 ) {
    398  |     /* abort search - i.e. clear the preflist*/
    399  |
    400  |       wr_clear_list( preflist);
    401  |
    402  |       ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    403  | 	"rp_mod_preflist: route/exact/composed - preflist cleared");
    404  |   }
    405  |
    406  |   /*+ inetnum / exact|exless specific :
    407  |     optimise: (composed range)
    408  |
    409  |       perform a separate first pass, with just one exact search on one of
    410  |       the composing prefixes - the object must be found if it's in the
    411  |       database.
    412  |
    413  |       So a little cheat: remove all but one prefixes from preflist
    414  |       and force a different search mode
    415  |       +*/
    416  |   if( fam_id == RX_FAM_IN
    417  |       && (search_mode == RX_SRCH_EXLESS || search_mode == RX_SRCH_EXACT)
    418  |       && key_type == IPK_RANGE && prefcount > 1 ) {
    419  |
    420  |       ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    421  | 	 "rp_mod_preflist: inet/ex***/composed - first pass EXACT forced");
    422  |
    423  |       *first_pass_mode = RX_SRCH_EXACT;
    424  |   } /* inetnum / exact|exless specific */
    425  |
    426  |   /* exact: set range so a comparison can be performed */
    427  |   /* must succeed after smart_conv succeeded */
    428  |   IP_smart_range(key, testrang, IP_EXPN, &key_type);
    429  |
    430  |   return RX_OK;
    431  | }
    432  | /**************************************************************************/
    433  |
    434  | /*+ appends the element pointed to by datref to finallist +*/
    435  | static
    436  | er_ret_t
    437  | rp_asc_append_datref(rx_datref_t *refptr, GList **finallist)
    438  | {
    439  |   er_ret_t err;
    440  |   rx_datcpy_t *datcpy;
    441  |   void *dataptr;
    442  |
    443  |     /* OK, so we ACCEPT this result. Copy it.*/
    444  |
    445  |     if( (err=wr_calloc( (void **)& datcpy, 1, sizeof(rx_datcpy_t))) != UT_OK) {
    446  |       return err; /*    die;*/
    447  |     }
    448  |
    449  |     datcpy->leafcpy = *(refptr->leafptr);
    450  |
    451  |     /* copy the immediate data too. Set the ptr.*/
    452  |
    453  |     if( (err=wr_calloc( (void **) & dataptr, 1, refptr->leafptr->data_len))
    454  |         != UT_OK) {
    455  |       return err; /*    die;*/
    456  |     }
    457  |     memcpy(dataptr, refptr->leafptr->data_ptr, refptr->leafptr->data_len);
    458  |
    459  |     datcpy->leafcpy.data_ptr = dataptr;
    460  |
    461  |     *finallist = g_list_prepend(*finallist, datcpy);
    462  |
    463  |     /* XXX this wouldn't work in access_control */
    464  |     ER_dbg_va(FAC_RP, ASP_RP_SRCH_DATA,
    465  | 	      "rp_asc_append 'ed: %s", dataptr);
    466  |
    467  |     return RX_OK;
    468  | }
    469  |
    470  | /*+ goes through datlist (list of references "datref") and add copies of
    471  | leaves referenced to the finallist
    472  |
    473  | maintains its own uniqhash which holds pointers to copied dataleaves.
    474  |
    475  | modifies: finallist
    476  |
    477  | returns: error from wr_malloc
    478  |
    479  | +*/
    480  | static
    481  | er_ret_t
    482  | rp_srch_copyresults(GList *datlist,
    483  | 		    GList **finallist,
    484  | 		    int maxcount)
    485  | {
    486  |   er_ret_t err;
    487  |   GList    *ditem;
    488  |   GHashTable *uniqhash = g_hash_table_new(NULL, NULL); /* defaults */
    489  |   int count = 0;
    490  |
    491  |   ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, "srch_copyresults");
    492  |
    493  |   /*  copy dataleaves pointed to by entries from the datlist
    494  |       only once (check uniqueness in the hash table) */
    495  |   for(ditem = g_list_first(datlist);
    496  |       ditem != NULL;
    497  |       ditem = g_list_next(ditem)) {
    498  |     rx_datref_t   *refptr = (rx_datref_t *) (ditem->data);
    499  |     rx_dataleaf_t *ansptr = refptr->leafptr;
    500  |
    501  |     /* search for every ansptr (dataleaf pointer) in uniqhash */
    502  |     if( g_hash_table_lookup(uniqhash, ansptr) == NULL ) {
    503  |
    504  |       /* it's not known yet. OK: put it in the hash (value==key) */
    505  |       g_hash_table_insert(uniqhash, ansptr, ansptr);
    506  |
    507  |       /* and copy the dataleaf */
    508  |       if( !NOERR(err = rp_asc_append_datref(refptr, finallist)) ) {
    509  | 	return err;
    510  |       }
    511  |     }
    512  |
    513  |     /* check the limit on number of objects if defined ( >0)  */
    514  |     count++;
    515  |     if( maxcount > 0 && count > maxcount ) {
    516  |       break;
    517  |     }
    518  |
    519  |   } /*  foreach (datlist) */
    520  |
    521  |   g_hash_table_destroy(uniqhash); /* elements are still linked to through datlist */
    522  |
    523  |   return RP_OK;
    524  | }
    525  |
    526  | static
    527  | void
    528  | rp_begend_preselection(GList **datlist, rx_fam_t fam_id, ip_range_t *testrang)
    529  | {
    530  |   GList *ditem, *newitem;
    531  |
    532  |   ditem = g_list_first(*datlist);
    533  |
    534  |   while( ditem != NULL ) {
    535  |     rx_datref_t *refptr = (rx_datref_t *) (ditem->data);
    536  |     newitem = g_list_next(ditem);
    537  |
    538  |     /* the test is indentical for route & inetnum trees */
    539  |     if( IP_addr_in_rang(&testrang->end, &refptr->leafptr->iprange) == 0 ) {
    540  |
    541  |       rp_exclude_datlink(datlist, ditem);
    542  |       ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
    543  | 		"process_datlist: discarded an uncovering leafptr %x",
    544  | 		refptr->leafptr);
    545  |
    546  |     }
    547  |     ditem = newitem;
    548  |   } /* while */
    549  | }
    550  |
    551  | /*+++++++++++++++
    552  |   translates a query into a binary prefix (or prefixes, if range).
    553  |   for registry+space (or if they are zero, for all
    554  |   registries/spaces)
    555  |   finds tree
    556  |   calls RX_bin_search (returning node copies).
    557  |   will not put duplicate entries (composed inetnums).
    558  |   returns some sort of error code :-)
    559  |
    560  |   Cuts the number of answers from RX_bin_search down to max_count,
    561  |   but since some of the answers may have been "normalized" in the
    562  |   underlying functions (multiple occurences removed),
    563  |   the result is _at_most_ max_count.
    564  |
    565  |   appends to a given list of data blocks (not nodes!)
    566  |
    567  |   The EXLESS search on inetnum tree should return the shortest range
    568  |   that was found, by means of comparing span (size) of the range.
    569  |   If there are more of size equal to the smallest one, they are also
    570  |   returned.
    571  |
    572  |   returns RX_OK or a code from an underlying function
    573  | ++++++++++++*/
    574  | er_ret_t
    575  | RP_asc_search (
    576  |                rx_srch_mt search_mode,
    577  |                int par_a,
    578  |                int par_b,
    579  |                char *key,          /*+ search term: (string) prefix/range/IP +*/
    580  |                rp_regid_t   reg_id,
    581  | 	       rp_attr_t  attr,    /*+ extra tree id (within the same reg/spc/fam +*/
    582  |                GList **finallist,    /*+ answers go here, please +*/
    583  |                int    max_count    /*+ max # of answers. RX_ALLANS == unlimited +*/
    584  |                )
    585  | {
    586  |   GList    *preflist = NULL;
    587  |   GList    *datlist = NULL;
    588  |
    589  |   er_ret_t   err;
    590  |   ip_range_t testrang;
    591  |   int        locked = 0;
    592  |   rx_srch_mt first_pass_mode = search_mode;
    593  |   ip_keytype_t key_type;
    594  |   ip_space_t   spc_id;
    595  |   rx_fam_t   fam_id = RP_attr2fam( attr );
    596  |   rx_tree_t   *mytree;
    597  |   int hits=0;
    598  |   int par_a_lyse;
    599  |
    600  |   /*  abort on error (but unlock the tree) */
    601  |   ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
    602  | 	    "RP_asc_search:  query %s : mode %d (%s) (par %d) for %s",
    603  | 	    DF_get_attribute_name(attr),
    604  | 	    search_mode, RX_text_srch_mode(search_mode), par_a, key);
    605  |
    606  |   /* parse the key */
    607  |   if( ( err = IP_smart_conv(key, 0, 0,
    608  | 			    &preflist, IP_EXPN, &key_type)) != IP_OK ) {
    609  |     /* XXX operational trouble (UT_*) or invalid key (IP_INVARG)*/
    610  |     return err;
    611  |   }
    612  |
    613  |   /* 1. find the tree */
    614  |   if( NOERR(err) ) {
    615  |     spc_id = IP_pref_b2_space( g_list_first(preflist)->data );
    616  |     err = RP_tree_get( &mytree, reg_id, spc_id, attr );
    617  |   }
    618  |
    619  |   if( ! NOERR(err) ) {
    620  |     return err;
    621  |   }
    622  |
    623  |   /* 2. lock the tree */
    624  |   TH_acquire_read_lock( &(mytree->rwlock) );
    625  |   locked = 1;
    626  |
    627  |
    628  |   /* XXX what an awful hack!!!
    629  |      In LESS(1) lookup, we have to provide more data so that something
    630  |      remains after the exact match is discarded.
    631  |    */
    632  |   par_a_lyse =  (search_mode == RX_SRCH_LESS && par_a == 1 ) ? 255 : par_a;
    633  |
    634  |   /*   0. check the list of prefixes to search for */
    635  |   err = rp_mod_preflist(search_mode, key, key_type, fam_id,
    636  | 			&preflist, &testrang, &first_pass_mode);
    637  |
    638  |   /*  a special first pass EXACT is needed for inetnums */
    639  |   if( first_pass_mode != search_mode ) {
    640  |     ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
    641  | 	      "RP_asc_search: doing pass 0 with mode %d", first_pass_mode );    /* 3. do the first pass */
    642  |     err = rp_preflist_search ( first_pass_mode, par_a, par_b,
    643  | 			       mytree, &preflist, &datlist);
    644  |   }
    645  |   if( NOERR(err) ) {
    646  |     /* 4. process the data pointers obtained from the search */
    647  |     err = rp_asc_process_datlist( first_pass_mode, par_a, fam_id,
    648  | 				  g_list_length(preflist),  &datlist,
    649  | 				  &testrang,  &hits );
    650  |   }
    651  |
    652  |   if( hits == 0 ) {
    653  |     /*  clear datlist from discarded elements */
    654  |     wr_clear_list( &datlist );
    655  |     /* reuse the preflist */
    656  |
    657  |     ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
    658  | 		"rp_asc_search: doing pass 1 with mode %d", search_mode);
    659  |     /* 3. perform the real search on all prefixes the query was converted to */
    660  |     err = rp_preflist_search ( search_mode, par_a_lyse, par_b,
    661  | 			       mytree, &preflist, &datlist);
    662  |
    663  |     if( NOERR(err) ) {
    664  |       /* 4. process the data pointers obtained from the search */
    665  |       err = rp_asc_process_datlist( search_mode, par_a, fam_id,
    666  | 				    g_list_length(preflist), &datlist,
    667  | 				    &testrang, &hits );
    668  |     }
    669  |   }
    670  |
    671  |   if( NOERR(err) ) {
    672  |     /* 5. an inetnum/composed/exless was forced to exact in the first go.
    673  |        So if the exact did not match yet, an encompassing prefix must
    674  |        be searched in exless mode */
    675  |     if(  hits == 0 ) {
    676  |       ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
    677  | 		"rp_asc_search: doing pass 2 with mode %d", search_mode);
    678  |
    679  |       /* clean old lists */
    680  |       wr_clear_list( &preflist );
    681  |       wr_clear_list( &datlist );
    682  |
    683  |       /* make a new prefix list with the encompassing prefix only */
    684  |       dieif ( IP_smart_conv(key, 0, 1,
    685  | 			    &preflist, IP_EXPN, &key_type) != IP_OK);
    686  |
    687  |       /* search again, this time with the real search_mode */
    688  |       err = rp_preflist_search ( search_mode, par_a, par_b,
    689  | 				mytree, &preflist, &datlist);
    690  |
    691  |       if( err == RX_OK ) {
    692  | 	/*  process the data pointers obtained from the search */
    693  | 	err = rp_asc_process_datlist( search_mode, par_a, fam_id,
    694  | 				      g_list_length(preflist), &datlist,
    695  | 				      &testrang, &hits  );
    696  |       }
    697  |     }
    698  |   }
    699  |
    700  |   if( NOERR(err) ) {
    701  |     err = rp_srch_copyresults(datlist, finallist, max_count); /* and uniq */
    702  |   }
    703  |
    704  |   if( locked ) {
    705  |     /* 100. unlock the tree */
    706  |     TH_release_read_lock( &(mytree->rwlock) );
    707  |   }
    708  |
    709  |   /* clean up */
    710  |   wr_clear_list( &preflist );
    711  |   wr_clear_list( &datlist );
    712  |
    713  |   return err;
    714  | }
    715  |
    716  |
    717  |
    718  |
    719  | er_ret_t
    720  | RP_new_asc_search (
    721  |                rx_srch_mt search_mode,
    722  |                int par_a,
    723  |                int par_b,
    724  |                char *key,     /*+ search term: (string) prefix/range/IP +*/
    725  |                rp_regid_t  reg_id,
    726  | 	       rp_attr_t  attr,    /*+ extra tree id (within the same reg/spc/fam +*/
    727  |                GList **finallist,    /*+ answers go here, please +*/
    728  |                int    max_count    /*+ max # of answers. RX_ALLANS == unlimited +*/
    729  |                )
    730  | {
    731  |   GList    *preflist = NULL;
    732  |   GList    *datlist = NULL;
    733  |   er_ret_t   err;
    734  |   ip_range_t  testrang;
    735  |   int        locked = 0;
    736  |   ip_keytype_t key_type;
    737  |   ip_space_t   spc_id;
    738  |   rx_fam_t   fam_id = RP_attr2fam( attr );
    739  |   rx_tree_t   *mytree;
    740  |   int hits=0;
    741  |   ip_prefix_t beginpref;
    742  |
    743  |
    744  |   /*  abort on error (but unlock the tree) */
    745  |   ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
    746  | 	    "RP_NEW_asc_search:  query %s : mode %d (%s) (par %d) for %s",
    747  | 	    DF_get_attribute_name(attr),
    748  | 	    search_mode, RX_text_srch_mode(search_mode), par_a, key);
    749  |
    750  |
    751  |   /* parse the key into a prefix list */
    752  |   if( ( err = IP_smart_conv(key, 0, 0,
    753  | 			    &preflist, IP_EXPN, &key_type)) != IP_OK ) {
    754  |     /* XXX operational trouble (UT_*) or invalid key (IP_INVARG)*/
    755  |     return err;
    756  |   }
    757  |
    758  |   /* set the test values */
    759  |   IP_smart_range(key, &testrang, IP_EXPN, &key_type);
    760  |
    761  |   /* find the tree */
    762  |   if( NOERR(err) ) {
    763  |     spc_id = IP_pref_b2_space( g_list_first(preflist)->data );
    764  |     if( ! NOERR(err = RP_tree_get( &mytree, reg_id, spc_id, attr ))) {
    765  |       return err;
    766  |     }
    767  |   }
    768  |   /* the point of no return: now we lock the tree. From here, even if errors
    769  |      occur, we still go through all procedure to unlock the tree at the end */
    770  |
    771  |   /* lock the tree */
    772  |   TH_acquire_read_lock( &(mytree->rwlock) );
    773  |   locked = 1;
    774  |
    775  |   /* Collection: this procedure is used for some search_modes only */
    776  |   if(    search_mode == RX_SRCH_EXLESS
    777  |       || search_mode == RX_SRCH_LESS
    778  |       || search_mode == RX_SRCH_EXACT )  {
    779  |
    780  |     /* 1. compose a /32(/128) prefix for beginning of range */
    781  |     beginpref.ip = testrang.begin;
    782  |     beginpref.bits = IP_sizebits(spc_id);
    783  |
    784  |     /* 2. dataleaves collection: look up the beginning prefix in LESS(255) mode */
    785  |     if( NOERR(err) ) {
    786  |       err = RX_bin_search( RX_SRCH_LESS, 255, 0, mytree, &beginpref,
    787  | 			   &datlist, RX_ANS_ALL);
    788  |     }
    789  |
    790  |     /* 3. preselection: exclude those that do not include end of range
    791  |      */
    792  |     if( NOERR(err) ) {
    793  |       rp_begend_preselection(&datlist, fam_id, &testrang);
    794  |     }
    795  |
    796  |   } /* if exless|less|exact */
    797  |   else {
    798  |     /* MORE */
    799  |
    800  |     /* standard collection using the traditional method:
    801  |        repeat the search for all prefixes and join results */
    802  |
    803  |     if( NOERR(err) ) {
    804  |       err = rp_preflist_search ( search_mode, par_a, par_b,
    805  | 				 mytree, &preflist, &datlist);
    806  |     }
    807  |   } /* collection */
    808  |
    809  |   ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
    810  | 	    "RP_NEW_asc_search: collected %d references ",
    811  | 	    g_list_length(datlist));
    812  |
    813  |
    814  |   /* 5. processing - using the same processing function */
    815  |   if( NOERR(err) ) {
    816  |     err = rp_asc_process_datlist( search_mode, par_a, fam_id,
    817  | 				  1, /* one occurence is enough */
    818  | 				  &datlist,
    819  | 				  &testrang,  &hits );
    820  |   }
    821  |
    822  |   /* 6. copy results */
    823  |   if( NOERR(err) ) {
    824  |     err = rp_srch_copyresults(datlist, finallist, max_count); /* and uniq */
    825  |   }
    826  |
    827  |   if( locked ) {
    828  |     /* 100. unlock the tree */
    829  |     TH_release_read_lock( &(mytree->rwlock) );
    830  |   }
    831  |
    832  |   /* clean up */
    833  |   wr_clear_list( &preflist );
    834  |   wr_clear_list( &datlist );
    835  |
    836  |   /* NOTE if error occured, finallist may be partly filled in. */
    837  |   return err;
    838  | }
    839  |