1/*	$NetBSD: svc_dg.c,v 1.12 2008/04/25 17:44:44 christos Exp $	*/
2
3/*
4 * Copyright (c) 2010, Oracle America, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *     * Redistributions of source code must retain the above copyright
11 *       notice, this list of conditions and the following disclaimer.
12 *     * Redistributions in binary form must reproduce the above
13 *       copyright notice, this list of conditions and the following
14 *       disclaimer in the documentation and/or other materials
15 *       provided with the distribution.
16 *     * Neither the name of the "Oracle America, Inc." nor the names of its
17 *       contributors may be used to endorse or promote products derived
18 *       from this software without specific prior written permission.
19 *
20 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
36 */
37
38/* #ident	"@(#)svc_dg.c	1.17	94/04/24 SMI" */
39
40
41/*
42 * svc_dg.c, Server side for connectionless RPC.
43 *
44 * Does some caching in the hopes of achieving execute-at-most-once semantics.
45 */
46
47#include <sys/cdefs.h>
48#if defined(LIBC_SCCS) && !defined(lint)
49__RCSID("$NetBSD: svc_dg.c,v 1.12 2008/04/25 17:44:44 christos Exp $");
50#endif
51
52#include "namespace.h"
53#include "reentrant.h"
54#include <sys/types.h>
55#include <sys/socket.h>
56#include <rpc/rpc.h>
57#include <assert.h>
58#include <errno.h>
59#include <unistd.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#ifdef RPC_CACHE_DEBUG
64#include <netconfig.h>
65#include <netdir.h>
66#endif
67#include <err.h>
68
69#include "rpc_internal.h"
70#include "svc_dg.h"
71
72#define	su_data(xprt)	((struct svc_dg_data *)(xprt->xp_p2))
73#define	rpc_buffer(xprt) ((xprt)->xp_p1)
74
75#ifdef __weak_alias
76__weak_alias(svc_dg_create,_svc_dg_create)
77#endif
78
79#ifndef MAX
80#define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
81#endif
82
83static void svc_dg_ops __P((SVCXPRT *));
84static enum xprt_stat svc_dg_stat __P((SVCXPRT *));
85static bool_t svc_dg_recv __P((SVCXPRT *, struct rpc_msg *));
86static bool_t svc_dg_reply __P((SVCXPRT *, struct rpc_msg *));
87static bool_t svc_dg_getargs __P((SVCXPRT *, xdrproc_t, caddr_t));
88static bool_t svc_dg_freeargs __P((SVCXPRT *, xdrproc_t, caddr_t));
89static void svc_dg_destroy __P((SVCXPRT *));
90static bool_t svc_dg_control __P((SVCXPRT *, const u_int, void *));
91static int cache_get __P((SVCXPRT *, struct rpc_msg *, char **, size_t *));
92static void cache_set __P((SVCXPRT *, size_t));
93
94/*
95 * Usage:
96 *	xprt = svc_dg_create(sock, sendsize, recvsize);
97 * Does other connectionless specific initializations.
98 * Once *xprt is initialized, it is registered.
99 * see (svc.h, xprt_register). If recvsize or sendsize are 0 suitable
100 * system defaults are chosen.
101 * The routines returns NULL if a problem occurred.
102 */
103static const char svc_dg_str[] = "svc_dg_create: %s";
104static const char svc_dg_err1[] = "could not get transport information";
105static const char svc_dg_err2[] = " transport does not support data transfer";
106static const char __no_mem_str[] = "out of memory";
107
108SVCXPRT *
109svc_dg_create(fd, sendsize, recvsize)
110	int fd;
111	u_int sendsize;
112	u_int recvsize;
113{
114	SVCXPRT *xprt;
115	struct svc_dg_data *su = NULL;
116	struct __rpc_sockinfo si;
117	struct sockaddr_storage ss;
118	socklen_t slen;
119
120	if (!__rpc_fd2sockinfo(fd, &si)) {
121		warnx(svc_dg_str, svc_dg_err1);
122		return (NULL);
123	}
124	/*
125	 * Find the receive and the send size
126	 */
127	sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize);
128	recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize);
129	if ((sendsize == 0) || (recvsize == 0)) {
130		warnx(svc_dg_str, svc_dg_err2);
131		return (NULL);
132	}
133
134	xprt = mem_alloc(sizeof (SVCXPRT));
135	if (xprt == NULL)
136		goto freedata;
137	memset(xprt, 0, sizeof (SVCXPRT));
138
139	su = mem_alloc(sizeof (*su));
140	if (su == NULL)
141		goto freedata;
142	su->su_iosz = ((MAX(sendsize, recvsize) + 3) / 4) * 4;
143	if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL)
144		goto freedata;
145	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
146		XDR_DECODE);
147	su->su_cache = NULL;
148	xprt->xp_fd = fd;
149	xprt->xp_p2 = (caddr_t)(void *)su;
150	xprt->xp_verf.oa_base = su->su_verfbody;
151	svc_dg_ops(xprt);
152	xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);
153
154	slen = sizeof ss;
155	if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0)
156		goto freedata;
157	xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
158	xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage);
159	xprt->xp_ltaddr.len = slen;
160	memcpy(xprt->xp_ltaddr.buf, &ss, slen);
161
162	xprt_register(xprt);
163	return (xprt);
164freedata:
165	(void) warnx(svc_dg_str, __no_mem_str);
166	if (xprt) {
167		if (su)
168			(void) mem_free(su, sizeof (*su));
169		(void) mem_free(xprt, sizeof (SVCXPRT));
170	}
171	return (NULL);
172}
173
174/*ARGSUSED*/
175static enum xprt_stat
176svc_dg_stat(xprt)
177	SVCXPRT *xprt;
178{
179	return (XPRT_IDLE);
180}
181
182static bool_t
183svc_dg_recv(xprt, msg)
184	SVCXPRT *xprt;
185	struct rpc_msg *msg;
186{
187	struct svc_dg_data *su;
188	XDR *xdrs;
189	char *reply;
190	struct sockaddr_storage ss;
191	socklen_t alen;
192	size_t replylen;
193	ssize_t rlen;
194
195	_DIAGASSERT(xprt != NULL);
196	_DIAGASSERT(msg != NULL);
197
198	su = su_data(xprt);
199	xdrs = &(su->su_xdrs);
200
201again:
202	alen = sizeof (struct sockaddr_storage);
203	rlen = recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, 0,
204	    (struct sockaddr *)(void *)&ss, &alen);
205	if (rlen == -1 && errno == EINTR)
206		goto again;
207	if (rlen == -1 || (rlen < (ssize_t)(4 * sizeof (u_int32_t))))
208		return (FALSE);
209	if (xprt->xp_rtaddr.len < alen) {
210		if (xprt->xp_rtaddr.len != 0)
211			mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.len);
212		xprt->xp_rtaddr.buf = mem_alloc(alen);
213		xprt->xp_rtaddr.len = alen;
214	}
215	memcpy(xprt->xp_rtaddr.buf, &ss, alen);
216#ifdef PORTMAP
217	if (ss.ss_family == AF_INET) {
218		xprt->xp_raddr = *(struct sockaddr_in *)xprt->xp_rtaddr.buf;
219		xprt->xp_addrlen = sizeof (struct sockaddr_in);
220	}
221#endif
222	xdrs->x_op = XDR_DECODE;
223	XDR_SETPOS(xdrs, 0);
224	if (! xdr_callmsg(xdrs, msg)) {
225		return (FALSE);
226	}
227	su->su_xid = msg->rm_xid;
228	if (su->su_cache != NULL) {
229		if (cache_get(xprt, msg, &reply, &replylen)) {
230			(void)sendto(xprt->xp_fd, reply, replylen, 0,
231			    (struct sockaddr *)(void *)&ss, alen);
232			return (FALSE);
233		}
234	}
235	return (TRUE);
236}
237
238static bool_t
239svc_dg_reply(xprt, msg)
240	SVCXPRT *xprt;
241	struct rpc_msg *msg;
242{
243	struct svc_dg_data *su;
244	XDR *xdrs;
245	bool_t stat = FALSE;
246	size_t slen;
247
248	_DIAGASSERT(xprt != NULL);
249	_DIAGASSERT(msg != NULL);
250
251	su = su_data(xprt);
252	xdrs = &(su->su_xdrs);
253
254	xdrs->x_op = XDR_ENCODE;
255	XDR_SETPOS(xdrs, 0);
256	msg->rm_xid = su->su_xid;
257	if (xdr_replymsg(xdrs, msg)) {
258		slen = XDR_GETPOS(xdrs);
259		if (sendto(xprt->xp_fd, rpc_buffer(xprt), slen, 0,
260		    (struct sockaddr *)xprt->xp_rtaddr.buf,
261		    (socklen_t)xprt->xp_rtaddr.len) == (ssize_t) slen) {
262			stat = TRUE;
263			if (su->su_cache)
264				cache_set(xprt, slen);
265		}
266	}
267	return (stat);
268}
269
270static bool_t
271svc_dg_getargs(xprt, xdr_args, args_ptr)
272	SVCXPRT *xprt;
273	xdrproc_t xdr_args;
274	caddr_t args_ptr;
275{
276	return (*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr);
277}
278
279static bool_t
280svc_dg_freeargs(xprt, xdr_args, args_ptr)
281	SVCXPRT *xprt;
282	xdrproc_t xdr_args;
283	caddr_t args_ptr;
284{
285	XDR *xdrs;
286
287	_DIAGASSERT(xprt != NULL);
288
289	xdrs = &(su_data(xprt)->su_xdrs);
290	xdrs->x_op = XDR_FREE;
291	return (*xdr_args)(xdrs, args_ptr);
292}
293
294static void
295svc_dg_destroy(xprt)
296	SVCXPRT *xprt;
297{
298	struct svc_dg_data *su;
299
300	_DIAGASSERT(xprt != NULL);
301
302	su = su_data(xprt);
303
304	xprt_unregister(xprt);
305	if (xprt->xp_fd != -1)
306		(void)close(xprt->xp_fd);
307	XDR_DESTROY(&(su->su_xdrs));
308	(void) mem_free(rpc_buffer(xprt), su->su_iosz);
309	(void) mem_free(su, sizeof (*su));
310	if (xprt->xp_rtaddr.buf)
311		(void) mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
312	if (xprt->xp_ltaddr.buf)
313		(void) mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
314	if (xprt->xp_tp)
315		(void) free(xprt->xp_tp);
316	(void) mem_free(xprt, sizeof (SVCXPRT));
317}
318
319static bool_t
320/*ARGSUSED*/
321svc_dg_control(xprt, rq, in)
322	SVCXPRT *xprt;
323	const u_int	rq;
324	void		*in;
325{
326	return (FALSE);
327}
328
329static void
330svc_dg_ops(xprt)
331	SVCXPRT *xprt;
332{
333	static struct xp_ops ops;
334	static struct xp_ops2 ops2;
335#ifdef _REENTRANT
336	extern mutex_t ops_lock;
337#endif
338
339	_DIAGASSERT(xprt != NULL);
340
341/* VARIABLES PROTECTED BY ops_lock: ops */
342
343	mutex_lock(&ops_lock);
344	if (ops.xp_recv == NULL) {
345		ops.xp_recv = svc_dg_recv;
346		ops.xp_stat = svc_dg_stat;
347		ops.xp_getargs = svc_dg_getargs;
348		ops.xp_reply = svc_dg_reply;
349		ops.xp_freeargs = svc_dg_freeargs;
350		ops.xp_destroy = svc_dg_destroy;
351		ops2.xp_control = svc_dg_control;
352	}
353	xprt->xp_ops = &ops;
354	xprt->xp_ops2 = &ops2;
355	mutex_unlock(&ops_lock);
356}
357
358/*  The CACHING COMPONENT */
359
360/*
361 * Could have been a separate file, but some part of it depends upon the
362 * private structure of the client handle.
363 *
364 * Fifo cache for cl server
365 * Copies pointers to reply buffers into fifo cache
366 * Buffers are sent again if retransmissions are detected.
367 */
368
369#define	SPARSENESS 4	/* 75% sparse */
370
371#define	ALLOC(type, size)	\
372	mem_alloc((sizeof (type) * (size)))
373
374#define	MEMZERO(addr, type, size)	 \
375	(void) memset((void *) (addr), 0, sizeof (type) * (int) (size))
376
377#define	FREE(addr, type, size)	\
378	mem_free((addr), (sizeof (type) * (size)))
379
380/*
381 * An entry in the cache
382 */
383typedef struct cache_node *cache_ptr;
384struct cache_node {
385	/*
386	 * Index into cache is xid, proc, vers, prog and address
387	 */
388	u_int32_t cache_xid;
389	rpcproc_t cache_proc;
390	rpcvers_t cache_vers;
391	rpcprog_t cache_prog;
392	struct netbuf cache_addr;
393	/*
394	 * The cached reply and length
395	 */
396	char *cache_reply;
397	size_t cache_replylen;
398	/*
399	 * Next node on the list, if there is a collision
400	 */
401	cache_ptr cache_next;
402};
403
404/*
405 * The entire cache
406 */
407struct cl_cache {
408	u_int uc_size;		/* size of cache */
409	cache_ptr *uc_entries;	/* hash table of entries in cache */
410	cache_ptr *uc_fifo;	/* fifo list of entries in cache */
411	u_int uc_nextvictim;	/* points to next victim in fifo list */
412	rpcprog_t uc_prog;	/* saved program number */
413	rpcvers_t uc_vers;	/* saved version number */
414	rpcproc_t uc_proc;	/* saved procedure number */
415};
416
417
418/*
419 * the hashing function
420 */
421#define	CACHE_LOC(transp, xid)	\
422	(xid % (SPARSENESS * ((struct cl_cache *) \
423		su_data(transp)->su_cache)->uc_size))
424
425#ifdef _REENTRANT
426extern mutex_t	dupreq_lock;
427#endif
428
429/*
430 * Enable use of the cache. Returns 1 on success, 0 on failure.
431 * Note: there is no disable.
432 */
433static const char cache_enable_str[] = "svc_enablecache: %s %s";
434static const char alloc_err[] = "could not allocate cache ";
435static const char enable_err[] = "cache already enabled";
436
437int
438svc_dg_enablecache(transp, size)
439	SVCXPRT *transp;
440	u_int size;
441{
442	struct svc_dg_data *su;
443	struct cl_cache *uc;
444
445	_DIAGASSERT(transp != NULL);
446
447	su = su_data(transp);
448
449	mutex_lock(&dupreq_lock);
450	if (su->su_cache != NULL) {
451		(void) warnx(cache_enable_str, enable_err, " ");
452		mutex_unlock(&dupreq_lock);
453		return (0);
454	}
455	uc = ALLOC(struct cl_cache, 1);
456	if (uc == NULL) {
457		warnx(cache_enable_str, alloc_err, " ");
458		mutex_unlock(&dupreq_lock);
459		return (0);
460	}
461	uc->uc_size = size;
462	uc->uc_nextvictim = 0;
463	uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
464	if (uc->uc_entries == NULL) {
465		warnx(cache_enable_str, alloc_err, "data");
466		FREE(uc, struct cl_cache, 1);
467		mutex_unlock(&dupreq_lock);
468		return (0);
469	}
470	MEMZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
471	uc->uc_fifo = ALLOC(cache_ptr, size);
472	if (uc->uc_fifo == NULL) {
473		warnx(cache_enable_str, alloc_err, "fifo");
474		FREE(uc->uc_entries, cache_ptr, size * SPARSENESS);
475		FREE(uc, struct cl_cache, 1);
476		mutex_unlock(&dupreq_lock);
477		return (0);
478	}
479	MEMZERO(uc->uc_fifo, cache_ptr, size);
480	su->su_cache = (char *)(void *)uc;
481	mutex_unlock(&dupreq_lock);
482	return (1);
483}
484
485/*
486 * Set an entry in the cache.  It assumes that the uc entry is set from
487 * the earlier call to cache_get() for the same procedure.  This will always
488 * happen because cache_get() is calle by svc_dg_recv and cache_set() is called
489 * by svc_dg_reply().  All this hoopla because the right RPC parameters are
490 * not available at svc_dg_reply time.
491 */
492
493static const char cache_set_str[] = "cache_set: %s";
494static const char cache_set_err1[] = "victim not found";
495static const char cache_set_err2[] = "victim alloc failed";
496static const char cache_set_err3[] = "could not allocate new rpc buffer";
497
498static void
499cache_set(xprt, replylen)
500	SVCXPRT *xprt;
501	size_t replylen;
502{
503	cache_ptr victim;
504	cache_ptr *vicp;
505	struct svc_dg_data *su;
506	struct cl_cache *uc;
507	u_int loc;
508	char *newbuf;
509#ifdef RPC_CACHE_DEBUG
510	struct netconfig *nconf;
511	char *uaddr;
512#endif
513
514	_DIAGASSERT(xprt != NULL);
515
516	su = su_data(xprt);
517	uc = (struct cl_cache *) su->su_cache;
518
519	mutex_lock(&dupreq_lock);
520	/*
521	 * Find space for the new entry, either by
522	 * reusing an old entry, or by mallocing a new one
523	 */
524	victim = uc->uc_fifo[uc->uc_nextvictim];
525	if (victim != NULL) {
526		loc = CACHE_LOC(xprt, victim->cache_xid);
527		for (vicp = &uc->uc_entries[loc];
528			*vicp != NULL && *vicp != victim;
529			vicp = &(*vicp)->cache_next)
530			;
531		if (*vicp == NULL) {
532			warnx(cache_set_str, cache_set_err1);
533			mutex_unlock(&dupreq_lock);
534			return;
535		}
536		*vicp = victim->cache_next;	/* remove from cache */
537		newbuf = victim->cache_reply;
538	} else {
539		victim = ALLOC(struct cache_node, 1);
540		if (victim == NULL) {
541			warnx(cache_set_str, cache_set_err2);
542			mutex_unlock(&dupreq_lock);
543			return;
544		}
545		newbuf = mem_alloc(su->su_iosz);
546		if (newbuf == NULL) {
547			warnx(cache_set_str, cache_set_err3);
548			FREE(victim, struct cache_node, 1);
549			mutex_unlock(&dupreq_lock);
550			return;
551		}
552	}
553
554	/*
555	 * Store it away
556	 */
557#ifdef RPC_CACHE_DEBUG
558	if (nconf = getnetconfigent(xprt->xp_netid)) {
559		uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
560		freenetconfigent(nconf);
561		printf(
562	"cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
563			su->su_xid, uc->uc_prog, uc->uc_vers,
564			uc->uc_proc, uaddr);
565		free(uaddr);
566	}
567#endif
568	victim->cache_replylen = replylen;
569	victim->cache_reply = rpc_buffer(xprt);
570	rpc_buffer(xprt) = newbuf;
571	xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt),
572			su->su_iosz, XDR_ENCODE);
573	victim->cache_xid = su->su_xid;
574	victim->cache_proc = uc->uc_proc;
575	victim->cache_vers = uc->uc_vers;
576	victim->cache_prog = uc->uc_prog;
577	victim->cache_addr = xprt->xp_rtaddr;
578	victim->cache_addr.buf = ALLOC(char, xprt->xp_rtaddr.len);
579	(void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf,
580	    (size_t)xprt->xp_rtaddr.len);
581	loc = CACHE_LOC(xprt, victim->cache_xid);
582	victim->cache_next = uc->uc_entries[loc];
583	uc->uc_entries[loc] = victim;
584	uc->uc_fifo[uc->uc_nextvictim++] = victim;
585	uc->uc_nextvictim %= uc->uc_size;
586	mutex_unlock(&dupreq_lock);
587}
588
589/*
590 * Try to get an entry from the cache
591 * return 1 if found, 0 if not found and set the stage for cache_set()
592 */
593static int
594cache_get(xprt, msg, replyp, replylenp)
595	SVCXPRT *xprt;
596	struct rpc_msg *msg;
597	char **replyp;
598	size_t *replylenp;
599{
600	u_int loc;
601	cache_ptr ent;
602	struct svc_dg_data *su;
603	struct cl_cache *uc;
604#ifdef RPC_CACHE_DEBUG
605	struct netconfig *nconf;
606	char *uaddr;
607#endif
608
609	_DIAGASSERT(xprt != NULL);
610	_DIAGASSERT(msg != NULL);
611	_DIAGASSERT(replyp != NULL);
612	_DIAGASSERT(replylenp != NULL);
613
614	su = su_data(xprt);
615	uc = (struct cl_cache *) su->su_cache;
616
617	mutex_lock(&dupreq_lock);
618	loc = CACHE_LOC(xprt, su->su_xid);
619	for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
620		if (ent->cache_xid == su->su_xid &&
621			ent->cache_proc == msg->rm_call.cb_proc &&
622			ent->cache_vers == msg->rm_call.cb_vers &&
623			ent->cache_prog == msg->rm_call.cb_prog &&
624			ent->cache_addr.len == xprt->xp_rtaddr.len &&
625			(memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf,
626				xprt->xp_rtaddr.len) == 0)) {
627#ifdef RPC_CACHE_DEBUG
628			if (nconf = getnetconfigent(xprt->xp_netid)) {
629				uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
630				freenetconfigent(nconf);
631				printf(
632	"cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
633					su->su_xid, msg->rm_call.cb_prog,
634					msg->rm_call.cb_vers,
635					msg->rm_call.cb_proc, uaddr);
636				free(uaddr);
637			}
638#endif
639			*replyp = ent->cache_reply;
640			*replylenp = ent->cache_replylen;
641			mutex_unlock(&dupreq_lock);
642			return (1);
643		}
644	}
645	/*
646	 * Failed to find entry
647	 * Remember a few things so we can do a set later
648	 */
649	uc->uc_proc = msg->rm_call.cb_proc;
650	uc->uc_vers = msg->rm_call.cb_vers;
651	uc->uc_prog = msg->rm_call.cb_prog;
652	mutex_unlock(&dupreq_lock);
653	return (0);
654}
655