modules/rp/rp_search.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- rp_exclude_datlink
- rp_preflist_search
- rp_find_smallest_span
- rp_leaf_occ_inc
- rp_exclude_exact_match
- rp_find_longest_prefix
- rp_asc_process_datlist
- rp_mod_preflist
- rp_asc_append_datref
- rp_srch_copyresults
- rp_begend_preselection
- RP_asc_search
- RP_new_asc_search
1 /***************************************
2 $Revision: 1.7 $
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)
/* [<][>][^][v][top][bottom][index][help] */
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 (
/* [<][>][^][v][top][bottom][index][help] */
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 ) {
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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(
/* [<][>][^][v][top][bottom][index][help] */
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 && fam_id == RX_FAM_IN )
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(
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
484 GList **finallist)
485 {
486 er_ret_t err;
487 GList *ditem, *uitem;
488 GHashTable *uniqhash = g_hash_table_new(NULL, NULL); /* defaults */
489
490 ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET, "srch_copyresults");
491
492 /* copy dataleaves pointed to by entries from the datlist
493 only once (check uniqueness in the hash table) */
494 for(ditem = g_list_first(datlist);
495 ditem != NULL;
496 ditem = g_list_next(ditem)) {
497 rx_datref_t *refptr = (rx_datref_t *) (ditem->data);
498 rx_dataleaf_t *ansptr = refptr->leafptr;
499
500 /* search for every ansptr (dataleaf pointer) in uniqhash */
501 if( g_hash_table_lookup(uniqhash, ansptr) == NULL ) {
502
503 /* it's not known yet. OK: put it in the hash (value==key) */
504 g_hash_table_insert(uniqhash, ansptr, ansptr);
505
506 /* and copy the dataleaf */
507 if( !NOERR(err = rp_asc_append_datref(refptr, finallist)) ) {
508 return err;
509 }
510 }
511 } /* foreach (datlist) */
512
513 g_hash_table_destroy(uniqhash); /* elements are still linked to through datlist */
514
515 return RP_OK;
516 }
517
518 static
519 void
520 rp_begend_preselection(GList **datlist, rx_fam_t fam_id, ip_range_t *testrang)
/* [<][>][^][v][top][bottom][index][help] */
521 {
522 GList *ditem, *newitem;
523
524 ditem = g_list_first(*datlist);
525
526 while( ditem != NULL ) {
527 rx_datref_t *refptr = (rx_datref_t *) (ditem->data);
528 newitem = g_list_next(ditem);
529
530 /* the test is indentical for route & inetnum trees */
531 if( IP_addr_in_rang(&testrang->end, &refptr->leafptr->iprange) == 0 ) {
532
533 rp_exclude_datlink(datlist, ditem);
534 ER_dbg_va(FAC_RP, ASP_RP_SRCH_DET,
535 "process_datlist: discarded an uncovering leafptr %x",
536 refptr->leafptr);
537
538 }
539 ditem = newitem;
540 } /* while */
541 }
542
543 /*+++++++++++++++
544 translates a query into a binary prefix (or prefixes, if range).
545 for registry+space (or if they are zero, for all
546 registries/spaces)
547 finds tree
548 calls RX_bin_search (returning node copies).
549 will not put duplicate entries (composed inetnums).
550 returns some sort of error code :-)
551
552 Cuts the number of answers from RX_bin_search down to max_count,
553 but since some of the answers may have been "normalized" in the
554 underlying functions (multiple occurences removed),
555 the result is _at_most_ max_count.
556
557 appends to a given list of data blocks (not nodes!)
558
559 The EXLESS search on inetnum tree should return the shortest range
560 that was found, by means of comparing span (size) of the range.
561 If there are more of size equal to the smallest one, they are also
562 returned.
563
564 returns RX_OK or a code from an underlying function
565 ++++++++++++*/
566 er_ret_t
567 RP_asc_search (
/* [<][>][^][v][top][bottom][index][help] */
568 rx_srch_mt search_mode,
569 int par_a,
570 int par_b,
571 char *key, /*+ search term: (string) prefix/range/IP +*/
572 int reg_id,
573 rp_attr_t attr, /*+ extra tree id (within the same reg/spc/fam +*/
574 GList **finallist, /*+ answers go here, please +*/
575 int max_count /*+ max # of answers. RX_ALLANS == unlimited +*/
576 )
577 {
578 GList *preflist = NULL;
579 GList *datlist = NULL;
580
581 er_ret_t err;
582 ip_range_t testrang;
583 int locked = 0;
584 rx_srch_mt first_pass_mode = search_mode;
585 ip_keytype_t key_type;
586 ip_space_t spc_id;
587 rx_fam_t fam_id = RP_attr2fam( attr );
588 rx_tree_t *mytree;
589 int hits=0;
590 int par_a_lyse;
591
592 /* abort on error (but unlock the tree) */
593 ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
594 "RP_asc_search: query %s : mode %d (%s) (par %d) for %s",
595 DF_get_attribute_name(attr),
596 search_mode, RX_text_srch_mode(search_mode), par_a, key);
597
598 /* parse the key */
599 if( ( err = IP_smart_conv(key, 0, 0,
600 &preflist, IP_EXPN, &key_type)) != IP_OK ) {
601 /* XXX operational trouble (UT_*) or invalid key (IP_INVARG)*/
602 return err;
603 }
604
605 /* 1. find the tree */
606 if( NOERR(err) ) {
607 spc_id = IP_pref_b2_space( g_list_first(preflist)->data );
608 err = RP_tree_get( &mytree, reg_id, spc_id, attr );
609 }
610
611 if( ! NOERR(err) ) {
612 return err;
613 }
614
615 /* 2. lock the tree */
616 TH_acquire_read_lock( &(mytree->rwlock) );
617 locked = 1;
618
619
620 /* XXX what an awful hack!!!
621 In LESS(1) lookup, we have to provide more data so that something
622 remains after the exact match is discarded.
623 */
624 par_a_lyse = (search_mode == RX_SRCH_LESS && par_a == 1 ) ? 255 : par_a;
625
626 /* 0. check the list of prefixes to search for */
627 err = rp_mod_preflist(search_mode, key, key_type, fam_id,
628 &preflist, &testrang, &first_pass_mode);
629
630 /* a special first pass EXACT is needed for inetnums */
631 if( first_pass_mode != search_mode ) {
632 ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
633 "RP_asc_search: doing pass 0 with mode %d", first_pass_mode ); /* 3. do the first pass */
634 err = rp_preflist_search ( first_pass_mode, par_a, par_b,
635 mytree, &preflist, &datlist);
636 }
637 if( NOERR(err) ) {
638 /* 4. process the data pointers obtained from the search */
639 err = rp_asc_process_datlist( first_pass_mode, par_a, fam_id,
640 g_list_length(preflist), &datlist,
641 &testrang, &hits );
642 }
643
644 if( hits == 0 ) {
645 /* clear datlist from discarded elements */
646 wr_clear_list( &datlist );
647 /* reuse the preflist */
648
649 ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
650 "rp_asc_search: doing pass 1 with mode %d", search_mode);
651 /* 3. perform the real search on all prefixes the query was converted to */
652 err = rp_preflist_search ( search_mode, par_a_lyse, par_b,
653 mytree, &preflist, &datlist);
654
655 if( NOERR(err) ) {
656 /* 4. process the data pointers obtained from the search */
657 err = rp_asc_process_datlist( search_mode, par_a, fam_id,
658 g_list_length(preflist), &datlist,
659 &testrang, &hits );
660 }
661 }
662
663 if( NOERR(err) ) {
664 /* 5. an inetnum/composed/exless was forced to exact in the first go.
665 So if the exact did not match yet, an encompassing prefix must
666 be searched in exless mode */
667 if( hits == 0 ) {
668 ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
669 "rp_asc_search: doing pass 2 with mode %d", search_mode);
670
671 /* clean old lists */
672 wr_clear_list( &preflist );
673 wr_clear_list( &datlist );
674
675 /* make a new prefix list with the encompassing prefix only */
676 dieif ( IP_smart_conv(key, 0, 1,
677 &preflist, IP_EXPN, &key_type) != IP_OK);
678
679 /* search again, this time with the real search_mode */
680 err = rp_preflist_search ( search_mode, par_a, par_b,
681 mytree, &preflist, &datlist);
682
683 if( err == RX_OK ) {
684 /* process the data pointers obtained from the search */
685 err = rp_asc_process_datlist( search_mode, par_a, fam_id,
686 g_list_length(preflist), &datlist,
687 &testrang, &hits );
688 }
689 }
690 }
691
692 if( NOERR(err) ) {
693 err = rp_srch_copyresults(datlist, finallist); /* and uniq */
694 }
695
696 if( locked ) {
697 /* 100. unlock the tree */
698 TH_release_read_lock( &(mytree->rwlock) );
699 }
700
701 /* clean up */
702 wr_clear_list( &preflist );
703 wr_clear_list( &datlist );
704
705 return err;
706 }
707
708
709
710
711 er_ret_t
712 RP_new_asc_search (
/* [<][>][^][v][top][bottom][index][help] */
713 rx_srch_mt search_mode,
714 int par_a,
715 int par_b,
716 char *key, /*+ search term: (string) prefix/range/IP +*/
717 int reg_id,
718 rp_attr_t attr, /*+ extra tree id (within the same reg/spc/fam +*/
719 GList **finallist, /*+ answers go here, please +*/
720 int max_count /*+ max # of answers. RX_ALLANS == unlimited +*/
721 )
722 {
723 GList *preflist = NULL;
724 GList *datlist = NULL;
725
726 er_ret_t err;
727 ip_range_t testrang;
728 int locked = 0;
729 ip_keytype_t key_type;
730 ip_space_t spc_id;
731 rx_fam_t fam_id = RP_attr2fam( attr );
732 rx_tree_t *mytree;
733 int hits=0;
734 unsigned begin,end;
735 ip_prefix_t beginpref;
736
737
738 /* abort on error (but unlock the tree) */
739 ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
740 "RP_NEW_asc_search: query %s : mode %d (%s) (par %d) for %s",
741 DF_get_attribute_name(attr),
742 search_mode, RX_text_srch_mode(search_mode), par_a, key);
743
744
745 /* parse the key into a prefix list */
746 if( ( err = IP_smart_conv(key, 0, 0,
747 &preflist, IP_EXPN, &key_type)) != IP_OK ) {
748 /* XXX operational trouble (UT_*) or invalid key (IP_INVARG)*/
749 return err;
750 }
751
752 /* set the test values */
753 IP_smart_range(key, &testrang, IP_EXPN, &key_type);
754
755 /* find the tree */
756 if( NOERR(err) ) {
757 spc_id = IP_pref_b2_space( g_list_first(preflist)->data );
758 if( ! NOERR(err = RP_tree_get( &mytree, reg_id, spc_id, attr ))) {
759 return err;
760 }
761 }
762 /* the point of no return: now we lock the tree. From here, even if errors
763 occur, we still go through all procedure to unlock the tree at the end */
764
765 /* lock the tree */
766 TH_acquire_read_lock( &(mytree->rwlock) );
767 locked = 1;
768
769 /* Collection: this procedure is used for some search_modes only */
770 if( search_mode == RX_SRCH_EXLESS
771 || search_mode == RX_SRCH_LESS
772 || search_mode == RX_SRCH_EXACT ) {
773
774 /* 1. compose a /32(/128) prefix for beginning of range */
775 beginpref.ip = testrang.begin;
776 beginpref.bits = IP_sizebits(spc_id);
777
778 /* 2. dataleaves collection: look up the beginning prefix in LESS(255) mode */
779 if( NOERR(err) ) {
780 err = RX_bin_search( RX_SRCH_LESS, 255, 0, mytree, &beginpref,
781 &datlist, RX_ANS_ALL);
782 }
783
784 /* 3. preselection: exclude those that do not include end of range
785 */
786 if( NOERR(err) ) {
787 rp_begend_preselection(&datlist, fam_id, &testrang);
788 }
789
790 } /* if exless|less|exact */
791 else {
792 /* MORE */
793
794 /* standard collection using the traditional method:
795 repeat the search for all prefixes and join results */
796
797 if( NOERR(err) ) {
798 err = rp_preflist_search ( search_mode, par_a, par_b,
799 mytree, &preflist, &datlist);
800 }
801 } /* collection */
802
803 ER_dbg_va(FAC_RP, ASP_RP_SRCH_GEN,
804 "RP_NEW_asc_search: collected %d references ",
805 g_list_length(datlist));
806
807
808 /* 5. processing - using the same processing function */
809 if( NOERR(err) ) {
810 err = rp_asc_process_datlist( search_mode, par_a, fam_id,
811 1, /* one occurence is enough */
812 &datlist,
813 &testrang, &hits );
814 }
815
816 /* 6. copy results */
817 if( NOERR(err) ) {
818 err = rp_srch_copyresults(datlist, finallist); /* and uniq */
819 }
820
821 if( locked ) {
822 /* 100. unlock the tree */
823 TH_release_read_lock( &(mytree->rwlock) );
824 }
825
826 /* clean up */
827 wr_clear_list( &preflist );
828 wr_clear_list( &datlist );
829
830 /* NOTE if error occured, finallist may be partly filled in. */
831 return err;
832 }
833