PRIVATE void rpc__dg_binding_free ( rpc_binding_rep_p_t *h_, unsigned32 *st ) { /* * The binding and the call binding are public at this point (remember * the call monitor) hence we need to ensure that things remain * orderly. */ RPC_LOCK_ASSERT(0); if ((*h_)->is_server == 0) { release_cached_ccall((rpc_dg_binding_client_p_t) *h_); RPC_MEM_FREE(*h_, RPC_C_MEM_DG_CHAND); } else { RPC_MEM_FREE(*h_, RPC_C_MEM_DG_SHAND); } *h_ = NULL; *st = rpc_s_ok; }
INTERNAL rpc_dg_scall_p_t scall_init ( rpc_dg_scall_p_t scall, rpc_dg_sock_pool_elt_p_t sp, rpc_dg_recvq_elt_p_t rqe ) { /* * Initialize the common call handle fields */ RPC_LOCK_ASSERT(0); rpc__dg_call_init(&scall->c); scall->c.c.is_server = true; scall->c.key_info = NULL; scall->c.auth_epv = NULL; scall->c.c.u.server.cthread.is_queued = false; /* * Initialize the fields of the common call handle header that * are really part of the prototype packet header. */ scall->c.call_server_boot = rpc_g_dg_server_boot_time; /* * Initialize the SCTE reference so that scall_reinit knows * that this is a newly created scall. */ scall->scte = NULL; /* * Initialize the server specific call handle fields */ scall->fwd2_rqe = NULL; scall->h = NULL; /* * use reinit to handle a sizeable chunk of the fields */ RPC_DG_CALL_LOCK(&scall->c); rpc__dg_scall_reinit(scall, sp, rqe); return scall; }
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); }
PRIVATE rpc_dg_scall_p_t rpc__dg_scall_cbk_alloc ( rpc_dg_ccall_p_t ccall, rpc_dg_sock_pool_elt_p_t sp, rpc_dg_recvq_elt_p_t rqe ) { rpc_dg_scall_p_t scall; static rpc_clock_t rpc_c_dg_scall_timer_freq_init = RPC_CLOCK_SEC(1); RPC_LOCK_ASSERT(0); RPC_MEM_ALLOC(scall, rpc_dg_scall_p_t, sizeof *scall, RPC_C_MEM_DG_SCALL, RPC_C_MEM_NOWAIT); /* * Initialize the common SCALL handle fields (and LOCK the SCALL) */ scall_init(scall, sp, rqe); /* * The rest is specific to callback SCALLs. */ scall->c.actid_hash = ccall->c.actid_hash; /* * Setup the CCALL (Logical SCTE) / SCALL cross linkage. */ scall->cbk_ccall = ccall; RPC_DG_CALL_REFERENCE(&ccall->c); ccall->cbk_scall = scall; RPC_DG_CALL_REFERENCE(&scall->c); /* * Initialize the fields of the common call handle header that * are really part of the prototype packet header. */ scall->c.call_actid = ccall->c.call_actid; scall->c.call_ahint = RPC_C_DG_NO_HINT; scall->c.is_cbk = true; { unsigned32 our_min ATTRIBUTE_UNUSED; /* * This is essentially a turnaround. The client, which is waiting * for a response, becomes the receiver. * * We inherit high_rcv_frag_size and snd_frag_size from the original * scall. */ scall->c.rq.high_rcv_frag_size = ccall->c.rq.high_rcv_frag_size; scall->c.xq.snd_frag_size = ccall->c.xq.snd_frag_size; /* * Also we inherit the reservation from the original ccall, which * gives us enough packets for receiving fragments. */ scall->c.n_resvs = ccall->c.n_resvs; } RPC_DG_CALL_SET_TIMER(&scall->c, rpc__dg_scall_timer, rpc_c_dg_scall_timer_freq_init); return(scall); }
PRIVATE void rpc__dg_scall_reinit ( rpc_dg_scall_p_t scall, rpc_dg_sock_pool_elt_p_t sp, rpc_dg_recvq_elt_p_t rqe ) { rpc_dg_pkt_hdr_p_t hdrp = rqe->hdrp; unsigned32 st; boolean maybe = RPC_DG_HDR_FLAG_IS_SET(rqe->hdrp, RPC_C_DG_PF_MAYBE); RPC_LOCK_ASSERT(0); RPC_DG_CALL_LOCK_ASSERT(&scall->c); /* * Re-initialize the common call handle fields */ RPC_DG_CALL_REINIT(&scall->c); scall->c.c.u.server.cancel.accepting = true; scall->c.c.u.server.cancel.queuing = true; scall->c.c.u.server.cancel.had_pending = false; scall->c.c.u.server.cancel.count = 0; /* * Typically, subsequent calls on a given actid will be for the same * naf and network address and received over the same server socket * from the same client socket (netaddr/endpoint), but alas, we can't * count on that... */ /* * Detect naf changes and reinit cached naf-specific info. * * The max_frag_size is really associated with the * more specific "network address / interface" than just the naf * (actually they're really dependent on the even lower level of * path through the network even if the peer address don't change). * However, since the runtime currently manages these as constant * for a particular naf (mostly due to to the inability of system * APIs and/or network transports to provide this dynamic information), * we really only have to reset them if the naf changed (the significance * of this is a "different netaddr" check would be more costly). */ if (scall->c.addr == NULL || rqe->from.rpc_protseq_id != scall->c.addr->rpc_protseq_id) { /* * Update to the current client address. */ rpc__naf_addr_overcopy((rpc_addr_p_t) &rqe->from, &scall->c.addr, &st); /* * Initialize the max_frag_size field for the conversation with this * client. */ RPC_DG_CALL_SET_MAX_FRAG_SIZE(&scall->c, &st); RPC_DBG_PRINTF(rpc_e_dbg_recv, 7, ("(rpc__dg_scall_reinit) Set max fs %u\n", scall->c.xq.max_frag_size)); } else { /* * Update to the (typically unchanged) current client address. * (Only its endpoint may change.) */ rpc__naf_addr_overcopy((rpc_addr_p_t) &rqe->from, &scall->c.addr, &st); } /* * Detect received socket changes and reinit cached socket specific info * (the scall may not yet have a cached sock ref or it may be different * from the current one). */ if (scall->c.sock_ref != sp) { if (scall->c.sock_ref != NULL) rpc__dg_network_sock_release(&scall->c.sock_ref); /* * This reference update is a little tricky. We need to be sure * that the socket is not closed before we get a chance to record * our reference. We can do this safely because we are the * listener thread, and and we know that the listener thread * has a reference to the socket. If the socket had failed, * and we had closed it, we wouldn't be here right now. */ scall->c.sock_ref = sp; rpc__dg_network_sock_reference(sp); /* * Initialize the max_rcv_tsdu and max_snd_tsdu fields * for the conversation with this client. */ rpc__naf_inq_max_tsdu(scall->c.addr->rpc_protseq_id, &scall->c.xq.max_rcv_tsdu, &st); scall->c.xq.max_snd_tsdu = scall->c.xq.max_rcv_tsdu; scall->c.xq.max_rcv_tsdu = MIN(scall->c.xq.max_rcv_tsdu, scall->c.sock_ref->rcvbuf); scall->c.xq.max_snd_tsdu = MIN(scall->c.xq.max_snd_tsdu, scall->c.sock_ref->sndbuf); RPC_DBG_PRINTF(rpc_e_dbg_recv, 7, ("(rpc__dg_scall_reinit) Set rcv tsdu %u, snd tsdu %u\n", scall->c.xq.max_rcv_tsdu, scall->c.xq.max_snd_tsdu)); /* * Reinit cached socket-specific information. */ RPC_DG_RBUF_SIZE_TO_WINDOW_SIZE(sp->rcvbuf, sp->is_private, scall->c.xq.max_frag_size, scall->c.rq.window_size); RPC_DBG_PRINTF(rpc_e_dbg_recv, 7, ("(rpc__dg_scall_reinit) Set ws %u, rcvbuf %u, max fs %u\n", scall->c.rq.window_size, sp->rcvbuf, scall->c.xq.max_frag_size)); } if (scall->c.is_cbk && scall->cbk_ccall != NULL) { /* * This is essentially a turnaround. The client, which is waiting * for a response, becomes the receiver. * * We inherit high_rcv_frag_size and snd_frag_size from the original * ccall. * * Note: If this is the initial allocation of the callback scall, * is_cbk is still false. rpc__dg_scall_cbk_alloc() will handle that * case. */ scall->c.rq.high_rcv_frag_size = scall->cbk_ccall->c.rq.high_rcv_frag_size; scall->c.xq.snd_frag_size = scall->cbk_ccall->c.xq.snd_frag_size; /* * Also we inherit the reservation from the original ccall, which * gives us enough packets for receiving fragments. */ scall->c.n_resvs = scall->cbk_ccall->c.n_resvs; } RPC_DBG_PRINTF(rpc_e_dbg_xmit, 6, ("(rpc__dg_scall_reinit) Set snd fs %lu, high rcv fs %lu\n", scall->c.xq.snd_frag_size, scall->c.rq.high_rcv_frag_size)); /* * Re-initialize the fields of the common call handle header that * are really part of the prototype packet header. */ scall->c.call_seq = hdrp->seq; scall->c.high_seq = hdrp->seq; scall->c.call_if_id = hdrp->if_id; scall->c.call_if_vers = hdrp->if_vers; scall->c.call_ihint = hdrp->ihint; scall->c.call_opnum = hdrp->opnum; scall->c.call_object = hdrp->object; /* * Re-initialize some remaining fields in the prototype packet header. * Note: the ptype may not currently be "response" due to the way * we handle fault pkts. */ scall->c.xq.base_flags = 0; scall->c.xq.base_flags2 = 0; scall->c.xq.hdr.flags = 0; scall->c.xq.hdr.flags2 = 0; RPC_DG_HDR_SET_PTYPE(&scall->c.xq.hdr, RPC_C_DG_PT_RESPONSE); /* * Reset the call state to the initial state. */ RPC_DG_CALL_SET_STATE(&scall->c, rpc_e_dg_cs_init); scall->call_is_setup = false; scall->has_call_executor_ref = false; scall->call_is_queued = false; scall->client_needs_sboot = false; /* Really "unknown" */ scall->c.com_timeout_knob = rpc_mgmt_inq_server_com_timeout(); /* * If the new call uses maybe semantics, and this scall is already * associated with an SCTE, then we may need to reposition this scall * within the SCTE. */ if (maybe && scall->scte != NULL && scall->scte->scall == scall) { rpc_dg_sct_elt_p_t scte = scall->scte; RPC_DBG_PRINTF(rpc_e_dbg_general, 3, ( "(rpc__dg_scall_reinit) using cached scall for maybe call\n")); scall->c.next = (rpc_dg_call_p_t) scte->maybe_chain; scte->maybe_chain = scall; scte->scall = NULL; } }
PRIVATE void rpc__dg_scall_orphan_call ( rpc_dg_scall_p_t scall ) { RPC_LOCK_ASSERT(0); RPC_DG_CALL_LOCK_ASSERT(&scall->c); /* * If for some reason we're already in the orphan state, just return. */ if (scall->c.state == rpc_e_dg_cs_orphan) { RPC_DBG_GPRINTF(("(rpc__dg_scall_orphan_call) already orphaned\n")); return; } RPC_DBG_GPRINTF(("(rpc__dg_scall_orphan) Orphaning%s scall [%s]\n", (scall->c.is_cbk ? " callback" : ""), rpc__dg_act_seq_string(&scall->c.xq.hdr))); if (! scall->c.is_cbk) { /* * For a normal scall, Disassociate the selected call from the active * call list (SCT). * * Note that the reference count of the SCALL is not decremented * to zero in this routine. We do not want to free the SCALL until * its associated resources have been freed in either the SCALL timer * or upon return from the RPC manager routine. */ assert(scall->scte != NULL); release_scall_from_scte(scall); RPC_DG_SCT_RELEASE(&scall->scte); } else { /* * For a callback scall we really can't do any dissociation to * attempt to get a new call to run before the current callback * completes. There is only one (client) thread that can run * a new callback and it will be busy until the current one * completes. If we did dissociate the cbk_scall from it's ccall, * then the listener (do_cbk_request) would try to create and * run another callback scall even though we're still running * one; ouch! * * So the best we can do is try to encourage the callback to * terminate. The end result is that a callback scall may exist * in the orphaned state for a while AND be locatable by the * listener while in this state, so the listener better be prepared * to deal with this state. */ } /* * Common callback and normal scall processing. */ /* * Now, set the call's state to "orphaned". It's resources will * be cleaned up by the SCALL timer thread when all outstanding * references are released. There may be up to three references * to the SCALL at this point; the timer thread, the state reference * (not held in the "idle" state, and possibly an orphan call * executor reference. An orphan call executor reference is freed * when the call returns from the server stub in execute_call(). * All other references are freed by the scall timer. */ RPC_DG_CALL_SET_STATE(&scall->c, rpc_e_dg_cs_orphan); /* * Wake the call executor thread to tell it that it is time to go * away. The signal_failure() routine will set the orphaned thread's * common call handle (CALL) status to an error condition so that * an error will be detected when the executor thread waiting on * the transmit or receive queue wakes up. This will guarantee that * the pending recv and xmit operations are not performed and the * call executor thread will exit to the stubs for fault processing. */ rpc__dg_call_signal_failure(&scall->c, rpc_s_call_orphaned); /* * Let's lose this call. Is it queued? */ if (rpc__cthread_dequeue(&scall->c.c)) { /* * Call is queued dequeueit and release the logical reference * from the call executor (actually from the call executor * queue). */ assert(scall->c.refcnt >= 2); rpc__dg_pkt_cancel_reservation(&scall->c); scall->has_call_executor_ref = false; RPC_DG_SCALL_RELEASE_NO_UNLOCK(&scall); } else { /* * Call not queued--cancel the call execution thread in an attempt * to achieve a more timely manager termination. The posting * of this cancel must follow the behavour for normal cancels * (i.e. it must not be posted 'too soon' or 'too late' and it * must be flushed at the end of the call). Note, we're using * this even for cbk_scall's (i.e. the original client thread) * - see rpc__dg_call_local_cancel() for more info; kinda scary, * huh? */ rpc__cthread_cancel(&scall->c.c); } }
PRIVATE rpc_dg_scall_p_t rpc__dg_scall_alloc ( rpc_dg_sct_elt_p_t scte, rpc_dg_sock_pool_elt_p_t sp, rpc_dg_recvq_elt_p_t rqe ) { rpc_dg_scall_p_t scall; unsigned32 st ATTRIBUTE_UNUSED; static rpc_clock_t rpc_c_dg_scall_timer_freq_init = RPC_CLOCK_SEC(1); boolean maybe = RPC_DG_HDR_FLAG_IS_SET(rqe->hdrp, RPC_C_DG_PF_MAYBE); RPC_LOCK_ASSERT(0); RPC_MEM_ALLOC(scall, rpc_dg_scall_p_t, sizeof *scall, RPC_C_MEM_DG_SCALL, RPC_C_MEM_NOWAIT); /* * Initialize the common SCALL handle fields (and LOCK the SCALL) */ scall_init(scall, sp, rqe); /* * The rest is specific to normal (non-callback) SCALLs. */ scall->cbk_ccall = NULL; scall->c.actid_hash = rpc__dg_uuid_hash(&scte->actid); /* * Initialize the server specific call handle fields */ /* * Setup the SCTE / SCALL cross linkage. */ RPC_DG_SCT_REFERENCE(scte); scall->scte = scte; RPC_DG_CALL_REFERENCE(&scall->c); if (! maybe) scte->scall = scall; else { RPC_DBG_PRINTF(rpc_e_dbg_general, 3, ( "(rpc__dg_scall_alloc) putting call on maybe chain\n")); scall->c.next = (rpc_dg_call_p_t) scte->maybe_chain; scte->maybe_chain = scall; } /* * Initialize the fields of the common call handle header that * are really part of the prototype packet header. */ scall->c.call_actid = scte->actid; scall->c.call_ahint = scte->ahint; scall->c.is_cbk = false; /* * Copy over authentication/keying information. */ scall->c.auth_epv = scte->auth_epv; scall->c.key_info = scte->key_info; if (scall->c.key_info != NULL) RPC_DG_KEY_REFERENCE(scall->c.key_info); RPC_DG_CALL_SET_TIMER(&scall->c, rpc__dg_scall_timer, rpc_c_dg_scall_timer_freq_init); return(scall); }
PRIVATE void rpc__dg_plog_dump(void) { unsigned16 i; unsigned32 st; static char *lossy_action = "d?r "; /* (0)drop, (1)?, (2)rexmit, (3)normal */ RPC_LOCK_ASSERT(0); if (rpc_g_dg_pkt_log == NULL) { RPC_DBG_PRINTF(rpc_e_dbg_dg_pktlog, 1, ("rpc__dg_plog_dump called, but DG Pkt Logging never enabled\n") ); return; } RPC_DBG_PRINTF(rpc_e_dbg_dg_pktlog, 1, ("tstamp ver ptyp f1 f2 seq/fnum/sn ihnt ahnt len interface/ver/op activity sboot object drep at\n") ); RPC_DBG_PRINTF(rpc_e_dbg_dg_pktlog, 1, ("---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n") ); for (i = 0; i < RPC_C_DG_PKT_LOG_SIZE; i++) { pktlog_elt_p_t p = &rpc_g_dg_pkt_log[i]; unsigned_char_p_t obj, iface, act; rpc_dg_pkt_hdr_p_t hdrp; #ifndef MISPACKED_HDR hdrp = (rpc_dg_pkt_hdr_p_t) &p->hdr; #else hdrp = converted local rep of raw hdr #endif if (p->timestamp == 0) break; dce_uuid_to_string(&hdrp->object, &obj, &st); dce_uuid_to_string(&hdrp->if_id, &iface, &st); dce_uuid_to_string(&hdrp->actuid, &act, &st); RPC_DBG_PRINTF(rpc_e_dbg_dg_pktlog, 1, ("%08x %c%c%1u %-4.4s %02x %02x %08x/%04x/%04x %04x %04x %4d %s/%02u/%03u %s %9u %s %02x%02x%02x %02x\n", p->timestamp, (hdrp->_rpc_vers & 0x80) ? 'R' : lossy_action[p->lossy_action], ((i + 1) % RPC_C_DG_PKT_LOG_SIZE == pkt_log_index) ? '*' : ' ', hdrp->_rpc_vers & 0x7f, rpc__dg_pkt_name(RPC_DG_HDR_INQ_PTYPE(hdrp)), hdrp->flags, hdrp->flags2, hdrp->seq, hdrp->fragnum, hdrp->serial_hi << 8 | hdrp->serial_lo, hdrp->ihint, hdrp->ahint, hdrp->len, iface, hdrp->if_vers, hdrp->opnum, act, hdrp->server_boot, obj, hdrp->drep[0], hdrp->drep[1], hdrp->drep[2], hdrp->auth_proto) ); rpc_string_free(&obj, &st); rpc_string_free(&act, &st); rpc_string_free(&iface, &st); } }
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); }