1    | /***************************************
2    |   $Revision: 1.23 $
3    | 
4    |   Radix tree (rx).  rx_search.c - functions to search nodes of the tree
5    | 
6    |   Status: NOT REVUED, TESTED, INCOMPLETE
7    | 
8    |   Design and implementation by: Marek Bukowy
9    | 
10   |   ******************/ /******************
11   |   Copyright (c) 1999                              RIPE NCC
12   |  
13   |   All Rights Reserved
14   |   
15   |   Permission to use, copy, modify, and distribute this software and its
16   |   documentation for any purpose and without fee is hereby granted,
17   |   provided that the above copyright notice appear in all copies and that
18   |   both that copyright notice and this permission notice appear in
19   |   supporting documentation, and that the name of the author not be
20   |   used in advertising or publicity pertaining to distribution of the
21   |   software without specific, written prior permission.
22   |   
23   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29   |   ***************************************/
30   | 
31   | 
32   | #include <erroutines.h>
33   | #include <rxroutines.h>
34   | #include <stubs.h>
35   | 
36   | #include "iproutines.h"
37   | 
38   | /***************************************************************************/
39   | 
40   | /*++++++++++++++
41   | Descends the given tree following the last prefix bit to get [past]
42   | the node with the given prefix.
43   | It fills up a stack of COPIES of nodes, including glue nodes.
44   | 
45   | Then it also sets the number of elements on the stack: 
46   | set maxdepth to the position where a next one would be written
47   | ( = last + 1, or number of nodes pushed)
48   | 
49   |    The dmodes:
50   |            
51   |    RX_STK_QUERY_NOGLUE = (search exact/less spec) stop when 
52   |                          * the current prefix length >= newprefix length 
53   |                          * the current prefix does not match anymore
54   |                          * do not add glue nodes
55   |                         
56   |    RX_STK_QUERY_ALLNOD = as above, except that the glue and data nodes are
57   |                          treated equally (i.e. glue nodes are not skipped)
58   | 
59   |    RX_STK_CREAT        = descend until the next non-glue node past the one found
60   |                          in exact mode (for creation)
61   | 
62   | ++++++++++++++*/
63   | 
64   | er_ret_t
65   | rx_build_stack(rx_nodcpy_t    stack[], 
66   |                int            *maxdepth, 
67   |                rx_tree_t      *tree, 
68   |                ip_prefix_t    *newpref,
69   |                rx_stk_mt      dmode
70   |                )
71   | {
72   |   register rx_node_t *curnode;
73   |   register int link, quit_now=0;
74   |   char bbf[IP_PREFSTR_MAX];
75   | 
76   |   if( ER_is_traced( FAC_RX, ASP_RX_STKBLD_GEN)) {
77   |     IP_pref_b2a( newpref , bbf, IP_PREFSTR_MAX);
78   |     ER_dbg_va(FAC_RX, ASP_RX_STKBLD_GEN, 
79   |             "rx_build_stack: searching for %s in mode %d", bbf, dmode);
80   |   }
81   | 
82   |   *maxdepth = 0;
83   |   
84   |   if ( tree -> num_nodes == 0) { 
85   |     // The tree was empty. 
86   |     return RX_OK;
87   |   }
88   |   
89   |   curnode = tree->top_ptr;
90   |   // this works for RAM, for SQL one would have to call a 'getsqlnode' here
91   |   
92   |   // OK, there is at least one node. Descend the tree 
93   |   // as long as the correct bit length is not exceeded
94   |   // or a glue is being found (take the last non-glue node then) 
95   |   // or you run out of nodes in the direction of descending
96   |   
97   |   do {
98   |     // check at the current node, where the one we look for would fit
99   |     // (the second argument of IP_addr_bit_get starts with 0,
100  |     // so this effectively looks at the bit next to the last significant bit
101  |     // of the current node
102  |     
103  |     link = IP_addr_bit_get( & newpref->ip, curnode->prefix.bits );    
104  |     
105  |     // check conditions for leaving the loop    
106  |     if(curnode->child_ptr[link] == NULL) {
107  |       // end of branch. quit after adding the current node to the stack
108  |       // (or before - subject to bit test in QUERY mode)
109  |       quit_now = 1;
110  |     }
111  |     else {
112  |     /* check the node. 
113  |        BIG DIFFERENCE between the modes:
114  |        in CREAT we don't mind the stack to go too deep, 
115  |        in QUERY it can lead to false answers
116  |        (e.g. a /24 is found for a /23 query). 
117  | 
118  |        So this must be "peeled off the stack" later in the search routine,
119  |        if both types of stack are to work properly with query searches.
120  | 
121  |        ONE MORE THING: in LESS SPECIFIC searches a QUERY stack MUST BE USED,
122  |        because only this one actually compares the prefixes. 
123  |        This has no effect on the exact search, but will cause less-spec
124  |        to return an object that does not contain the search term.
125  |     */
126  | 
127  | 
128  |       if( curnode->prefix.bits > newpref->bits ) {
129  |         // deep enough.
130  |         quit_now = 2;
131  |       }
132  | 
133  |       if(dmode == RX_STK_CREAT && curnode->glue) {
134  |         // mode: creation. 
135  |         // Cancel quitting if glue -- in CREAT mode the stack building 
136  |         // should stop at the next real (non-glue) node.
137  |         // ("next" meaning following link #0)
138  |         quit_now = 0;
139  |       }
140  |     }
141  |     
142  |     /* now that the conditions for leaving the loop after the node is
143  |        added on the stack, see if we shouldn't leave the loop BEFOREHAND */
144  |     
145  |     /* In query mode, we should quit as soon as we see a mismatch */
146  | 
147  |     if(dmode != RX_STK_CREAT
148  |        && 0 != IP_addr_cmp(&curnode->prefix.ip, &newpref->ip, 
149  |                            curnode->prefix.bits) ) {
150  |         //QUIT NOW! (do not add this node)
151  |       quit_now = 64;
152  |     }
153  | 
154  |     // push the current node on the stack. RAM only.
155  |     // 
156  |     // (unless quit_now is 64 which means do NOT copy the current node.
157  |     //
158  |     // In CREAT and QUERY_ALLNOD modes, push everything. 
159  |     // In QUERY_NOGLUE mode, only non-glues.
160  |       
161  |     if( quit_now < 64 
162  |         && (dmode != RX_STK_QUERY_NOGLUE || curnode->glue == 0 )) {
163  |       memcpy( & stack[*maxdepth].cpy, curnode, sizeof(rx_node_t));
164  |       stack[*maxdepth].srcptr = curnode;
165  |       stack[*maxdepth].srckey = SQ_NOKEY;
166  |       stack[*maxdepth].tree = tree;
167  |       (*maxdepth)++;
168  |     }
169  |     
170  |     // make debug info.
171  |    
172  |     if( ER_is_traced( FAC_RX, ASP_RX_STKBLD_DET)) {
173  |       IP_pref_b2a( & curnode->prefix , bbf, IP_PREFSTR_MAX );
174  |       ER_dbg_va(FAC_RX, ASP_RX_STKBLD_DET,
175  |                 "rx_build_stack: %s%d at %s%s (stk len: %d)",
176  |                 quit_now ? "stop/" : "link ",  
177  |                 quit_now ? quit_now : link,
178  |                 bbf, ( curnode->glue ) ? " ++glue++" : "",
179  |                 *maxdepth  );
180  |     }
181  |     
182  |        curnode = curnode -> child_ptr[link];
183  | 
184  |   } while( !quit_now ); 
185  | 
186  |   return RX_OK;
187  | }
188  | 
189  | /***************************************************************************/
190  | /*+++++++++
191  |    helper for the nod_search routine: 
192  | 
193  |    allocates a new node copy struct, copy the struct and add to nodlist
194  | ++++++++++*/
195  | 
196  | static
197  | er_ret_t
198  | rx_nod_append( GList **nodlist, rx_nodcpy_t *element) 
199  | {
200  |   rx_nodcpy_t *newcpy;
201  |   er_ret_t err;
202  |   
203  |   if( (err=wr_calloc( (void **) & newcpy, 1, sizeof(rx_nodcpy_t))) != UT_OK) {
204  |     return err; //    die;
205  |   }
206  |   memcpy(newcpy, element, sizeof(rx_nodcpy_t));        
207  |   (*nodlist) = g_list_prepend( *nodlist, newcpy );
208  | 
209  |   return RX_OK;
210  | }
211  | 
212  | 
213  | 
214  | 
215  | /***************************************************************************/
216  | 
217  | /*+++++++++++
218  |   helper for MORE specific lookup in rx_nod_search 
219  | 
220  |   adds a node to the list of answers.
221  | +++++++++++*/
222  | 
223  | static
224  | er_ret_t
225  | rx_walk_hook_addnode(rx_node_t *node, int level, int nodecounter, 
226  |                      void *userptr)
227  | {   
228  |   rx_nodcpy_t nodcpy;
229  |   hook_addnode_userdat_t *userdat = userptr;
230  |   
231  | 
232  |   // do not append glue nodes
233  |   if( node->glue == 1 ) return RX_OK;
234  |   
235  |   // in RAM mode, do not copy the node.
236  |   //  memcpy( &nodcpy.cpy, node, sizeof(rx_node_t));
237  |   /* XXX to avoid warnings from workshop: but slows things down! */
238  |   memset( &nodcpy.cpy, 0, sizeof(rx_node_t));
239  | 
240  |   nodcpy.srcptr = node;
241  |   nodcpy.srckey = SQ_NOKEY;
242  |   nodcpy.tree = userdat->tree;
243  |   
244  |   return rx_nod_append( userdat->nodlist, &nodcpy);
245  | }
246  | 
247  | 
248  | /***************************************************************************/
249  | 
250  | /*+++++++++++
251  |   helper for DBLS lookup in rx_nod_search 
252  | 
253  |   adds a node to the list of answers.
254  | +++++++++++*/
255  | 
256  | static
257  | er_ret_t
258  | rx_walk_hook_adddoubles(rx_node_t *node, int level, int nodecounter, 
259  |                         void *userptr)
260  | {
261  |   rx_nodcpy_t nodcpy;
262  |   hook_addnode_userdat_t  *userdat = userptr;
263  |   int leaves = g_list_length(node->leaves_ptr);
264  |   char buf[1024];
265  |   
266  |   // do not append glue nodes
267  |   if( node->glue == 1 ) return RX_OK;
268  | 
269  |  
270  |   // add only nodes with more than 1 dataleaf
271  |   if( leaves < 2 ) return RX_OK;
272  | 
273  |   if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
274  |     rx_nod_print(node, buf, 1024);
275  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
276  |               "rx_walk_hook_adddoubles: %30s, %d leaves", buf, leaves);
277  |   }
278  | 
279  |   //  memcpy( &nodcpy.cpy, node, sizeof(rx_node_t));
280  |   nodcpy.srcptr = node;
281  |   nodcpy.srckey = SQ_NOKEY;
282  |   nodcpy.tree = userdat->tree;
283  |   
284  |   return rx_nod_append( userdat->nodlist, &nodcpy);
285  | }
286  | 
287  | 
288  | /***************************************************************************/
289  | er_ret_t
290  | rx_nod_search (
291  |                rx_srch_mt  search_mode,
292  |                int         par_a,
293  |                int         par_b,
294  |                /* see rx_asc_search() for explanation */
295  |                rx_tree_t  *tree,           // tree ptr
296  |                ip_prefix_t  *prefix,          // binary prefix
297  | 
298  |                rx_nodcpy_t stack[],         // stack==array of node_copies
299  |                int         stackcount,      // number of element on the stack,
300  |                                             // can come from a creat stack!
301  | 
302  |                GList       **nodlist,       // answers go here
303  |                int         max_count        // max # of answers
304  |                )                        
305  |      /*
306  |         searches the stack for a given prefix, finds *nodes* in the stack 
307  |         and appends *copies of the nodes* to the nodlist;
308  | 
309  |         finds
310  |         0 or 1 nodes for exact search
311  |         0 or 1 nodes for exless (0 if no less specific node found)
312  |         any number (incl. 0) for {more|less}^n-m specific 
313  |      
314  |        returns errcode.
315  | 
316  |        
317  |      */
318  | {
319  |   char buf[1024];
320  |   int sps = stackcount-1;       // stack position.
321  |   int depthcounter=0;
322  |   er_ret_t err=RX_OK;
323  |   int i;
324  |   hook_addnode_userdat_t datstr;
325  |   er_ret_t (*hook_function)();  // pointer to the walk_hook function
326  |                                 // (see MORE spec lookup)
327  | 
328  |   /* structure for carrying data to walk_tree hook functions, used only
329  |      in MORE, DBLS and RANG search modes 
330  |   */
331  |   datstr.nodlist = nodlist;
332  |   datstr.tree    = tree;
333  |   datstr.prefix  = prefix;
334  |     
335  |   
336  |   if( ER_is_traced( FAC_RX, ASP_RX_SRCH_BOT)) {
337  |     IP_pref_b2a( prefix , buf, IP_PREFSTR_MAX);
338  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
339  |               "rx_nod_search: searching for %s in mode %d", buf, search_mode);
340  |   }
341  | 
342  |   /* in non-CREAT modes, glue nodes are skipped anyway. 
343  |      (they should normally not be there if the stack was created in
344  |      the STK_QUERY mode, but it's possible to use a CREAT stack too).
345  | 
346  |      It's also possible that the stack is too deep.
347  |      So, truncate the stack to the last non-glue node 
348  |      of the length <= search term.
349  |   */
350  |   
351  |   if( search_mode != RX_SRCH_CREAT && search_mode != RX_SRCH_RANG) {
352  |     while( sps >= 0 
353  |            && ( 
354  |                stack[sps].cpy.prefix.bits > prefix->bits           // too deep
355  |                || ( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS 
356  |                     && stack[sps].cpy.glue == 1 )                  // is glue
357  |                || ( search_mode == RX_SRCH_LESS
358  |                     && stack[sps].cpy.prefix.bits == prefix->bits )// too deep
359  |                )
360  |            ) {
361  |       
362  |       if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
363  |         rx_nod_print( & stack[sps].cpy , buf, IP_PREFSTR_MAX);
364  |         ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
365  |                   "rx_nod_search: peeling off %d: %s", sps, buf);
366  |       }
367  |       sps--;
368  |     }
369  |   }
370  |   
371  |   // nothing left on the stack. Sorry.
372  |   // we allow that for more spec search -- this means
373  |   // that the search term is a shorter prefix than the one
374  |   // in the top node. Possibly it's 0/0 which is valid for more spec search.
375  | 
376  |   if( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS 
377  |       && sps < 0 ) {       
378  |     return RX_OK;
379  |   }
380  |       
381  |   switch(search_mode) {
382  |   case RX_SRCH_EXACT:
383  |   case RX_SRCH_CREAT:
384  |     // go up the tree (stack) and exit when the proper prefix is found.
385  |     // For RX_SRCH_EXACT skip glue nodes, for RX_SRCH_CREAT take all.
386  |     // They may contain a valid prefix, so watch out.
387  | 
388  |     while(sps >= 0) {
389  | 
390  |       if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
391  |         rx_nod_print(& stack[sps].cpy, buf, 1024);
392  |         ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
393  |                   "rx_nod_search: position %d: %s", sps, buf);
394  |       }
395  |       
396  |       if ( search_mode == RX_SRCH_EXACT 
397  |            && stack[sps].cpy.glue ) {
398  |         die;
399  |       }
400  |       
401  |       if ( memcmp( & stack[sps].cpy.prefix, 
402  |                    prefix, 
403  |                    sizeof(ip_prefix_t)) == 0 ) {
404  |         // FOUND!!
405  |         // add to the nodlist.
406  | 
407  |         if( (err=rx_nod_append( nodlist, & stack[sps])) != RX_OK ) {
408  |           return err;
409  |         }
410  | 
411  |         ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, "rx_nod_search: found!");
412  |         break;
413  |       }
414  |       sps--;
415  |     }
416  |     break;
417  | 
418  |   case RX_SRCH_EXLESS:
419  |     // just fetch the last element off the stack (if any). 
420  |     // Must be non-glue for EXLESS.
421  | 
422  |     if( sps >= 0 ) {
423  |       rx_nod_append( nodlist, & stack[sps]); 
424  |     }
425  | 
426  |     // else : nothing found.
427  |     // For EXLESS: check if the stack contains only non-glue nodes.
428  |     // If it contains a glue, it means it was created in the CREAT mode,
429  |     // which renders the above algorithm absolutely useless. Then crash,
430  |     // this is a programmer's error.
431  | 
432  |     while( sps >= 0 ) {
433  |       if( stack[sps].cpy.glue ) {
434  |         die;
435  |       }
436  |       sps--;
437  |     }
438  | 
439  |     break;
440  | 
441  |   case RX_SRCH_LESS:
442  |     while( sps >= 0 && depthcounter < par_a ) {
443  |       if( stack[sps].cpy.glue == 0 ) {
444  |         rx_nod_append( nodlist, & stack[sps]); 
445  |         depthcounter++;
446  |       }
447  |       sps--;
448  |     }
449  |     break;
450  | 
451  |   case RX_SRCH_MORE:
452  |   case RX_SRCH_DBLS:   // special (debug?) mode : find nodes with multiple
453  |                        // data leaves. Much like more specific, except that
454  |                        // most nodes will be skipped.
455  |                        // The difference is in calling another hook function
456  |     hook_function = ( search_mode == RX_SRCH_MORE )  
457  |       ? rx_walk_hook_addnode
458  |       : rx_walk_hook_adddoubles;
459  |     
460  |     // the result of a more spec search should NOT contain the object exactly
461  |     // matching the query, even if it exists in the database. So two walks are 
462  |     // performed, one for each child (if it exists). 
463  |     // MEMORY IMPLEMENTATION ONLY FOR THE MOMENT
464  | 
465  |     // COVER THE CASE 0.0.0.0/0 
466  |     // or any other prefix that the tree might be set to represent,
467  |     // but there is no actual object for it (not even glue)
468  | 
469  |     if( sps < 0 )  {                  // start from the top node if contained 
470  |       if( prefix->bits >= tree->prefix.bits ) {
471  |         
472  |         rx_walk_tree( tree->top_ptr, hook_function,
473  |                       RX_WALK_REVERS | RX_WALK_SKPGLU, // skip glue nodes while counting
474  |                       par_a, // display this many levels 
475  |                       0, 0, &datstr, &err);
476  |         if( err != RX_OK ) {
477  |           return err;
478  |         }
479  |       }
480  |     }            
481  |     else { 
482  |       for( i = 1; i >= 0; i--) {
483  |         if( stack[sps].cpy.child_ptr[i] != NULL ) {
484  |           if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
485  |             IP_pref_b2a(& stack[sps].cpy.child_ptr[i]->prefix, buf, 1023);
486  |           }
487  | 
488  |           if( 0 == IP_addr_cmp( & stack[sps].cpy.child_ptr[i]->prefix.ip, 
489  |                                 & prefix->ip, 
490  |                                 prefix->bits) ) {
491  |             
492  |             ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
493  |                       "rx_nod_search: digging child %d: %s", i, buf);
494  |             
495  |             rx_walk_tree( stack[sps].cpy.child_ptr[i],  hook_function, 
496  |                           RX_WALK_REVERS | RX_WALK_SKPGLU, // skip glue nodes while counting
497  |                           par_a, // display this many levels 
498  |                           0, 0, &datstr, &err);
499  |             if( err != RX_OK ) {
500  |               return err;
501  |             }
502  |           }
503  |           else {
504  |             ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
505  |                       "rx_nod_search: prefix mismatch with child %d: %s", 
506  |                       i, buf);
507  |           }
508  |         }
509  |       }
510  |     }
511  |     break;
512  | 
513  |   case RX_SRCH_RANG:
514  |     /* OK, start from the node at the end of the stack (exless match including
515  |        glue nodes) then
516  | 
517  |        
518  |        if its prefix length is 
519  |          longer -> OK, it stopped too far, go up
520  |          OK -> found! descend from here as long as the prefixes are in range
521  |          shorter -> apparently there is even no such glue node. come back down
522  |                     one step
523  |                    
524  |     */
525  |     
526  |     i = sps;               // go up the tree (down the stack) 
527  |                            // until too far (one node too much, after >= )
528  |     while( i >= 0 && stack[i].cpy.prefix.bits >= prefix->bits ) {
529  |       i--;
530  |     }
531  |     
532  |     // look where you are:
533  |     
534  |     if( i < 0 )          // it was the top object, but its prefix was too long
535  |       i=0;               // take the top object as the base
536  |     else
537  |       i++;               // went one too much, now come back one step
538  |     
539  |     
540  |     rx_walk_tree( stack[i].srcptr,  rx_walk_hook_addnode, 
541  |                   RX_WALK_PRFLEN, // skip glue nodes while counting
542  |                   par_a, // display up to this max length
543  |                   0, 0, &datstr, &err);
544  |     if( err != RX_OK ) {
545  |       return err;
546  |     }
547  |     
548  |     break;    
549  | 
550  |     // return RX_NOYETI;
551  |     //not implemented
552  |     //    die; 
553  |   default:
554  |     die; // are you nuts??
555  |   }
556  | 
557  |   return err;
558  | 
559  | }
560  | 
561  | 
562  | 
563  | /*****************************************************************************/
564  | /*+
565  |    this is a specialised find function to find nodes in the list of 
566  |    answer structs that point to the given data leaf.
567  |    This is used to avoid reporting data leaves more than once
568  |    (eg. because the node is composed (inetnum) 
569  |    
570  | +*/
571  | 
572  | static
573  | GList *
574  | rx_find_leaf(GList *anslist, rx_dataleaf_t *leafptr)
575  | {
576  |   GList *item;
577  |   
578  |   for(item = g_list_first(anslist);
579  |       item != NULL;
580  |       item = g_list_next(item)) {
581  |     if( ((rx_datref_t *)(item->data))->leafptr == leafptr) {
582  |       return item;
583  |     }
584  |   }
585  |   
586  |   return NULL; 
587  | }
588  | 
589  | /*****************************************************************************/
590  | /*+++++++++++++
591  |   builds a stack for this prefix, finds *nodes* in the stack 
592  |   and appends *copies of the data leaves* to the LL of answers;
593  |   
594  |   sorts by SQL object keys and uniq's the data
595  |   
596  |   finds:
597  |   0 or 1 nodes for exact search
598  |   0 or 1 nodes for exless (0 if no less specific node found)
599  |   any number (incl. 0) for {more|less}-n specific 
600  |   
601  |   then copies the nodes/dataleaves to the answer structs and appends them
602  |   to the given LL. So, effectively, the number of answers can be
603  |   anything from 0 to infinity, because objects may be duplicate 
604  |   even at the same node.
605  |   
606  |   returns errcode.
607  |   
608  |   algorithm:
609  |   
610  |   builds stack[MAXBIT (==128)];
611  |   
612  |   if( more/less-depth && par_a == 0)
613  |   
614  |   run rx_nod_search, then 
615  |   
616  |   if(more spec) rx_nod_walk(maxdepth=n, append_to_LL() );
617  |   if(less spec) do { append(LL, stack[i]) } while(i-- && n--);
618  |   otherwise just set LL
619  |   
620  |   
621  |   The routine provides _at_least_ max_count answers. 
622  |   It will *try* to stop after max_count as soon as possible 
623  |   - but it's the higher level routine that should do the final cut.
624  | +++++++++++++++*/
625  | 
626  | er_ret_t
627  | RX_bin_search (
628  |                rx_srch_mt  search_mode,
629  |                int         par_a,
630  |                int         par_b,
631  |                rx_tree_t  *tree,           // tree ptr
632  |                ip_prefix_t *prefix,         // binary prefix
633  |                GList       **datleaves,    // data leaves go here
634  |                int         max_count 
635  |                )
636  |    
637  | {
638  |   char buf[256];
639  |   rx_nodcpy_t  stack[128];
640  |   int i, k;
641  |   int stkcnt, resnum = 0, maxleaves;
642  |   GList  *nodlist = NULL, *nitem;
643  |   rx_node_t *curnode;
644  |   rx_nodcpy_t *curcpy;
645  |   rx_datref_t *datref;
646  |   rx_stk_mt     dmode;
647  | 
648  |   // more specific node search may start from a glue node, 
649  |   // for all others the stack should not contain glues.
650  | 
651  |   dmode = ( search_mode == RX_SRCH_MORE 
652  |             || search_mode == RX_SRCH_DBLS
653  |             || search_mode == RX_SRCH_RANG ) 
654  |     ? RX_STK_QUERY_ALLNOD
655  |     : RX_STK_QUERY_NOGLUE;
656  |   
657  |   rx_build_stack(stack, &stkcnt, tree, prefix, dmode);
658  | 
659  |   rx_nod_search( search_mode, par_a, par_b, tree, prefix, 
660  |                  stack, stkcnt, &nodlist, 1000);
661  |   
662  |   ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, "RX_bin_search: processing nodes");
663  | 
664  |   for( nitem = g_list_first(nodlist);
665  |        nitem != NULL;
666  |        nitem = g_list_next(nitem)) {    
667  |     
668  |     resnum++;
669  |     curcpy = nitem->data;
670  |     
671  |     /*
672  |       if memory mode includes RAM:
673  |       * do not expect copies of nodes in the list received from bin_search.
674  |       * iterate through data leaves with g_list_nth_data.
675  |       */
676  |     
677  |     curnode = curcpy->srcptr;
678  |     
679  |     //    rx_nod_print( curnode, buf, 1024 );
680  |     
681  |     maxleaves = g_list_length(curnode->leaves_ptr);
682  |     //    fprintf(stderr,"###node %d, %d dataleaves attached:", i, maxleaves);
683  | 
684  |     // iterate through dataleafs attached to this node
685  |     for(k=0; k<maxleaves; k++) {
686  |       rx_dataleaf_t *leafptr = g_list_nth_data(curnode->leaves_ptr, k);
687  | 
688  |       /* 
689  | 	 check the conditions to add the leaf:
690  | 
691  | 	 1. never add the same leaf twice (can occur for repeated queries
692  | 	 because of composed ranges)
693  | 	 2. never add composed inetnum for exact prefix search
694  | 	 (but do for exact range search...) - must be solved in upper layer.
695  | 
696  |       */
697  | 
698  |       // add only if not yet on the list, i.e if it's composed then check,
699  |       // otherwise just add
700  | 
701  |       //      if( tree->family == RX_FAM_IN && leafptr->composed > 0 ) {
702  |         GList *item;
703  |         int already_there = 0;
704  |         
705  |         for(item = g_list_first(*datleaves);
706  |             item != NULL;
707  |             item = g_list_next(item)) {
708  | 	  rx_datref_t *tmpref = (rx_datref_t *) item->data;
709  | 	  
710  | 	  if( tmpref->leafptr == leafptr ) {
711  | 	    already_there = 1;
712  | 	    break;
713  | 	  }
714  |         }
715  |         
716  |         if( already_there == 1 ) {
717  |           continue; // take next element
718  |         }
719  |         else {
720  | 	  // add
721  | 
722  | 	  dieif( wr_calloc( (void **) &datref, 
723  | 			    sizeof(rx_datref_t), 1) != UT_OK);
724  | 	  datref->leafptr = leafptr;
725  |       
726  | 	  *datleaves = g_list_prepend(*datleaves, datref);
727  | 	}
728  |     }
729  |   }
730  | 
731  |   g_list_foreach(nodlist, rx_free_list_element, NULL);
732  |   g_list_free(nodlist);
733  | 
734  |   ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
735  |             "RX_bin_search: found %d nodes", resnum);
736  |     
737  |   
738  |   /* the LL of answers (*datleaves) contains pointers to answer structs, 
739  |      that SHOULD BE NORMALIZED HERE (==with no redundant entries)
740  |   */
741  | 
742  | return RX_OK;
743  | }
744  | 
745  | /**************************************************************************/
746  | /*+++++++++++
747  |     this routine goes through the list of prefixes and performs a bin_search
748  |    on each of them; attaches the results to datlist.
749  |    Then, frees the prefix list.
750  | +++++++++++*/
751  | static
752  | er_ret_t
753  | rx_preflist_search (
754  |                     rx_srch_mt search_mode, 
755  |                     int par_a,
756  |                     int par_b,
757  |                     rx_tree_t  *mytree,
758  |                     GList    **preflist,
759  |                     GList    **datlist
760  |                     )
761  | 
762  | { 
763  |   char   prefstr[IP_PREFSTR_MAX];
764  |   GList   *qitem;
765  |   ip_prefix_t *querypref;
766  |   er_ret_t err;
767  |   
768  |   for( qitem = g_list_first(*preflist);
769  |        qitem != NULL;
770  |        qitem = g_list_next(qitem)) {
771  |     
772  |     querypref = qitem->data;
773  |     
774  |     if( IP_pref_b2a( querypref, prefstr, IP_PREFSTR_MAX) != IP_OK ) {
775  |       die;
776  |     }
777  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
778  |               "rx_preflist_search: mode %d (par %d) for %s", 
779  |               search_mode, par_a, prefstr);
780  |     
781  |     if (mytree->num_nodes > 0) {
782  |       err = RX_bin_search( search_mode, par_a, par_b, mytree, querypref, 
783  |                    datlist, RX_ANS_ALL);
784  |       if( err != RX_OK ) {
785  |         return err;
786  |       }
787  |     }
788  |   }
789  |   
790  |   g_list_foreach(*preflist, rx_free_list_element, NULL);
791  |   g_list_free(*preflist);
792  |   *preflist = NULL;
793  |   return RX_OK;
794  | }
795  | 
796  |   
797  | /**************************************************************************/
798  | /*+++++++++++++++
799  |   translates a query into a binary prefix (or prefixes, if range).
800  |   for registry+space (or if they are zero, for all
801  |   registries/spaces)
802  |   finds tree 
803  |   calls RX_bin_search (returning node copies).
804  |   will not put duplicate entries (composed inetnums).
805  |   returns some sort of error code :-) 
806  |   
807  |   Cuts the number of answers from RX_bin_search down to max_count,
808  |   but since some of the answers may have been "normalized" in the
809  |   underlying functions (multiple occurences removed), 
810  |   the result is _at_most_ max_count.
811  |   
812  |   appends to a given list of data blocks (not nodes!)
813  |   
814  |   returns RX_OK or a code from an underlying function
815  | ++++++++++++*/
816  | 
817  | er_ret_t
818  | RX_asc_search ( 
819  |                rx_srch_mt search_mode, 
820  |                /*+ MODE={exact|exless|less-depth|more-depth|more-bit}
821  |                  exless == our default search (exact or less-1 spec.)
822  |                  more-bit == more specific bit length searches:
823  |                  inclusive, exclusive, bit length, range of bit lengths...
824  |                  All of them can be expressed using a range of lengths.
825  |                  That means TWO integer parameters must be also given.
826  |                  +*/
827  |                int par_a,
828  |                int par_b,
829  |                /*+ the semantic of these parameters (par_a,par_b) is:
830  |                   - for more-bit: the length range [preflen - par_a], 
831  |                   - for less/more-depth: par_a is the depth
832  |                   (1-2-3... means so many levels,
833  |                   RX_ALL_DEPTHS means all levels)
834  |                   - for exact/exless: both are ignored.
835  | 
836  |                   par_b is ignored, it's there for historical/future reasons
837  |                +*/
838  |                
839  |                char *key,          /*+ search term: (string) prefix/range/IP +*/
840  |                int   reg_id,
841  |                ip_space_t spc_id,  /*+ space id, one of IPv4 IPv6. +*/
842  |                rx_fam_t   fam_id,  /*+ RX_FAM_RT or RX_FAM_IN +*/
843  |                GList **anslist,    /*+ answers go here, please +*/
844  |                int    max_count    /*+ max # of answers. RX_ALLANS == unlimited +*/
845  |                )
846  |      
847  | {
848  |   GList    *datlist = NULL, *preflist = NULL, *ditem;
849  |   ip_prefix_t testpref;
850  |   char   prefstr[IP_PREFSTR_MAX];
851  |   int prefcount = 0;
852  |   rx_tree_t  *mytree;
853  |   er_ret_t err; 
854  |   ip_range_t myrang;
855  |   int is_exless_inetnum = 0, is_exact_range_inetnum = 0;
856  |   ip_rangesize_t  min_span, span;
857  | 
858  |  
859  |   ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
860  |             "rx_asc_search: mode %d (par %d) for %s", 
861  |             search_mode, par_a, key);
862  |   
863  |   if( (err = RX_get_tree ( &mytree, reg_id, spc_id, fam_id)) != RX_OK ) {
864  |     return err;
865  |   }  
866  |   
867  |   /* 
868  |      the behaviour for a default inetnum (range) query is: 
869  |        do an exact match; 
870  |        if it fails, do an exless match on the encompassing prefix
871  |      for routes(prefixes):
872  |        do an exless match
873  |      
874  |      So if it's the default search mode on an inetnum tree,
875  |      and the key is a range, 
876  |      then an exact search is performed on one of the composing prefixes.
877  | 
878  |      Then the resulting data leaves are checked for exact matching with 
879  |      the range queried for.
880  |      Any dataleaves that do not match are discarded, and if none are left,
881  |      the procedure falls back to searching for the encompassing prefix.
882  |      (calculated in the smart_conv routine).
883  | 
884  |      Whatever way, the EXLESS search on inetnum tree should
885  |      return the shortest range that was found. 
886  |      (or range*s* if of equal length). That is done at the end.
887  |   */
888  |   
889  |   /* EXACT search of a route tree for a composed range makes no sense */
890  | 
891  |   if( fam_id == RX_FAM_RT && search_mode == RX_SRCH_EXACT 
892  |       && IP_rang_a2b(&myrang, key) == IP_OK ) {
893  |     IP_rang_decomp(&myrang, &preflist);
894  |     prefcount = g_list_length(preflist);
895  | 
896  |     if( prefcount > 1 ) {
897  |       // abort search
898  |       return RX_OK;
899  |     }
900  |   }
901  |   
902  |   if( fam_id == RX_FAM_IN ) {
903  |     if (search_mode == RX_SRCH_EXLESS ) { 
904  |       /* save a flag for later, will be needed when processing the results */
905  |       is_exless_inetnum = 1;
906  |     }
907  |     
908  |     if( (search_mode == RX_SRCH_EXLESS || search_mode == RX_SRCH_EXACT) 
909  |         && IP_rang_a2b(&myrang, key) == IP_OK ) {
910  |       /* 
911  |          perform just one exact search on one of the composing prefixes
912  |          - the object must be found if it's in the database 
913  |       */
914  |       
915  |       if (search_mode == RX_SRCH_EXACT ) { 
916  |         /* save a flag for later, will be needed when processing the results */
917  |         is_exact_range_inetnum = 1;
918  |       }
919  |       
920  |       IP_rang_decomp(&myrang, &preflist);
921  |       
922  |       ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
923  |                 "rx_asc_search: quick decomp exact search");
924  |       
925  |       err = RX_bin_search( RX_SRCH_EXACT, 0, 0, mytree, 
926  |                            preflist->data,
927  |                            &datlist, RX_ANS_ALL);
928  |       if( err != RX_OK ) {
929  |         return err;
930  |       }
931  |       
932  |       g_list_foreach(preflist, rx_free_list_element, NULL);
933  |       g_list_free(preflist);
934  |       preflist = NULL;
935  |       
936  |       /* now check the results */
937  |       
938  |       ditem = g_list_first(datlist);
939  |       while( ditem != NULL ) {
940  |         rx_dataleaf_t *lptr = ( (rx_datref_t *)ditem->data)->leafptr;
941  |         
942  |         if( memcmp(&lptr->iprange, &myrang, sizeof(ip_range_t)) == 0) {
943  |           // found! leave it, remove others
944  |           ditem = g_list_next(ditem);
945  |         }
946  |         else {
947  | 
948  |           ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
949  |                     "rx_asc_search: ex/rang/in: range mismatch, discarded");
950  | 
951  |           // mismatch. remove that one and start over (Aarggh!)
952  |           datlist = g_list_remove(datlist, ditem->data);
953  |           wr_free(ditem->data);
954  |           ditem = g_list_first(datlist);
955  |         }
956  |       }
957  |     }
958  |   }
959  | 
960  |   /* now let's see if anything is left */
961  |   
962  |   if( g_list_length(datlist) > 0 ) {
963  | 
964  |     /* YES! we hit an inetnum object. */    
965  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
966  |               "rx_asc_search: exact match found for %s", key);
967  | 
968  |     // do not treat it as exless anymore, it was exact and matched.
969  |     is_exless_inetnum = 0;
970  |   }
971  |   else if( is_exact_range_inetnum != 1 ) {
972  | 
973  |     /* nothing found.
974  | 
975  |        Either it's not a range (and so wasn't even tried)
976  |        or the mode was different from EXLESS and EXACT.
977  |        Any way, we will now perform the search on anything that 
978  |        the query can be converted to.
979  | 
980  |     */
981  | 
982  |     if( ( err = IP_smart_conv(key, 0, (search_mode == RX_SRCH_EXLESS),
983  |                               &preflist, IP_EXPN)) != IP_OK ) {
984  |       return err;
985  |     }
986  |     
987  |     prefcount = g_list_length(preflist);
988  |     
989  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
990  |               "rx_asc_search:  query translated into %d prefix subqueries", 
991  |               prefcount);
992  | 
993  |     // if there is only one prefix, store it for possible use later
994  | 
995  |     testpref = *( (ip_prefix_t *) g_list_first(preflist) -> data);
996  |     
997  |     // now go through the list of prefixes and perform the searches
998  |     // rx_preflist_search deallocates the prefixes after use.
999  |     
1000 |     err = rx_preflist_search (search_mode, par_a, par_b, 
1001 |                               mytree, &preflist, &datlist);
1002 |     if( err != RX_OK ) {
1003 |       return err;
1004 |     }
1005 |   }
1006 | 
1007 |   // find the smallest range span
1008 | 
1009 |   if( is_exless_inetnum == 1 ) { 
1010 |     min_span = 0xffffffff;
1011 | 
1012 |     // go through the list and find the shortest range.    
1013 |     for(ditem = g_list_first(datlist);
1014 |         ditem != NULL;
1015 |         ditem = g_list_next(ditem)) {
1016 |       rx_datref_t *refptr = (rx_datref_t *) (ditem->data);
1017 | 
1018 |       span = IP_rang_span(refptr->leafptr->iprange);
1019 |       
1020 |       if( span < min_span ) {
1021 |         min_span = span;
1022 |       }
1023 |     }
1024 | 
1025 |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
1026 |               "rx_asc_search: minimal span is %d", min_span);
1027 |   }
1028 |   
1029 |   // Add the dataleaf copies to the list of answers.
1030 |   
1031 |   for(ditem = g_list_first(datlist);
1032 |       ditem != NULL;
1033 |       ditem = g_list_next(ditem)) {
1034 |     er_ret_t err;
1035 |     rx_datref_t *refptr = (rx_datref_t *) (ditem->data);
1036 |     rx_datcpy_t *datcpy;
1037 |     void *dataptr;
1038 |     
1039 |     // For exless_inet_range search, discard all 
1040 |     // except the one(s) of the smallest size.    
1041 |     if( is_exless_inetnum == 1 
1042 |         && (span = IP_rang_span(refptr->leafptr->iprange)) != min_span ) {
1043 | 
1044 |       ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
1045 |                 "rx_asc_search: discarded object with span %d", span);
1046 |       continue;
1047 |     }
1048 |     
1049 |     // if this is an EXACT search on inetnums specified with a prefix,
1050 |     // (i.e., one prefix was returned from the conversion)
1051 |     // then check if the range in the object is equivalent to that prefix
1052 | 
1053 |     if( search_mode == RX_SRCH_EXACT 
1054 |         && fam_id == RX_FAM_IN 
1055 |         && prefcount == 1 ) {
1056 |       ip_range_t  testrang;
1057 | 
1058 |       IP_pref_2_rang( &testrang, &testpref );
1059 |       
1060 |       if( memcmp( & refptr->leafptr->iprange, 
1061 |                   &testrang, sizeof(ip_range_t)) != 0) {
1062 | 
1063 |         ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
1064 |                   "rx_asc_search: discarded an object from exact/inetnum/prefix search");
1065 | 
1066 |         continue;
1067 |       }
1068 |     }
1069 |     
1070 | 
1071 |     // OK, so we ACCEPT this result. Copy it.
1072 | 
1073 |     if( (err=wr_calloc( (void **)& datcpy, 1, sizeof(rx_datcpy_t))) != UT_OK) {
1074 |       return err; //    die;
1075 |     }
1076 |     
1077 |     datcpy->leafcpy = *(refptr->leafptr);
1078 |     
1079 |     // copy the immediate data too. Set the ptr.
1080 |     
1081 |     if( (err=wr_calloc( (void **) & dataptr, 1, refptr->leafptr->data_len)) 
1082 |         != UT_OK) {
1083 |       return err; //    die;
1084 |     }
1085 |     memcpy(dataptr, refptr->leafptr->data_ptr, refptr->leafptr->data_len);
1086 |     
1087 |     datcpy->leafcpy.data_ptr = dataptr;
1088 |     
1089 |     *anslist = g_list_prepend(*anslist, datcpy);
1090 |   }    
1091 |   
1092 |  g_list_foreach(datlist, rx_free_list_element, NULL);
1093 |  g_list_free(datlist);
1094 |  
1095 |  return RX_OK;
1096 | 
1097 | }
1098 |