1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME:
80**
81**      dgsct.c
82**
83**  FACILITY:
84**
85**      Remote Procedure Call (RPC)
86**
87**  ABSTRACT:
88**
89**  DG protocol service routines.  Handle server connection table (SCT).
90**
91**
92*/
93
94#include <dg.h>
95#include <dgsct.h>
96#include <dgslive.h>
97#include <dgscall.h>
98#include <comauth.h>
99
100#include <dce/conv.h>
101
102/* ========================================================================= */
103
104/*
105 * The following structure is used to register the network monitor function
106 * in the timer queue.
107 */
108
109INTERNAL rpc_timer_t sct_timer;
110
111typedef struct {
112    rpc_clock_t timer_timestamp;
113    unsigned32  timer_last_scanned_count;
114    unsigned32  timer_last_freed_count;
115} sct_stats_t;
116
117INTERNAL sct_stats_t sct_stats;
118
119/*
120 * Keep a count of the number of SCT entries, active or not, so we know when we
121 * need to be running the monitor routine.  (We especially don't want to be
122 * running the monitor for clients that would never need it.)
123 *
124 * This variable is only used under the protection of the global lock.
125 */
126
127INTERNAL unsigned32 num_sct_entries = 0;
128
129/*
130 * Run the SCT monitor routine every 5 minutes, clean out entries that are over
131 * 10 minutes old.
132 */
133
134#define SCT_MONITOR_INTERVAL  300
135#define SCTE_TIMEOUT_INTERVAL 600
136
137INTERNAL int rpc_g_dg_sct_mon_int = SCT_MONITOR_INTERVAL;
138INTERNAL int rpc_g_dg_sct_timeout = SCTE_TIMEOUT_INTERVAL;
139
140/* ========================================================================= */
141
142INTERNAL void rpc__dg_sct_timer ( dce_pointer_t );
143
144/* ========================================================================= */
145
146/*
147 * R P C _ _ D G _ S C T _ I N Q _ S C A L L
148 *
149 * Return the connection's locked scall.  If the connection doesn't have
150 * an (accurate) previous / in-progress scall then for all practical
151 * purposes it has no scall; return NULL.
152 *
153 * Also, check the scte's maybe chain if the current request is using
154 * maybe semantics.
155 */
156
157PRIVATE void rpc__dg_sct_inq_scall
158(
159    rpc_dg_sct_elt_p_t scte,
160    rpc_dg_scall_p_t *scallp,
161    rpc_dg_recvq_elt_p_t rqe
162)
163{
164    unsigned32 cur_rqe_seq = rqe->hdrp->seq;
165    rpc_dg_scall_p_t temp;
166
167    RPC_LOCK_ASSERT(0);
168
169    /*
170     * First see if the SCTE's ".scall" field holds a useable scall.
171     */
172    *scallp = scte->scall;
173    if (*scallp != NULL)
174    {
175        RPC_DG_CALL_LOCK(&(*scallp)->c);
176
177        /*
178         * If the current RQE's sequence number is equal to the scall's
179         * sequence number, then this RQE is for the currently running
180         * call associated with the scall.  If the RQE's sequence number
181         * is greater than the scall sequence number, then this RQE is
182         * for a new call.  In either case, we want to use the current
183         * scall for deciding how to dispense with this new RQE.
184         */
185        if (RPC_DG_SEQ_IS_LTE((*scallp)->c.call_seq, cur_rqe_seq))
186        {
187            /*
188             * One last check we need to do is to make sure that the current
189             * .scall does not represent a call that was "backed out" (see
190             * rpc__dg_sct_backout_new_call).  We can tell this by the
191             * sequence number in the SCTE.  If this is the case, don't
192             * return the current scall, since it doesn't represent any
193             * useful state.
194             */
195            if ((*scallp)->c.call_seq != (scte)->high_seq)
196            {
197                RPC_DG_CALL_UNLOCK(&(*scallp)->c);
198                *scallp = NULL;
199                RPC_DBG_PRINTF(rpc_e_dbg_general, 3, (
200                    "(rpc__dg_sct_inq_scall) not using backed out scall\n"));
201
202                return;
203
204            }
205            RPC_DBG_PRINTF(rpc_e_dbg_general, 3, (
206                "(rpc__dg_sct_inq_scall) using cached scall\n"));
207            return;
208        }
209
210        /*
211         * The RQE's sequence number indicates that it is for a call
212         * prior to the one associated with the current .scall.  Unlock
213         * the .scall, and go look for a matching scall on the maybe_chain.
214         */
215        RPC_DG_CALL_UNLOCK(&(*scallp)->c);
216        *scallp = NULL;
217    }
218
219    for (temp = scte->maybe_chain; temp != NULL;
220         temp = (rpc_dg_scall_p_t) temp->c.next)
221    {
222        RPC_DG_CALL_LOCK(&temp->c);
223        if (temp->c.call_seq == cur_rqe_seq)
224        {
225            *scallp = temp;
226            RPC_DBG_PRINTF(rpc_e_dbg_general, 3, (
227                "(rpc__dg_sct_inq_scall) using scall from maybe_chain\n"));
228            return;
229        }
230        RPC_DG_CALL_UNLOCK(&temp->c);
231    }
232
233    /*
234     * return with *scallp set to NULL
235     */
236    RPC_DBG_PRINTF(rpc_e_dbg_general, 3, (
237       "(rpc__dg_sct_inq_scall) didn't find scall\n"));
238}
239
240/*
241 * R P C _ _ D G _ S C T _ N E W _ C A L L
242 *
243 * Setup the connection for the new call to be run.  Return a locked
244 * scall for the new call (create one if necessary).  Make certain that
245 * no one does anything crazy with the connection sequence number. The
246 * scallp arg is [in,out].  If it is NULL a scall will be "materialized",
247 * otherwise, the scall it identifies must already be the one associated
248 * with the connection and it must already be locked (this operation
249 * is paired with INQ_SCALL() above).
250 */
251
252PRIVATE void rpc__dg_sct_new_call
253(
254    rpc_dg_sct_elt_p_t scte,
255    rpc_dg_sock_pool_elt_p_t si,
256    rpc_dg_recvq_elt_p_t rqe,
257    rpc_dg_scall_p_t *scallp
258)
259{
260    boolean  maybe = RPC_DG_HDR_FLAG_IS_SET(rqe->hdrp, RPC_C_DG_PF_MAYBE);
261    unsigned32 cur_rqe_seq = rqe->hdrp->seq;
262
263    RPC_LOCK_ASSERT(0);
264
265    if (*(scallp) == NULL)
266    {
267       /*
268        * Use the SCTE's ".scall" pointer if available.  Maybe calls require
269        * a little different treatment;  if the maybe call was originally sent
270        * prior to the call associated with the ".scall" (i.e. has a lower
271        * sequence number), we want to make sure it doesn't count as an
272        * implicit ACK.  In this case, just alloc up a new scall.
273        */
274
275        *scallp = scte->scall;
276
277        if (*scallp != NULL)
278        {
279            RPC_DG_CALL_LOCK(&(*scallp)->c);
280
281            if (maybe &&
282                RPC_DG_SEQ_IS_LT( cur_rqe_seq, (*scallp)->c.call_seq))
283            {
284                RPC_DG_CALL_UNLOCK(&(*scallp)->c);
285                *scallp = NULL;
286
287                RPC_DBG_PRINTF(rpc_e_dbg_general, 3, (
288                    "(rpc__dg_sct_new_call) handling out-of-order maybe\n"));
289            }
290            else
291            {
292                RPC_DBG_PRINTF(rpc_e_dbg_general, 3, (
293                    "(rpc__dg_sct_new_call) using cached scall\n"));
294            }
295        }
296    }
297    else
298    {
299        /* assert(*scallp == scte->scall || *scallp is on maybe_chain); */
300    }
301    if (*scallp != NULL)
302    {
303        rpc__dg_scall_reinit(*scallp, si, rqe);
304    }
305    else
306    {
307        *scallp = rpc__dg_scall_alloc(scte, si, rqe);
308    }
309    if (*scallp != NULL)
310    {
311        if (RPC_DG_SEQ_IS_LT((*scallp)->c.call_seq, scte->high_seq) && !maybe)
312        {
313            /*
314             * rpc_m_invalid_seqnum
315             * "(%s) Invalid call sequence number"
316             */
317            rpc_dce_svc_printf (
318                __FILE__, __LINE__,
319                "%s",
320                rpc_svc_general,
321                svc_c_sev_fatal | svc_c_action_abort,
322                rpc_m_invalid_seqnum,
323                "rpc__dg_sct_new_call" );
324        }
325        if (!maybe || RPC_DG_SEQ_IS_LT(scte->high_seq, (*scallp)->c.call_seq))
326            scte->high_seq = (*scallp)->c.call_seq;
327    }
328}
329
330/*
331 * R P C _ _ D G _ S C T _ B A C K O U T _ N E W _ C A L L
332 *
333 * A previously "tentatively accepted" RPC has now been unaccepted due
334 * to some server resource constraints.  Backup the connection's notion
335 * of the highest sequence to ensure that a (possible) subsequent
336 * retransmission will be allowed to retry.
337 *
338 * Don't EVER use this if the call has actually been run!
339 */
340
341PRIVATE void rpc__dg_sct_backout_new_call
342(
343    rpc_dg_sct_elt_p_t scte,
344    unsigned32 seq
345)
346{
347    RPC_LOCK_ASSERT(0);
348
349    if (scte != NULL && seq == (scte)->high_seq)
350    {
351        scte->high_seq--;
352    }
353}
354
355/*
356 * R P C _ _ D G _ S C T _ L O O K U P
357 *
358 * Lookup the connection (SCTE) in the server connection table using
359 * the provided actid and probe hint (ahint).  Return the scte or NULL
360 * if no matching entry is found.
361 *
362 * This increments the SCTE reference count (if one is found), making
363 * it unavailable for GCing - it should be released when the reference
364 * is no longer needed.
365 */
366
367PRIVATE rpc_dg_sct_elt_p_t rpc__dg_sct_lookup
368(
369    uuid_p_t actid,
370    unsigned32 probe_hint
371)
372{
373    rpc_dg_sct_elt_p_t scte;
374    unsigned16 probe;
375    boolean once = false;
376    unsigned32 st;
377
378    RPC_LOCK_ASSERT(0);
379
380    /*
381     * Determine the hash chain to use
382     */
383
384    if (probe_hint == RPC_C_DG_NO_HINT || probe_hint >= RPC_DG_SCT_SIZE)
385        probe = rpc__dg_uuid_hash(actid) % RPC_DG_SCT_SIZE;
386    else
387        probe = probe_hint;
388
389    /*
390     * Scan down the probe chain, reserve and return a matching SCTE.
391     */
392
393RETRY:
394    scte = rpc_g_dg_sct[probe];
395
396    while (scte != NULL)
397    {
398        if (UUID_EQ(*actid, scte->actid, &st))
399        {
400            RPC_DG_SCT_REFERENCE(scte);
401            return(scte);
402        }
403        else
404            scte = scte->next;
405    }
406
407    /*
408     * No matching entry found.  If we used the provided hint, try
409     * recomputing the probe and if this yields a new probe, give it
410     * one more try.
411     */
412
413    if (probe == probe_hint && !once)
414    {
415        once = true;
416        probe = rpc__dg_uuid_hash(actid) % RPC_DG_SCT_SIZE;
417        if (probe != probe_hint)
418            goto RETRY;
419    }
420
421    return(NULL);
422}
423
424/*
425 * R P C _ _ D G _ S C T _ G E T
426 *
427 * Lookup the connection (SCTE) in the server connection table using
428 * the provided actid and probe hint (ahint); create one if necessary
429 * (based on the call seq that is inducing its creation).
430 * Return the SCTE.
431 *
432 * This increments the SCTE reference count, making it unavailable for
433 * GCing - it should be released when the reference is no longer needed.
434 */
435
436PRIVATE rpc_dg_sct_elt_p_t rpc__dg_sct_get
437(
438    uuid_p_t actid,
439    unsigned32 probe_hint,
440    unsigned32 seq
441)
442{
443    rpc_dg_sct_elt_p_t scte;
444    unsigned16 probe;
445
446    /*
447     * Determine the hash chain to use.
448     */
449
450    if (probe_hint == RPC_C_DG_NO_HINT || probe_hint >= RPC_DG_SCT_SIZE)
451        probe = rpc__dg_uuid_hash(actid) % RPC_DG_SCT_SIZE;
452    else
453        probe = probe_hint;
454
455    /*
456     * Use an existing entry if we've got one.
457     */
458
459    scte = rpc__dg_sct_lookup(actid, probe);
460    if (scte != NULL)
461        return(scte);
462
463    /*
464     * Create the new SCTE, reserve it and add it to the head of the list.
465     */
466
467    RPC_MEM_ALLOC(scte, rpc_dg_sct_elt_p_t, sizeof *scte,
468            RPC_C_MEM_DG_SCTE, RPC_C_MEM_NOWAIT);
469    scte->refcnt = 0;
470    scte->high_seq = seq - 1;
471    scte->high_seq_is_way_validated = false;
472    scte->auth_epv = NULL;
473    scte->key_info = NULL;
474    scte->scall = NULL;
475    scte->maybe_chain = NULL;
476    scte->actid = *actid;
477    scte->ahint = probe;
478    scte->timestamp = RPC_CLOCK_SEC(0);
479    scte->client = NULL;
480
481    RPC_LOCK_ASSERT(0);
482
483    /*
484     * Add the new SCTE to the head of the list.
485     */
486
487    scte->next = rpc_g_dg_sct[probe];
488    rpc_g_dg_sct[probe] = scte;
489    RPC_DG_SCT_REFERENCE(scte);     /* This is for the reference from the SCT */
490
491    /*
492     * Increment the count of SCTE's.  If this is the first/only one,
493     * add the SCT monitor to the timer queue to handle aging SCTE's.
494     */
495
496    rpc__server_incr_clients();
497    num_sct_entries++;
498    if (num_sct_entries == 1)
499    {
500        rpc__timer_set(&sct_timer, rpc__dg_sct_timer,
501           (dce_pointer_t) NULL, RPC_CLOCK_SEC(rpc_g_dg_sct_mon_int));
502    }
503
504    /*
505     * Reserve a reference to and return the SCTE.
506     */
507
508    RPC_DG_SCT_REFERENCE(scte);
509
510    return(scte);
511}
512
513/*
514 * R P C _ _ D G _ S C T _ T I M E R
515 *
516 * This routine searches through the SCT for entries that have not been in use
517 * for a certain period of time.  It is run peridically from the timer queue.
518 */
519
520INTERNAL void rpc__dg_sct_timer
521(
522    dce_pointer_t junk ATTRIBUTE_UNUSED
523)
524{
525    rpc_dg_sct_elt_p_t scte, prev_scte;
526    unsigned32 i;
527    int sct_timeout = rpc_g_dg_sct_timeout;
528
529    /*
530     * The SCT is protected by the global lock.
531     */
532
533    RPC_LOCK(0);
534
535    sct_stats.timer_timestamp = rpc__clock_stamp();
536    sct_stats.timer_last_freed_count = 0;
537    sct_stats.timer_last_scanned_count = 0;
538
539    /*
540     * Look in each bucket of hash table...
541     */
542
543    for (i = 0; i < RPC_DG_SCT_SIZE; i++)
544    {
545        scte = rpc_g_dg_sct[i];
546        prev_scte = NULL;
547
548        /*
549         * Scan each chain for scte's that have aged.
550         */
551
552        while (scte != NULL)
553        {
554            sct_stats.timer_last_scanned_count++;
555            /*
556             * If the SCTE reference count is one, then the only reference
557             * is from the SCT itself.  (Any other references would be
558             * from SCALLs.)  So if the count is 1, and we're not
559             * monitoring liveness for client associated with this SCTE,
560             * and the SCTE hasn't been used in a while, release the
561             * SCTE.
562             */
563
564            if (scte->refcnt == 1 &&
565                (scte->client == NULL || scte->client->rundown == NULL) &&
566                rpc__clock_aged(scte->timestamp,
567                                RPC_CLOCK_SEC(sct_timeout)))
568            {
569                sct_stats.timer_last_freed_count++;
570                if (prev_scte == NULL)
571                    rpc_g_dg_sct[i] = scte->next;
572                else
573                    prev_scte->next = scte->next;
574
575                /*
576                 * If the SCTE has reference to some auth_info, free it.
577                 */
578
579                if (scte->key_info)
580                {
581                    RPC_DG_KEY_RELEASE(scte->key_info);
582                    CLOBBER_PTR(scte->key_info);
583                }
584
585                /*
586                 * If the SCTE has a reference to a client handle (ie. was
587                 * monitoring liveness) release that reference.
588                 */
589
590                RPC_DG_CLIENT_RELEASE(scte);
591
592                /*
593                 * Release the actual SCTE.
594                 */
595
596                RPC_MEM_FREE(scte, RPC_C_MEM_DG_SCTE);
597
598                rpc__server_decr_clients();
599                num_sct_entries--;
600
601                scte = (prev_scte == NULL) ? rpc_g_dg_sct[i] : prev_scte->next;
602            }
603            else
604            {
605                prev_scte = scte;
606                scte = scte->next;
607            }
608        }
609    }
610
611    RPC_DBG_PRINTF(rpc_e_dbg_general, 5,
612        ("(sct_timer) Scanned %d Freed %d\n",
613            sct_stats.timer_last_scanned_count,
614            sct_stats.timer_last_freed_count));
615
616    /*
617     * If there are no more SCTE's in the SCT, then remove the monitor
618     * routine from the timer queue.
619     */
620
621    if (num_sct_entries == 0)
622    {
623        RPC_DBG_PRINTF(rpc_e_dbg_general, 2,
624            ("(sct_timer) removing SCT monitor from timer queue\n"));
625
626        rpc__timer_clear(&sct_timer);
627    }
628
629    RPC_UNLOCK(0);
630}
631
632/*
633 * R P C _ _ D G _ S C T _ M A K E _ W A Y _ B I N D I N G
634 *
635 * Make a binding to do a WAY call with.
636 *
637 * All WAY calls *must* be made using a activity id other than our own
638 * (i.e., we must not do a "real callback"; see conv.idl).  To achieve
639 * this, we create a new call handle bound to the client.
640 *
641 * Alloc up a copy of the address before creating the binding handle,
642 * binding_free will expect to be able to free this pointer to the address.
643 */
644
645PRIVATE rpc_binding_handle_t rpc__dg_sct_make_way_binding
646(
647    rpc_dg_sct_elt_p_t scte,
648    unsigned32 *st
649)
650{
651    rpc_addr_p_t way_addr;
652    rpc_dg_binding_client_p_t client_binding;
653    unsigned32 xst;
654
655    RPC_LOCK_ASSERT(0);
656
657    /*
658     * Since the scte doesn't directly contain the info necessary
659     * to create a binding to the client, use the scall.
660     */
661    if (scte->scall == NULL)
662    {
663        RPC_DBG_GPRINTF(("(rpc__dg_sct_make_way_binding) no scall\n"));
664        *st = rpc_s_who_are_you_failed;
665        return (NULL);
666    }
667
668    rpc__naf_addr_copy(scte->scall->c.addr, &way_addr, st);
669
670    client_binding = (rpc_dg_binding_client_p_t) rpc__binding_alloc
671        (false, &uuid_g_nil_uuid, RPC_C_PROTOCOL_ID_NCADG, way_addr, st);
672
673    if (*st != rpc_s_ok)
674    {
675        RPC_DBG_GPRINTF
676            (("(rpc__dg_make_way_binding) Couldn't create handle, st=0x%x\n", *st));
677        rpc__naf_addr_free(&way_addr, &xst);
678        return (NULL);
679    }
680
681    /*
682     * Indicate that this handle was created for making calls on the
683     * conv interface.  The packet rationing code needs to know this
684     * in order to allow the WAY/WAY2 call to inherit the packet
685     * reservation made by the originating scall.
686     */
687
688    client_binding->is_WAY_binding = scte->scall->c.n_resvs;
689
690    /*
691     * Set the com_timeout value for this handle to match the one in use
692     * by the server.
693     */
694    rpc_mgmt_set_com_timeout((rpc_binding_handle_t) client_binding,
695                             rpc_mgmt_inq_server_com_timeout(), st);
696
697    return ((rpc_binding_handle_t) client_binding);
698}
699
700/*
701 * R P C _ _ D G _ S C T _ W A Y _ V A L I D A T E
702 *
703 * Ensure that the connection sequence information is Who-Are-You
704 * Validated.  WAY validated sequence information is necessary to
705 * to ensure that non-idempotent calls have at-most-once semantics.
706 *
707 * Server's interlude to "conv_who_are_you" remote procedure (or the
708 * equivalent local procedure that handles authentication).
709 *
710 * The analogous "who_are_you" processing necessary for non-idempotent
711 * callbacks (from a server manager to the client originating the call,
712 * who needs to validate the callback's seq) is performed elsewhere.
713 */
714
715PRIVATE void rpc__dg_sct_way_validate
716(
717    rpc_dg_sct_elt_p_t scte,
718    unsigned32 force_way_auth,
719    unsigned32 *st
720)
721{
722    rpc_dg_sct_elt_p_t scte_ref;
723    unsigned32 seq;
724    unsigned32 xst;
725    rpc_key_info_p_t key_info;
726    idl_uuid_t cas_uuid;            /* retrieved but ignored here. */
727    rpc_binding_handle_t h;
728
729    /*
730     * The connection must be locked.
731     */
732    RPC_LOCK_ASSERT(0);
733
734    *st = rpc_s_ok;
735
736    /*
737     * for lazy coders...
738     */
739    if (RPC_DG_SCT_IS_WAY_VALIDATED(scte) && ! force_way_auth)
740        return;
741
742    h = rpc__dg_sct_make_way_binding(scte, st);
743
744    if (*st != rpc_s_ok)
745        return;
746
747    key_info = scte->key_info;
748
749    /*
750     * Since we're going to unlock the scte while performing the WAY,
751     * acquire a reference to it to ensure that it doesn't dissappear...
752     */
753    scte_ref = scte;
754    RPC_DG_SCT_REFERENCE(scte_ref);
755
756    RPC_UNLOCK(0);
757
758    /*
759     * It's not entirely clear if the caller of this code will always
760     * (or must) have general cancel delivery disabled.  To be flexible,
761     * try to do the right thing regardless of how we were called.  A
762     * failure in either scenario results is a bad status (in the case
763     * of a cancel, we give the caller the opportunity to do something based
764     * on this knowledge.
765     *
766     * Note our use of "scte->actid" is "safe" even though we don't
767     * hold the scte lock at this time: (a) we do have a 'reference' to the
768     * scte, so it isn't going away and (b) nobody will be changing the
769     * actid.  We could copy this info while holding the lock, but why waste
770     * the time / stack space?
771     */
772    DCETHREAD_TRY
773    {
774        /*
775         * Do either an AUTH-WAY or a plain WAY.  At least one AUTH-WAY
776         * (per call) is required if this is an authenticated call that
777         * we're validating (key_info != NULL).  However, if the SCTE
778         * says that the high seq has been WAY validated, then we know
779         * we must have done the AUTH-WAY (and we're here because the
780         * client simply failed to present a non-zero boot time in the
781         * original request); in that case, just do the plain WAY (unless
782         * we're "forcing" a WAY, which the auth logic might be doing
783         * in some key expiration case).  And clearly if the call is
784         * not authenticated, we just do the plain WAY.
785         */
786
787        if (key_info != NULL && (force_way_auth || ! scte->high_seq_is_way_validated))
788        {
789            rpc_dg_auth_epv_p_t auth_epv = scte->auth_epv;
790
791            RPC_DBG_PRINTF(rpc_e_dbg_general, 3,
792                ("(rpc__dg_sct_way_validate) Doing AUTH who-are-you callback\n"));
793
794            (*auth_epv->way)
795                (key_info, h, &scte->actid, rpc_g_dg_server_boot_time, &seq,
796                 &cas_uuid, st);
797        }
798        else
799        {
800            RPC_DBG_PRINTF(rpc_e_dbg_general, 3,
801                ("(rpc__dg_sct_way_validate) Doing who-are-you callback\n"));
802
803            (*conv_v3_0_c_epv.conv_who_are_you)
804                (h, &scte->actid, rpc_g_dg_server_boot_time, &seq, st);
805        }
806    }
807    DCETHREAD_CATCH (dcethread_interrupt_e)
808    {
809        RPC_DBG_GPRINTF(("(rpc__dg_sct_way_validate) cancel exception while performing callback\n"));
810        *st = rpc_s_call_cancelled;
811    }
812    DCETHREAD_CATCH_ALL(THIS_CATCH)
813    {
814        RPC_DBG_GPRINTF(("(rpc__dg_sct_way_validate) exception while performing callback\n"));
815        *st = rpc_s_who_are_you_failed;
816    }
817    DCETHREAD_ENDTRY
818
819    rpc_binding_free((rpc_binding_handle_t *) &h, &xst);
820
821    /*
822     * Reaquire the connection's lock and release our temporary reference.
823     */
824    RPC_LOCK(0);
825    RPC_DG_SCT_RELEASE(&scte_ref);
826
827    if (*st != rpc_s_ok)
828    {
829        RPC_DBG_GPRINTF
830            (("(rpc__dg_sct_way_validate) who_are_you failed, st=0x%x\n", *st));
831        return;
832    }
833
834    /*
835     * The WAY succeded; update the connection's WAY validated sequence info.
836     *
837     * BE CAREFUL...
838     * Since we've had things unlocked, it is possible the the client
839     * has made a *new* call to us since we actually performed the WAY.
840     * If this has happened our newly determined seq will be less that
841     * what's in the scte; DON'T SET IT BACKWARDS - it is still correct
842     * to set it as way_validated.
843     */
844
845    if (! RPC_DG_SEQ_IS_LT(seq, scte->high_seq))
846    {
847        scte->high_seq = seq;
848    }
849
850    scte->high_seq_is_way_validated = true;
851
852    if (scte->scall == NULL)
853    {
854        RPC_DBG_GPRINTF(("(rpc__dg_sct_way_validate) SCTE's SCALL was NULL\n"));
855    }
856    else
857    {
858        RPC_DG_CALL_LOCK(&scte->scall->c);
859        scte->scall->client_needs_sboot = false;
860        RPC_DG_CALL_UNLOCK(&scte->scall->c);
861    }
862}
863
864/*
865 * R P C _ _ D G _ S C T _ F O R K _ H A N D L E R
866 *
867 * Handle fork related processing for this module.
868 */
869
870PRIVATE void rpc__dg_sct_fork_handler
871(
872    rpc_fork_stage_id_t stage
873)
874{
875    unsigned32 i;
876
877    switch ((int)stage)
878    {
879        case RPC_C_PREFORK:
880            break;
881        case RPC_C_POSTFORK_PARENT:
882            break;
883        case RPC_C_POSTFORK_CHILD:
884            /*
885             * Clear out the Server Connection Table
886             */
887            num_sct_entries = 0;
888            for (i = 0; i < RPC_DG_SCT_SIZE; i++)
889                rpc_g_dg_sct[i] = NULL;
890            break;
891    }
892}
893