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)
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 && 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(
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 | {
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)
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 (
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 (
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 |