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.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)
/* [<][>][^][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 )
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 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)
/* [<][>][^][v][top][bottom][index][help] */
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 (
/* [<][>][^][v][top][bottom][index][help] */
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 (
/* [<][>][^][v][top][bottom][index][help] */
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