Source File modules/rx/rx_search.c

    1    | /***************************************
    2    |   $Revision: 1.20 $
    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  |   GList  *complist = NULL;
    644  |   rx_node_t *curnode;
    645  |   rx_nodcpy_t *curcpy;
    646  |   rx_datref_t *datref;
    647  |   rx_stk_mt     dmode;
    648  |
    649  |   // more specific node search may start from a glue node,
    650  |   // for all others the stack should not contain glues.
    651  |
    652  |   dmode = ( search_mode == RX_SRCH_MORE
    653  |             || search_mode == RX_SRCH_DBLS
    654  |             || search_mode == RX_SRCH_RANG )
    655  |     ? RX_STK_QUERY_ALLNOD
    656  |     : RX_STK_QUERY_NOGLUE;
    657  |
    658  |   rx_build_stack(stack, &stkcnt, tree, prefix, dmode);
    659  |
    660  |   rx_nod_search( search_mode, par_a, par_b, tree, prefix,
    661  |                  stack, stkcnt, &nodlist, 1000);
    662  |
    663  |   ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, "RX_bin_search: processing nodes");
    664  |
    665  |   for( nitem = g_list_first(nodlist);
    666  |        nitem != NULL;
    667  |        nitem = g_list_next(nitem)) {
    668  |
    669  |     resnum++;
    670  |     curcpy = nitem->data;
    671  |
    672  |     /*
    673  |       if memory mode includes RAM:
    674  |       * do not expect copies of nodes in the list received from bin_search.
    675  |       * iterate through data leaves with g_list_nth_data.
    676  |       */
    677  |
    678  |     curnode = curcpy->srcptr;
    679  |
    680  |     //    rx_nod_print( curnode, buf, 1024 );
    681  |
    682  |     maxleaves = g_list_length(curnode->leaves_ptr);
    683  |     //    fprintf(stderr,"###node %d, %d dataleaves attached:", i, maxleaves);
    684  |
    685  |     // iterate through dataleafs attached to this node
    686  |     for(k=0; k<maxleaves; k++) {
    687  |       rx_dataleaf_t *leafptr = g_list_nth_data(curnode->leaves_ptr, k);
    688  |
    689  |       // check the conditions to add the leaf:
    690  |
    691  |       // 1. never add the same leaf twice (can occur for composed inetnums)
    692  |       // 2. never add composed inetnum for exact prefix search
    693  |       //    (but do for exact range search...) - must be solved in upper layer.
    694  |
    695  |       // add only if not yet on the list, i.e if it's composed then check,
    696  |       // otherwise just add
    697  |       if( tree->family == RX_FAM_IN && leafptr->composed == 1 ) {
    698  |         GList *item;
    699  |         int already_there = 0;
    700  |
    701  |         for(item = g_list_first(complist);
    702  |             item != NULL;
    703  |             item = g_list_next(item)) {
    704  |           if( item->data == leafptr ) {
    705  |             already_there = 1;
    706  |           }
    707  |         }
    708  |
    709  |         if( already_there == 1 ) {
    710  |           continue;
    711  |         }
    712  |         else {
    713  |           g_list_prepend(complist, leafptr);
    714  |           // and now safely add the answer
    715  |         }
    716  |       }
    717  |
    718  |       if( wr_calloc( (void **) &datref, sizeof(rx_datref_t), 1) != UT_OK) {
    719  |         die;
    720  |       }
    721  |       datref->leafptr = leafptr;
    722  |
    723  |       *datleaves = g_list_prepend(*datleaves, datref);
    724  |     }
    725  |   }
    726  |
    727  |   g_list_foreach(nodlist, rx_free_list_element, NULL);
    728  |   g_list_free(nodlist);
    729  |
    730  |   // the payload of complist are pointers or (cast) integers included
    731  |   // in the list elements. no need to free them, just the list
    732  |   g_list_free(complist);
    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 |