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  |