Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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);
} 
Beispiel #4
0
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);
}
Beispiel #5
0
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;
    }
}
Beispiel #6
0
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);
    }
}
Beispiel #7
0
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);
}
Beispiel #8
0
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);
    }
}
Beispiel #9
0
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);
}