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