/* @(#)67    1.12  src/examples/common/bind_util.c, examples.src, os2dce21.dss, 960602a.1 1/11/96 09:54:09 */
/*
 * COMPONENT_NAME:  examples.src
 *
 * FUNCTIONS:
 *
 * ORIGINS: 27
 *
 * (C) COPYRIGHT International Business Machines Corp. 1992, 1993, 1994
 * All Rights Reserved
 * Licensed Materials - Property of IBM
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 */

/*********************************************************************
 *  File      :  bind_util.c                                         *
 *********************************************************************
 *                                                                   *
 *  Functions :  get_binding_begin()                                 *
 *               get_binding_next()                                  *
 *               get_binding_vector()                                *
 *               get_binding_endpoint()                              *
 *               get_binding_done()                                  *
 *                                                                   *
 *  Comments  :  Routines that obtain a binding handle suitable      *
 *               for use in an RPC.  The three major routines are:   *
 *                                                                   *
 *               See the function descriptions in the body of this   *
 *               module for details.                                 *
 *                                                                   *
 *               get_binding_begin()                                 *
 *                                                                   *
 *                 Initializes a binding context that is used        *
 *                 by get_binding_next().                            *
 *                                                                   *
 *               get_binding_next()                                  *
 *                                                                   *
 *                 Returns one binding handle that is ready to use   *
 *                 in an RPC to a server.  Repeated calls are made   *
 *                 to this routine until a valid binding is found,   *
 *                 or no more binding handles are available.         *
 *                                                                   *
 *               get_binding_done()                                  *
 *                                                                   *
 *                 Frees up the binding context.                     *
 *                                                                   *
 *********************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#ifdef IBMOS2
# include <dce/dce_error.h>
# include <os2def.h>
#endif
#include <dce/rpc.h>
#include <util.h>
#include <bind_util.h>


/*
 * Internal (opaque) data structure for maintaining
 * namespace, endpoint map, etc. context across invocations
 * of get_binding_next().
 */
typedef struct binding_ctx_int_t {

    /* in args */
    unsigned_char_t      *entry_name;    /* name service entry point */
    rpc_if_handle_t       if_handle;    /* interface specification */
    uuid_t               *obj_uuid;    /* optional object uuid */
    boolean32             bypass_ns_cache; /* TRUE to bypass local ns cache */
    bind_vec_sort_fn_t    bind_vec_sort_fn; /* optional binding sort fn */

    /* internal (default) constants */
    unsigned32            ns_entry_name_syntax;    /* only one available now */

    /* internal state & variables */
    rpc_binding_vector_t *binding_vector; /* binding vector from ns calls */
    unsigned32            bv_index;    /* current index into binding vec */
    rpc_ns_handle_t       lookup_context; /* ns search criteria, location */
    boolean32             have_lookup_context;
    boolean32             resolve_endpoint; /* robust path through ep map */
    rpc_ep_inq_handle_t   endpoint_context; /* ep map search crit, location */
    boolean32             have_endpoint_context;
    unsigned_char_t      *search_protseq_str; /* current  ep element protseq */
    unsigned_char_t      *search_netaddr_str; /* current ep element netaddr */
} binding_ctx_int_t;


/*********************************************************************
 *  FUNCTION      get_binding_begin()                                *
 *********************************************************************
 *                                                                   *
 *  DESCRIPTION                                                      *
 *    The first of the get_binding_(begin,next,done) sequence used   *
 *    to obtain a compatible rpc binding handle.                     *
 *                                                                   *
 *    This routine initializes the binding context used for the      *
 *    duration of the binding process.                               *
 *                                                                   *
 *    The application must call the get_binding_done routine to      *
 *    free the resources allocated to the binding context.           *
 *                                                                   *
 *  PARAMETERS                                                       *
 *    Input                                                          *
 *      entry_name - The name of the name service entry where the    *
 *        search for compatible bindings begins.                     *
 *                                                                   *
 *      if_handle - The interface specification.                     *
 *                                                                   *
 *      obj_uuid - Optional; used when the application wants to      *
 *        operate on an object maintained by a remote server.        *
 *        Specify NULL or the nil uuid otherwise.                    *
 *                                                                   *
 *      bypass_ns_cache - Provided to instruct the namespace calls   *
 *        to bypass the namespace cache, if one exists.  Initially,  *
 *        this should be set to FALSE.  Should the first sequence of *
 *        calls to these routines fail to find a serviceable         *
 *        binding, the argument may be set to TRUE and the sequence  *
 *        repeated.                                                  *
 *                                                                   *
 *      bind_vec_sort_fn - Optional; the application can provide a   *
 *        function that takes a binding vector as an argument and    *
 *        sorts it based on some criteria, such as local ip address. *
 *        Specify NULL if this is not used.                          *
 *                                                                   *
 *        N.B.  The rpc_ns_lookup_next routine returns the bindings  *
 *              from an rpc entry (in contrast to group members or   *
 *              profile elements of the same priority) in a fixed    *
 *              order.  Consider providing a function to randomly    *
 *              sort the binding vector.                             *
 *                                                                   *
 *    Output                                                         *
 *      binding_context - A binding context obtained from a call to  *
 *        get_binding_bingin, or a previous call to this routine.    *
 *                                                                   *
 *  RETURNS                                                          *
 *      rpc_s_ok (success) or a dce status code.                     *
 *                                                                   *
 *  RELATED INFORMATION                                              *
 *    See the rpc_ns_binding_lookup_*, rpc_mgmt_handle_set_exp_age   *
 *    manpages in the DCE Application Development Guide.             *
 *                                                                   *
 *********************************************************************/

unsigned32
get_binding_begin( entry_name,
           if_handle,
           obj_uuid,
           bypass_ns_cache,
           bind_vec_sort_fn,
           binding_context_p )
char                  *entry_name;
rpc_if_handle_t        if_handle;
uuid_t                *obj_uuid;
boolean32              bypass_ns_cache;
bind_vec_sort_fn_t     bind_vec_sort_fn;
binding_ctx_handle_t  *binding_context_p;
{
    binding_ctx_int_t *bc;

    bc = (binding_ctx_int_t *)malloc( sizeof(binding_ctx_int_t) );

    /*
     *  The error path is not quite true, since runtime is not at fault.
     *  It seemed like a better idea than returning an arbitrary
     *  non-zero value, since the caller of these routines may want
     *  to use dce_error_inq_text to decode errors.
     */
    if( bc == NULL ) return( rpc_s_no_memory );

    /* in args */
    bc->entry_name = ( unsigned_char_t * )malloc( strlen( entry_name ) + 1 );
    if( bc->entry_name == NULL ) return( rpc_s_no_memory );
    strcpy( (char *)bc->entry_name, entry_name);
    bc->if_handle = if_handle;
    bc->obj_uuid = obj_uuid;
    bc->bypass_ns_cache = bypass_ns_cache;
    bc->bind_vec_sort_fn = bind_vec_sort_fn;

    /* out args */
    *binding_context_p = bc;

    /* internal (default) contants */
    bc->ns_entry_name_syntax = rpc_c_ns_syntax_default;

    /* internal state */
    bc->binding_vector = NULL;
    bc->bv_index = 0;
    bc->have_lookup_context = FALSE;
    bc->lookup_context = NULL;
    bc->have_endpoint_context = FALSE;
    bc->endpoint_context = NULL;
    bc->resolve_endpoint = FALSE;

    return(0);

} /* end get_binding_begin() */


/*********************************************************************
 *  FUNCTION      get_binding_next()                                 *
 *********************************************************************
 *                                                                   *
 *  DESCRIPTION                                                      *
 *      Returns a compatible binding handle based on the criteria    *
 *      specified binding context that is passed in. This includes   *
 *      the interface specification and, optionally, an object uuid. *
 *                                                                   *
 *      Starting at the entry point specified in binding context,    *
 *      the namespace is searched using repeated calls to the        *
 *    rpc_ns_lookup_* routines. Thus priorities and heirarchies    *
 *      established via ns profiles and groups are maintained.       *
 *                                                                   *
 *      If bypass_ns_cache is set in the binding context,            *
 *      rpc_ns_mgmt_handle_set_exp_age is called to set the          *
 *      expiration age in the name service context handle to 0.      *
 *      This has the effect of bypassing the local ns cache, if one  *
 *      is present.                                                  *
 *                                                                   *
 *      For each binding vector returned by rpc_ns_lookup_next,      *
 *      the binding sort routine is called, of one was provided.     *
 *      A "fast path" and then a "robust path" is executed.          *
 *                                                                   *
 *      The fast path takes from the vector, in order, each          *
 *      partially bound handle (no endpoint) from the vector, copies *
 *      it, sets the communications timeout of the copy to a minimal *
 *      value and returns the copy.                                  *
 *                                                                   *
 *      The robust path reuses the same vector, but searches the     *
 *      endpoint map pointed at by each handle using the             *
 *      rpc_mgmt_ep_elt_inq_* routines.  For each endpoint map       *
 *      element matching both the original search criteria and the   *
 *      binding information in the handle (protocol sequence and     *
 *      network address), a fully bound copy of the handle is        *
 *      returned.                                                    *
 *                                                                   *
 *      The routine maintains the binding context to keep track of   *
 *    which binding handles have been tried, and which have had    *
 *      endpoints resolved.                                          *
 *                                                                   *
 *      The status "rpc_s_no_more_bindings" is returned when all     *
 *    compatible binding handles have been returned.               *
 *                                                                   *
 *      In either case, a copy of the binding vector element is      *
 *      returned. The application is responsible for calling         *
 *      rpc_binding_free to deallocate this memory.                  *
 *                                                                   *
 *      Optionally, the routine will return the string name of the   *
 *      name-service entry the binding handle was discovered in. The *
 *      application is responsible for calling rpc_string_free to    *
 *      deallocate this memory.                                      *
 *                                                                   *
 *  PARAMETERS                                                       *
 *    Input                                                          *
 *      binding_context - A binding context obtained from a call to  *
 *        get_binding_bingin, or a previous call to this routine.    *
 *                                                                   *
 *    Input/Output                                                   *
 *      binding_ns_entry_name_p - The string name of the name        *
 *        service entry the returned binding was discovered in       *
 *        (success). Supply NULL as an argument to suppress          *
 *        this output.                                               *
 *                                                                   *
 *    Output                                                         *
 *      binding_handle_p - A pointer to a binding handle (success),  *
 *        or NULL.                                                   *
 *                                                                   *
 *  RETURNS                                                          *
 *      rpc_s_ok (success) or a dce status code.                     *
 *                                                                   *
 *  RELATED INFORMATION                                              *
 *    See the rpc_ns_binding_lookup_*, rpc_mgmt_set_com_timeout,     *
 *    rpc_mgmt_ep_element_inq_*, manpages in the DCE Application     *
 *    Development Guide.                                             *
 *                                                                   *
 *********************************************************************/

unsigned32
get_binding_next( binding_context,
          binding_ns_entry_name_p,
          binding_handle_p )
binding_ctx_handle_t   binding_context;
unsigned_char_t      **binding_ns_entry_name_p;
rpc_binding_handle_t  *binding_handle_p;
{
    unsigned32         status = rpc_s_ok;
    boolean32          have_binding = FALSE;
    binding_ctx_int_t *bc = binding_context;

    while( !have_binding )
    {
        TRACE("Looking for a binding\n");
        /*
         *  A NULL binding vector indicates we are just starting
         *  the task of looking for a binding handle.  If all of the
         *  handles in the vector have been tried, then call
         *  get_binding_vector(), and try to get a new vector of
         *  additional bindings.
     *
     *  Note once a binding vector is perused on the fastpath,
     *  the index is reset and it is reused on the robust path.
         */

        if ((bc->binding_vector == NULL) ||
            (bc->bv_index >= bc->binding_vector->count))
        {
        status = get_binding_vector( bc );
        if( status != rpc_s_ok ) return( status );
        }

    if( bc->resolve_endpoint == FALSE )
    {
        /*
         *  Fastpath.
         *
         *  The "Fast Path" is to return a binding handle that is only
         *  partially bound, ie it doesn't contain an endpoint.  The
         *  RPC runtime will resolve it (connection-oriented) or rpcd
         *  will forward it (connection-less).
         *
         *  Return a copy of a binding handle.  This way, we can free
         *  the binding vector without worry that the caller has any
         *  references to actual binding vector data. Note that the
         *  binding vector index is incremented after referencing the
         *  binding handle in preparation for the next iteration.
         */

        rpc_binding_copy( bc->binding_vector->binding_h[bc->bv_index],
                  binding_handle_p,
                 &status );

        CHECK_STATUS( "rpc_binding_copy()\n",
              status,
              RETURN );

        /*
         *  If fastpath, set com_timeout to minimal value.
         *
         *  On the fastpath, we don't hang out waiting for
         *  a potentially dead server.
         */

        rpc_mgmt_set_com_timeout( *binding_handle_p,
                              /*rpc_c_binding_min_timeout*/
                              rpc_c_binding_max_timeout,
                              &status );

        CHECK_STATUS( "rpc_mgmt_set_com_timeout()\n",
              status,
              RETURN );

        have_binding = TRUE;
        bc->bv_index++;

    } /* end fastpath */
    else
    {
        /*
         *  The "Robust Path" takes the partially bound handle and,
         *  for each endpoint map element meeting the search criteria
         *  (e.g., interface uuid, object uuid, and binding info) on the
         *  host the handle points to, returns the handle fully bound with
         *  that endpoint.
         *
             *  Resolve the endpoint.  If we encounter an error (e.g.,
         *  rpcd on the target host is down) or the target ep map
         *  is exhausted, we'll try the next handle.
         *
         *  If the current binding vector becomes exhausted, the
         *  containing "have_binding loop will refresh the binding
         *  vector.
         *
         *  Note that a single unresolved binding handle may provide
         *  more than one fully bound handle due to multiple
         *  server instances on the host, or stale ep map elements.
         *
         *  No need to copy binding handle, since get_binding_endpoint
         *  allocates a binding handle.
             */

            TRACE("Binding endpoint resolution will be done\n");

        while( bc->bv_index < bc->binding_vector->count )
        {
        status = get_binding_endpoint( bc,
                           binding_handle_p );
        if( status != rpc_s_ok )
        {
            bc->bv_index++;
        }
        else
        {
            have_binding = TRUE;
            break;
        }
        }

    } /* end robust path */

    } /* while !have_binding */

    if( binding_ns_entry_name_p != NULL )
    {
    int offset = bc->resolve_endpoint == TRUE ? 0 : 1;

    TRACE("Inquiring ns entry name\n");

    rpc_ns_binding_inq_entry_name( bc->binding_vector->
                         binding_h[ bc->bv_index - offset ],
                       bc->ns_entry_name_syntax,
                       binding_ns_entry_name_p,
                      &status );

    CHECK_STATUS( "rpc_ns_binding_inq_entry_name()\n",
                  status,
                  RETURN );
    }

    TRACE("Returning a binding handle\n");
    return( status );

} /* end get_binding_next() */


/*********************************************************************
 *   FUNCTION    get_binding_vector()                                *
 *********************************************************************
 *                                                                   *
 * DESCRIPTION                                                       *
 *     Obtains a vector of binding handles that will be used to make *
 *     RPC's to the server. The constraints on what binding handles  *
 *     will be present in the vector are defined in the binding      *
 *     context, and were set when the call to get_binding_begin()    *
 *     was made.                                                     *
 *                                                                   *
 *     Note that this vector does not necessarily represent all      *
 *     binding handles.  Read the rpc_ns_lookup_next manpage for an  *
 *     explanation.                                                  *
 *                                                                   *
 * RETURNS                                                           *
 *     Status: rpc_s_ok success, or appropriate dce status code.     *
 *                                                                   *
 *********************************************************************/

static unsigned32
get_binding_vector( binding_context )
binding_ctx_handle_t binding_context;
{
    unsigned32         status = 0;
    binding_ctx_int_t *bc = binding_context;

    TRACE("Getting a binding vector\n");
    /*
     *  If we are making the first call to get a binding vector, then
     *  we need to get a context to work with.
     */

    if ( bc->have_lookup_context == FALSE )
    {
        rpc_ns_binding_lookup_begin( bc->ns_entry_name_syntax,
                                     bc->entry_name,
                                     bc->if_handle,
                                     bc->obj_uuid,
                                     rpc_c_binding_max_count_default,
                                     &bc->lookup_context,
                                     &status );

        CHECK_STATUS( "rpc_ns_binding_begin()\n",
                      status,
                      RETURN );

        /*
         *  If the set-expiration-age-to-zero flag is set, then force the
         *  RPC runtime to get fresh namespace data, as opposed to using
         *  info in its local cache.
         */

        if ( bc->bypass_ns_cache != FALSE )
        {
            TRACE("Setting expiration age of local nameservice data to 0\n");
            rpc_ns_mgmt_handle_set_exp_age( bc->lookup_context,
                        0,
                        &status );

            CHECK_STATUS( "rpc_ns_mgmt_handle_set_exp_age()\n",
                          status,
                          RETURN );
        }

        bc->have_lookup_context = TRUE;
    }

    /*
     *  If we are in this routine, and the binding vector is not NULL,
     *  then it means the current binding vector has been exhausted.
     *  If we have not tried this vector of binding handles with resolved
     *  endpoints, then  set the resolve_endpoint flag to TRUE,
     *  reset the binding vector index to zero, and return.
     *  Otherwise,  we are finished with it, so let's free it up.
     *  We will then attempt to get a vector of new bindings from the
     *  namespace.
     *
     *  The motivation for retrying a particular binding vector
     *  immediately, rather than cycling through the remainder of
     *  the namespace is to ensure we do our best to contact higher
     *  priority servers (e.g., priority 1 in ns profile).
     */

    if ( bc->binding_vector != NULL )
    {
        TRACE("Current binding vector has been exhausted\n");
        if ( bc->resolve_endpoint == FALSE )
        {
            TRACE("Reusing existing binding vector\n");
            bc->resolve_endpoint = TRUE;
            bc->bv_index = 0;
            return(0);
        }

    rpc_binding_vector_free( &bc->binding_vector,
                 &status );

    CHECK_STATUS( "rpc_binding_vector_free()\n",
              status,
              RETURN );
    TRACE("Freed the binding vector\n");

    }

    /*
     *  Get a vector of binding handles.
     *  Reset the index.
     */

    rpc_ns_binding_lookup_next( bc->lookup_context,
                                &bc->binding_vector,
                                &status );

    if( status != rpc_s_ok ) return( status );

    TRACE("Obtained a binding vector\n");

    bc->resolve_endpoint = FALSE;
    bc->bv_index = 0;

    /*
     *  Code to sort the binding vector entries, based on some
     *  criteria, such as network address, protocol sequence,
     *  object_uuid, etc...
     */

    if (bc->bind_vec_sort_fn != NULL)
    {
        TRACE("Sorting binding vector\n");
        if ((status =
         bc->bind_vec_sort_fn(
           (rpc_binding_vector_t *)&bc->binding_vector )) != 0)
        {
            PRINT("Error sorting binding vector:  %d\n", status);
            return( status );
        }
    }

    return(0);

} /* end get_binding_vector */


/*********************************************************************
 *   Function    :  get_binding_endpoint()                           *
 *********************************************************************
 *                                                                   *
 *   Description :  Starts at the current point in the vector of     *
 *                  binding handles, and attempts to resolve an      *
 *                  endpoint for that binding handle.  The context   *
 *                  for the endpoint lookup is kept in the binding   *
 *                  context, so that if the fully bound handle       *
 *                  turns out to be invalid, the next endpoint for   *
 *                  that handle can be tried.  When all of the       *
 *                  endpoints for a binding handle have been tried,  *
 *                  this routine returns in error, and the caller    *
 *                  can call it again with a new binding handle.     *
 *                                                                   *
 *   Returns     :  Output:  A fully bound binding handle (ie it     *
 *                           contains an endpoint).                  *
 *                                                                   *
 *                  Status:  (0) success, or appropriate error code. *
 *                                                                   *
 *********************************************************************/

static unsigned32
get_binding_endpoint( binding_context,
              binding_handle_p )
binding_ctx_handle_t  binding_context;
rpc_binding_handle_t *binding_handle_p;
{
    unsigned32         status, rc;
    boolean32          have_endpoint = FALSE;
    rpc_if_id_t        if_id;
    rpc_if_id_t        server_if_id;
    uuid_t             saved_obj_uuid;
    unsigned_char_t              *search_binding_str;
    unsigned_char_t              *new_binding_str;
    unsigned_char_t              *new_protseq_str;
    unsigned_char_t              *new_netaddr_str;
    binding_ctx_int_t *bc = ( binding_ctx_int_t * )binding_context;

    TRACE("Resolving an endpoint\n");

    if (bc->have_endpoint_context == FALSE)
    {
    /*
     *  The rpc_mgmt_ep_elt_inq_*() routines require that the
     *  object uuid in the binding handle be removed.  We will
     *  remove and save it, make the rpc_mgmt_ep_elt_inq_*()
     *  calls, and then put it back in the resolved binding
     *  we get back.
     */

    PRINT_BINDING_HANDLE( bc->binding_vector->binding_h[bc->bv_index] );

    rpc_binding_inq_object( bc->binding_vector->
                    binding_h[bc->bv_index],
                   &saved_obj_uuid,
                   &status );

    CHECK_STATUS( "rpc_binding_inq_object()\n",
              status,
              RETURN );

    rpc_binding_set_object( bc->binding_vector->
                    binding_h[bc->bv_index],
                    NULL,
                   &status );

    CHECK_STATUS( "rpc_binding_set_object()\n",
              status,
              RETURN );

    /*
     *  The search criteria for an endpoint in the endpoint map
     *  are both the object uuid and the interface id.  So we
     *  need to get the interface id from the interface handle,
     *  and use it in the rpc_mgmt_ep_elt_inq_begin() call.
     */

    rpc_if_inq_id( bc->if_handle,
              &server_if_id,
              &status );

    CHECK_STATUS( "rpc_if_inq_id()\n",
              status,
              RETURN );

    /*
     *  By providing an interface id and object uuid, we
     *  filter out endpoints that don't match those criteria.
     */

    rpc_mgmt_ep_elt_inq_begin( bc->binding_vector->
                   binding_h[bc->bv_index],
                   rpc_c_ep_match_by_both,
                  &server_if_id,
                   rpc_c_vers_compatible,
                  &saved_obj_uuid,
                  &bc->endpoint_context,
                  &status );

    CHECK_STATUS( "rpc_mgmt_ep_elt_inq_begin()\n",
              status,
              RETURN );

    /*
     *  Restore the search binding's object uuid.
     */
    rpc_binding_set_object( bc->binding_vector->
                   binding_h[bc->bv_index],
                    &saved_obj_uuid,
                    &status );

    CHECK_STATUS( "rpc_binding_set_object()\n",
              status,
              RETURN );


    bc->have_endpoint_context = TRUE;

    /*
     *  Acquire protseq and netaddr strings from search binding.
     */
    rpc_binding_to_string_binding( bc->binding_vector->
                           binding_h[bc->bv_index],
                       &search_binding_str,
                       &status );

    CHECK_STATUS( "rpc_binding_to_string_binding()\n",
              status,
              RETURN );

    rpc_string_binding_parse( search_binding_str,
                  NULL,
                  &bc->search_protseq_str,
                  &bc->search_netaddr_str,
                  NULL,
                  NULL,
                  &status );

    CHECK_STATUS( "rpc_string_binding_parse()\n",
              status,
              RETURN );

    rpc_string_free( &search_binding_str, &rc );

    } /* if no endpoint context */

    /*
     * Now, loop on rpc_mgmt_ep_elt_inq_next until we find an
     * endpoint with binding info (protseq & netaddr) the same
     * as the search binding.
     */

    while (!have_endpoint)
    {
        /*
         *  All we need back from this rpc call is the binding, with
         *  the endpoint in it.  The interface id and object uuid are
         *  superflous, since we filtered out handles that don't have
         *  matching interface ids and object uuids when we did the
         *  rpc_mgmt_ep_elt_inq_begin().
         */

        rpc_mgmt_ep_elt_inq_next( bc->endpoint_context,
                                  &if_id,
                                  binding_handle_p,
                                  NULL,
                                  NULL,
                                  &status );

        if ( status != rpc_s_ok )
        {
        /*
         *  Assume we've exhausted this ep context.
         *  Free resources specific to this binding
         *  and return.
         */
        rpc_string_free( &bc->search_protseq_str, &rc );
        rpc_string_free( &bc->search_netaddr_str, &rc );

            rpc_mgmt_ep_elt_inq_done( &bc->endpoint_context,
                                      &rc );
            bc->have_endpoint_context = FALSE;

        return(status);
        }

    /*
     *  Compare new binding info to the original binding handle's.
         *  Only return binding with same protseq and netaddr to observe
     *  network preferences, etc., that may have been established
     *  earlier.
     *
     *  Acquire protseq and netaddr strings from new binding
     *  and compare with those of search binding.
     */

    rpc_binding_to_string_binding( *binding_handle_p,
                       &new_binding_str,
                       &status );

    CHECK_STATUS( "rpc_binding_to_string_binding()\n",
              status,
              RETURN );

    rpc_string_binding_parse( new_binding_str,
                  NULL,
                  &new_protseq_str,
                  &new_netaddr_str,
                  NULL,
                  NULL,
                  &status );

    CHECK_STATUS( "rpc_string_binding_parse()\n",
              status,
              RETURN );

    if( strcmp( (const char *)bc->search_protseq_str, (const char *)new_protseq_str )
        ||
        strcmp( (const char *)bc->search_netaddr_str, (const char *)new_netaddr_str ))
    {
        /*
         *  Okay to use non-rpc status here because won't exit
         *  routine.
         */
        status = 1;
        rpc_binding_free( binding_handle_p, &rc );
    }

    rpc_string_free( &new_protseq_str, &rc );
    rpc_string_free( &new_netaddr_str, &rc );
    rpc_string_free( &new_binding_str, &rc );


    if( status == 1 ) continue;

    /*
     *  The binding handle returned from rpc_ep_elt_inq_next()
     *  now has an endpoint, but the object uuid is not present.
     *  We saved it before starting to look for endpoints, so
     *  let's put it back in the new fully bound handle.
     */

    rpc_binding_set_object( *binding_handle_p,
                    &saved_obj_uuid,
                    &status );

    CHECK_STATUS( "rpc_binding_set_object()\n",
              status,
              RETURN );

    PRINT_BINDING_HANDLE( *binding_handle_p );

    have_endpoint = TRUE;

    } /* end while */

    TRACE("Resolved an endpoint\n");

    return(0);

} /* end get_binding_endpoint() */


/*********************************************************************
 *   Function    :  get_binding_done()                               *
 *********************************************************************
 *                                                                   *
 *   Description :  Free up all info associated with the binding     *
 *                  context, and then free the binding context       *
 *                  itself.                                          *
 *                  We don't check all of the status codes, because  *
 *                  sometimes the call to *_done() is not really     *
 *                  appropriate (ie the *_begin/next() were never    *
 *                  called). Also, we really don't care if an error  *
 *                  occurred, because we are shutting down anyways.  *
 *                                                                   *
 *   Returns     :  Status:  (0) success, or appropriate error code. *
 *                                                                   *
 *********************************************************************/

unsigned32
get_binding_done( binding_context_p )
binding_ctx_handle_t *binding_context_p;
{
    unsigned32         status = 0;
    binding_ctx_int_t *bc = ( binding_ctx_int_t * )*binding_context_p;

    if (bc->binding_vector != NULL)
    {
        rpc_binding_vector_free( &bc->binding_vector,
                                 &status );
    }

    if( bc->have_lookup_context == TRUE )
    {
    rpc_ns_binding_lookup_done( &bc->lookup_context,
                    &status );
    }

    if( bc->have_endpoint_context == TRUE )
    {
    rpc_mgmt_ep_elt_inq_done( &bc->endpoint_context,
                  &status );
    }

    /*
     *  Free the binding context resources.
     */
    if( bc != NULL )
    {
    free( bc->entry_name );
    /*
    free( bc->search_protseq_str );
    free( bc->search_netaddr_str );
    */
    free( bc );
    }

    *binding_context_p = NULL;

    return(0);

} /* end get_binding_done() */

/* EOF bind_util.c */


