INTERNAL boolean conv_common ( dce_uuid_t *actuid, unsigned32 boot_time, rpc_dg_ccall_p_t *ccall, unsigned32 *st ) { /* * Find the call he's asking about. */ *ccall = rpc__dg_ccallt_lookup(actuid, RPC_C_DG_NO_HINT); /* * No ccall entry will exist if an old duplicate WAY callback request * is received. If there is no ccall entry corresponding to the * incoming activity id then we return an error status code. The * server performing the WAY callback will detect the error and send * a reject packet that will be dropped by the client. */ if (*ccall == NULL) { *st = nca_s_bad_actid; return (false); } /* * If we don't know the boot time yet for this server, stash it away * now. */ if ((*ccall)->c.call_server_boot == 0) { (*ccall)->c.call_server_boot = boot_time; } else { /* * We DO know the boot time. If what the server's supplied boot * time isn't what we think it should be, then we must be getting * called back by a new instance of the server (i.e., which * received a duplicate of an outstanding request of ours). Since * we can't know whether the original instance of the server * executed our call or not, we return failure to the server * to prevent IT from executing call (i.e., for a possible second * time, violating the "at most once" rule). */ if ((*ccall)->c.call_server_boot != boot_time) { *st = nca_s_you_crashed; RPC_DG_CCALL_RELEASE(ccall); return (false); } } *st = rpc_s_ok; return (true); }
INTERNAL void release_cached_ccall ( rpc_dg_binding_client_p_t h ) { if (h->ccall == NULL) return; RPC_DG_CALL_LOCK(&h->ccall->c); rpc__dg_ccall_free_prep(h->ccall); RPC_DG_CCALL_RELEASE(&h->ccall); /* unlocks as a side effect */ }
PRIVATE void conv_are_you_there ( handle_t h ATTRIBUTE_UNUSED, /* Not really */ dce_uuid_t *actuid, unsigned32 boot_time, unsigned32 *st ) { rpc_dg_ccall_p_t ccall; RPC_LOCK_ASSERT(0); if (! conv_common(actuid, boot_time, &ccall, st)) { return; } RPC_DG_CCALL_RELEASE(&ccall); }
INTERNAL boolean32 scall_uncache ( rpc_dg_scall_p_t scall ) { unsigned32 st; boolean b; RPC_TRY_LOCK(&b); if (! b) { RPC_DBG_GPRINTF(("(scall_uncache) couldn't get global lock\n")); return false; } RPC_DG_CALL_LOCK_ASSERT(&scall->c); assert(scall->c.state == rpc_e_dg_cs_idle || scall->c.state == rpc_e_dg_cs_orphan); if (scall->c.is_cbk) { /* * This is a *client side* callback scall; dissociate from our * cbk_ccall if necessary. */ if (scall->cbk_ccall != NULL) { rpc_dg_ccall_p_t ccall = scall->cbk_ccall; assert(ccall->cbk_scall == scall); /* * Acquire the callback ccall lock. Note the locking hierarchy * for this type of call handle pairing is: cbk_ccall, is_cbk scall * (see dg.h). */ RPC_DG_CALL_TRY_LOCK(&ccall->c, &b); if (! b) { RPC_DBG_GPRINTF( ("(scall_uncache) couldn't get cbk_scall->cbk_ccall lock\n")); RPC_UNLOCK(0); return false; } ccall->cbk_start = false; RPC_DG_CCALL_RELEASE(&scall->cbk_ccall); RPC_DG_SCALL_RELEASE_NO_UNLOCK(&ccall->cbk_scall); } } else { /* * This is a normal (server side) scall. */ /* * If this server side scall has been part of a callback back * to the client, free up the cached *server side* callback ccall * resources. */ if (scall->cbk_ccall != NULL) { rpc_dg_ccall_p_t ccall = scall->cbk_ccall; assert(ccall->cbk_scall == scall); /* * Acquire the callback ccall lock. Note the locking hierarchy * for this type of call handle pairing is: scall, is_cbk ccall * (see dg.h). */ RPC_DG_CALL_LOCK(&ccall->c); rpc__dg_ccall_free_prep(ccall); /* * Release the reference the CCALL has to its originating SCALL. */ RPC_DG_SCALL_RELEASE_NO_UNLOCK(&ccall->cbk_scall); /* * Release the reference the SCALL has to the CCALL it used for * the callback. Then call free_handle, which will stop the * timer and release the client binding handles reference to * the CCALL. */ RPC_DG_CCALL_RELEASE(&scall->cbk_ccall); RPC_BINDING_RELEASE((rpc_binding_rep_p_t *) &ccall->h, &st); } /* * Dissociate the scall from its scte if necessary. Presumably, * the only time that the scall won't have a scte is if the call * had been orphaned, though we don't count on that. */ if (scall->scte != NULL) { release_scall_from_scte(scall); /* * Release the SCALL's reference to the SCTE. */ RPC_DG_SCT_RELEASE(&scall->scte); } } /* * Common scall uncache processing. */ RPC_DBG_PRINTF(rpc_e_dbg_general, 3, ("(scall_uncache) Freeing cached SCALL [%s]\n", rpc__dg_act_seq_string(&scall->c.xq.hdr))); /* * Dissociate the scall from the server binding handle if necessary. */ if (scall->h != NULL) { RPC_DG_SCALL_RELEASE_NO_UNLOCK(&scall->h->scall); RPC_BINDING_RELEASE((rpc_binding_rep_p_t *) &scall->h, &st); } /* * Stop the scall's timer and dissociate it from the scall. */ rpc__timer_clear(&scall->c.timer); RPC_DG_SCALL_RELEASE(&scall); RPC_UNLOCK(0); return true; }
PRIVATE void conv_who_are_you_auth ( handle_t h ATTRIBUTE_UNUSED, /* not really */ dce_uuid_t *actuid, unsigned32 boot_time, ndr_byte *in_data, signed32 in_len, signed32 out_max_len, unsigned32 *seq, dce_uuid_t *cas_uuid, ndr_byte *out_data, signed32 *out_len, unsigned32 *st ) { rpc_dg_ccall_p_t ccall; rpc_dg_auth_epv_p_t epv; ndr_byte *save_out_data = out_data; RPC_LOCK_ASSERT(0); if (! conv_common(actuid, boot_time, &ccall, st)) { return; } *cas_uuid = rpc_g_dg_my_cas_uuid; *seq = ccall->c.call_seq; /* * If there's already a credentials buffer associated with this * call handle, free it. We rely on the underlying security code * to do cacheing if appropriate. */ if (ccall->auth_way_info != NULL) { RPC_MEM_FREE(ccall->auth_way_info, RPC_C_MEM_DG_EPAC); ccall->auth_way_info = NULL; ccall->auth_way_info_len = 0; } /* * Make sure that we really have an authenticated call here, * lest we dereference null and blow up the process. */ epv = ccall->c.auth_epv; if (epv == NULL) { *st = rpc_s_binding_has_no_auth; } else { RPC_DG_CALL_UNLOCK(&(ccall->c)); RPC_UNLOCK(0); (*epv->way_handler) (ccall->c.key_info, in_data, in_len, out_max_len, &out_data, out_len, st); RPC_LOCK(0); RPC_DG_CALL_LOCK(&(ccall->c)); if (*out_len > out_max_len) { /* * If the credentials did not fit in the buffer provided, * the WAY handler will have alloced up a buffer big enough * to hold them, and returned a pointer to that storage in * out_data. * * Stash a pointer to this buffer in the call handle, copy * as much of the credentials as will fit in the real response * packet, and return a status that indicates that the caller * needs to fetch the rest of the credentials. */ ccall->auth_way_info = out_data; ccall->auth_way_info_len = *out_len; memcpy(save_out_data, out_data, out_max_len); *out_len = out_max_len; *st = rpc_s_partial_credentials; } } RPC_DG_CCALL_RELEASE(&ccall); }