1 /*
2  * svc_udp.c,
3  * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
4  * achieving execute-at-most-once semantics.)
5  *
6  * Copyright (C) 2012-2022 Free Software Foundation, Inc.
7  * This file is part of the GNU C Library.
8  *
9  * The GNU C Library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * The GNU C Library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with the GNU C Library; if not, see
21  * <https://www.gnu.org/licenses/>.
22  *
23  * Copyright (c) 2010, Oracle America, Inc.
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions are
27  * met:
28  *
29  *     * Redistributions of source code must retain the above copyright
30  *       notice, this list of conditions and the following disclaimer.
31  *     * Redistributions in binary form must reproduce the above
32  *       copyright notice, this list of conditions and the following
33  *       disclaimer in the documentation and/or other materials
34  *       provided with the distribution.
35  *     * Neither the name of the "Oracle America, Inc." nor the names of its
36  *       contributors may be used to endorse or promote products derived
37  *       from this software without specific prior written permission.
38  *
39  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
40  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
41  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
42  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
43  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
44  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
46  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
48  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
49  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51  */
52 
53 #include <stdio.h>
54 #include <unistd.h>
55 #include <string.h>
56 #include <rpc/rpc.h>
57 #include <sys/socket.h>
58 #include <errno.h>
59 #include <libintl.h>
60 
61 #ifdef IP_PKTINFO
62 #include <sys/uio.h>
63 #endif
64 
65 #include <wchar.h>
66 #include <libio/iolibio.h>
67 #include <shlib-compat.h>
68 
69 #define rpc_buffer(xprt) ((xprt)->xp_p1)
70 #ifndef MAX
71 #define MAX(a, b)     ((a > b) ? a : b)
72 #endif
73 
74 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
75 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
76 static enum xprt_stat svcudp_stat (SVCXPRT *);
77 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
78 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
79 static void svcudp_destroy (SVCXPRT *);
80 
81 static const struct xp_ops svcudp_op =
82 {
83   svcudp_recv,
84   svcudp_stat,
85   svcudp_getargs,
86   svcudp_reply,
87   svcudp_freeargs,
88   svcudp_destroy
89 };
90 
91 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
92 		      u_long *replylenp);
93 static void cache_set (SVCXPRT *xprt, u_long replylen);
94 
95 /*
96  * kept in xprt->xp_p2
97  */
98 struct svcudp_data
99   {
100     u_int su_iosz;		/* byte size of send.recv buffer */
101     u_long su_xid;		/* transaction id */
102     XDR su_xdrs;		/* XDR handle */
103     char su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
104     char *su_cache;		/* cached data, NULL if no cache */
105   };
106 #define	su_data(xprt)	((struct svcudp_data *)(xprt->xp_p2))
107 
108 /*
109  * Usage:
110  *      xprt = svcudp_create(sock);
111  *
112  * If sock<0 then a socket is created, else sock is used.
113  * If the socket, sock is not bound to a port then svcudp_create
114  * binds it to an arbitrary port.  In any (successful) case,
115  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
116  * associated port number.
117  * Once *xprt is initialized, it is registered as a transporter;
118  * see (svc.h, xprt_register).
119  * The routines returns NULL if a problem occurred.
120  */
121 SVCXPRT *
svcudp_bufcreate(int sock,u_int sendsz,u_int recvsz)122 svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
123 {
124   bool_t madesock = FALSE;
125   SVCXPRT *xprt;
126   struct svcudp_data *su;
127   struct sockaddr_in addr;
128   socklen_t len = sizeof (struct sockaddr_in);
129   int pad;
130   void *buf;
131 
132   if (sock == RPC_ANYSOCK)
133     {
134       if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
135 	{
136 	  perror (_("svcudp_create: socket creation problem"));
137 	  return (SVCXPRT *) NULL;
138 	}
139       madesock = TRUE;
140     }
141   memset ((char *) &addr, 0, sizeof (addr));
142   addr.sin_family = AF_INET;
143   if (bindresvport (sock, &addr))
144     {
145       addr.sin_port = 0;
146       (void) __bind (sock, (struct sockaddr *) &addr, len);
147     }
148   if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
149     {
150       perror (_("svcudp_create - cannot getsockname"));
151       if (madesock)
152 	(void) __close (sock);
153       return (SVCXPRT *) NULL;
154     }
155   xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
156   su = (struct svcudp_data *) mem_alloc (sizeof (*su));
157   buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
158   if (xprt == NULL || su == NULL || buf == NULL)
159     {
160       (void) __fxprintf (NULL, "%s: %s",
161 			 "svcudp_create",  _("out of memory\n"));
162       mem_free (xprt, sizeof (SVCXPRT));
163       mem_free (su, sizeof (*su));
164       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
165       return NULL;
166     }
167   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
168   rpc_buffer (xprt) = buf;
169   xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE);
170   su->su_cache = NULL;
171   xprt->xp_p2 = (caddr_t) su;
172   xprt->xp_verf.oa_base = su->su_verfbody;
173   xprt->xp_ops = &svcudp_op;
174   xprt->xp_port = ntohs (addr.sin_port);
175   xprt->xp_sock = sock;
176 
177 #ifdef IP_PKTINFO
178   if ((sizeof (struct iovec) + sizeof (struct msghdr)
179        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
180       > sizeof (xprt->xp_pad))
181     {
182       (void) __fxprintf (NULL,"%s", _("\
183 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
184       return NULL;
185     }
186   pad = 1;
187   if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
188 		    sizeof (pad)) == 0)
189     /* Set the padding to all 1s. */
190     pad = 0xff;
191   else
192 #endif
193     /* Clear the padding. */
194     pad = 0;
195   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
196 
197   xprt_register (xprt);
198   return xprt;
199 }
200 #ifdef EXPORT_RPC_SYMBOLS
libc_hidden_def(svcudp_bufcreate)201 libc_hidden_def (svcudp_bufcreate)
202 #else
203 libc_hidden_nolink_sunrpc (svcudp_bufcreate, GLIBC_2_0)
204 #endif
205 
206 SVCXPRT *
207 svcudp_create (int sock)
208 {
209   return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
210 }
211 #ifdef EXPORT_RPC_SYMBOLS
libc_hidden_def(svcudp_create)212 libc_hidden_def (svcudp_create)
213 #else
214 libc_hidden_nolink_sunrpc (svcudp_create, GLIBC_2_0)
215 #endif
216 
217 static enum xprt_stat
218 svcudp_stat (SVCXPRT *xprt)
219 {
220 
221   return XPRT_IDLE;
222 }
223 
224 static bool_t
svcudp_recv(SVCXPRT * xprt,struct rpc_msg * msg)225 svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
226 {
227   struct svcudp_data *su = su_data (xprt);
228   XDR *xdrs = &(su->su_xdrs);
229   int rlen;
230   char *reply;
231   u_long replylen;
232   socklen_t len;
233 
234   /* It is very tricky when you have IP aliases. We want to make sure
235      that we are sending the packet from the IP address where the
236      incoming packet is addressed to. H.J. */
237 #ifdef IP_PKTINFO
238   struct iovec *iovp;
239   struct msghdr *mesgp;
240 #endif
241 
242 again:
243   /* FIXME -- should xp_addrlen be a size_t?  */
244   len = (socklen_t) sizeof(struct sockaddr_in);
245 #ifdef IP_PKTINFO
246   iovp = (struct iovec *) &xprt->xp_pad [0];
247   mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
248   if (mesgp->msg_iovlen)
249     {
250       iovp->iov_base = rpc_buffer (xprt);
251       iovp->iov_len = su->su_iosz;
252       mesgp->msg_iov = iovp;
253       mesgp->msg_iovlen = 1;
254       mesgp->msg_name = &(xprt->xp_raddr);
255       mesgp->msg_namelen = len;
256       mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
257 					  + sizeof (struct msghdr)];
258       mesgp->msg_controllen = sizeof(xprt->xp_pad)
259 			      - sizeof (struct iovec) - sizeof (struct msghdr);
260       rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
261       if (rlen >= 0)
262 	{
263 	  struct cmsghdr *cmsg;
264 	  len = mesgp->msg_namelen;
265 	  cmsg = CMSG_FIRSTHDR (mesgp);
266 	  if (cmsg == NULL
267 	      || CMSG_NXTHDR (mesgp, cmsg) != NULL
268 	      || cmsg->cmsg_level != SOL_IP
269 	      || cmsg->cmsg_type != IP_PKTINFO
270 	      || cmsg->cmsg_len < (sizeof (struct cmsghdr)
271 				   + sizeof (struct in_pktinfo)))
272 	    {
273 	      /* Not a simple IP_PKTINFO, ignore it.  */
274 	      mesgp->msg_control = NULL;
275 	      mesgp->msg_controllen = 0;
276 	    }
277 	  else
278 	    {
279 	      /* It was a simple IP_PKTIFO as we expected, discard the
280 		 interface field.  */
281 	      struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
282 	      pkti->ipi_ifindex = 0;
283 	    }
284 	}
285     }
286   else
287 #endif
288     rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
289 		       (int) su->su_iosz, 0,
290 		       (struct sockaddr *) &(xprt->xp_raddr), &len);
291   xprt->xp_addrlen = len;
292   if (rlen == -1)
293     {
294       if (errno == EINTR)
295 	goto again;
296       __svc_accept_failed ();
297     }
298   if (rlen < 16)		/* < 4 32-bit ints? */
299     return FALSE;
300   xdrs->x_op = XDR_DECODE;
301   XDR_SETPOS (xdrs, 0);
302   if (!xdr_callmsg (xdrs, msg))
303     return FALSE;
304   su->su_xid = msg->rm_xid;
305   if (su->su_cache != NULL)
306     {
307       if (cache_get (xprt, msg, &reply, &replylen))
308 	{
309 #ifdef IP_PKTINFO
310 	  if (mesgp->msg_iovlen)
311 	    {
312 	      iovp->iov_base = reply;
313 	      iovp->iov_len = replylen;
314 	      (void) __sendmsg (xprt->xp_sock, mesgp, 0);
315 	    }
316 	  else
317 #endif
318 	    (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
319 			     (struct sockaddr *) &xprt->xp_raddr, len);
320 	  return TRUE;
321 	}
322     }
323   return TRUE;
324 }
325 
326 static bool_t
svcudp_reply(SVCXPRT * xprt,struct rpc_msg * msg)327 svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
328 {
329   struct svcudp_data *su = su_data (xprt);
330   XDR *xdrs = &(su->su_xdrs);
331   int slen, sent;
332   bool_t stat = FALSE;
333 #ifdef IP_PKTINFO
334   struct iovec *iovp;
335   struct msghdr *mesgp;
336 #endif
337 
338   xdrs->x_op = XDR_ENCODE;
339   XDR_SETPOS (xdrs, 0);
340   msg->rm_xid = su->su_xid;
341   if (xdr_replymsg (xdrs, msg))
342     {
343       slen = (int) XDR_GETPOS (xdrs);
344 #ifdef IP_PKTINFO
345       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
346       if (mesgp->msg_iovlen)
347 	{
348 	  iovp = (struct iovec *) &xprt->xp_pad [0];
349 	  iovp->iov_base = rpc_buffer (xprt);
350 	  iovp->iov_len = slen;
351 	  sent = __sendmsg (xprt->xp_sock, mesgp, 0);
352 	}
353       else
354 #endif
355 	sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
356 			 (struct sockaddr *) &(xprt->xp_raddr),
357 			 xprt->xp_addrlen);
358       if (sent == slen)
359 	{
360 	  stat = TRUE;
361 	  if (su->su_cache && slen >= 0)
362 	    {
363 	      cache_set (xprt, (u_long) slen);
364 	    }
365 	}
366     }
367   return stat;
368 }
369 
370 static bool_t
svcudp_getargs(SVCXPRT * xprt,xdrproc_t xdr_args,caddr_t args_ptr)371 svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
372 {
373 
374   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
375 }
376 
377 static bool_t
svcudp_freeargs(SVCXPRT * xprt,xdrproc_t xdr_args,caddr_t args_ptr)378 svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
379 {
380   XDR *xdrs = &(su_data (xprt)->su_xdrs);
381 
382   xdrs->x_op = XDR_FREE;
383   return (*xdr_args) (xdrs, args_ptr);
384 }
385 
386 static void
svcudp_destroy(SVCXPRT * xprt)387 svcudp_destroy (SVCXPRT *xprt)
388 {
389   struct svcudp_data *su = su_data (xprt);
390 
391   xprt_unregister (xprt);
392   (void) __close (xprt->xp_sock);
393   XDR_DESTROY (&(su->su_xdrs));
394   mem_free (rpc_buffer (xprt), su->su_iosz);
395   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
396   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
397 }
398 
399 
400 /***********this could be a separate file*********************/
401 
402 /*
403  * Fifo cache for udp server
404  * Copies pointers to reply buffers into fifo cache
405  * Buffers are sent again if retransmissions are detected.
406  */
407 
408 #define SPARSENESS 4		/* 75% sparse */
409 
410 #define CACHE_PERROR(msg)	\
411 	(void) __fxprintf(NULL, "%s\n", msg)
412 
413 #define ALLOC(type, size)	\
414 	(type *) mem_alloc((unsigned) (sizeof(type) * (size)))
415 
416 #define CALLOC(type, size)	\
417   (type *) calloc (sizeof (type), size)
418 
419 /*
420  * An entry in the cache
421  */
422 typedef struct cache_node *cache_ptr;
423 struct cache_node
424   {
425     /*
426      * Index into cache is xid, proc, vers, prog and address
427      */
428     u_long cache_xid;
429     u_long cache_proc;
430     u_long cache_vers;
431     u_long cache_prog;
432     struct sockaddr_in cache_addr;
433     /*
434      * The cached reply and length
435      */
436     char *cache_reply;
437     u_long cache_replylen;
438     /*
439      * Next node on the list, if there is a collision
440      */
441     cache_ptr cache_next;
442   };
443 
444 
445 
446 /*
447  * The entire cache
448  */
449 struct udp_cache
450   {
451     u_long uc_size;		/* size of cache */
452     cache_ptr *uc_entries;	/* hash table of entries in cache */
453     cache_ptr *uc_fifo;		/* fifo list of entries in cache */
454     u_long uc_nextvictim;	/* points to next victim in fifo list */
455     u_long uc_prog;		/* saved program number */
456     u_long uc_vers;		/* saved version number */
457     u_long uc_proc;		/* saved procedure number */
458     struct sockaddr_in uc_addr;	/* saved caller's address */
459   };
460 
461 
462 /*
463  * the hashing function
464  */
465 #define CACHE_LOC(transp, xid)	\
466  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
467 
468 
469 /*
470  * Enable use of the cache.
471  * Note: there is no disable.
472  */
473 int
svcudp_enablecache(SVCXPRT * transp,u_long size)474 svcudp_enablecache (SVCXPRT *transp, u_long size)
475 {
476   struct svcudp_data *su = su_data (transp);
477   struct udp_cache *uc;
478 
479   if (su->su_cache != NULL)
480     {
481       CACHE_PERROR (_("enablecache: cache already enabled"));
482       return 0;
483     }
484   uc = ALLOC (struct udp_cache, 1);
485   if (uc == NULL)
486     {
487       CACHE_PERROR (_("enablecache: could not allocate cache"));
488       return 0;
489     }
490   uc->uc_size = size;
491   uc->uc_nextvictim = 0;
492   uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
493   if (uc->uc_entries == NULL)
494     {
495       mem_free (uc, sizeof (struct udp_cache));
496       CACHE_PERROR (_("enablecache: could not allocate cache data"));
497       return 0;
498     }
499   uc->uc_fifo = CALLOC (cache_ptr, size);
500   if (uc->uc_fifo == NULL)
501     {
502       mem_free (uc->uc_entries, size * SPARSENESS);
503       mem_free (uc, sizeof (struct udp_cache));
504       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
505       return 0;
506     }
507   su->su_cache = (char *) uc;
508   return 1;
509 }
libc_hidden_nolink_sunrpc(svcudp_enablecache,GLIBC_2_0)510 libc_hidden_nolink_sunrpc (svcudp_enablecache, GLIBC_2_0)
511 
512 
513 /*
514  * Set an entry in the cache
515  */
516 static void
517 cache_set (SVCXPRT *xprt, u_long replylen)
518 {
519   cache_ptr victim;
520   cache_ptr *vicp;
521   struct svcudp_data *su = su_data (xprt);
522   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
523   u_int loc;
524   char *newbuf;
525 
526   /*
527    * Find space for the new entry, either by
528    * reusing an old entry, or by mallocing a new one
529    */
530   victim = uc->uc_fifo[uc->uc_nextvictim];
531   if (victim != NULL)
532     {
533       loc = CACHE_LOC (xprt, victim->cache_xid);
534       for (vicp = &uc->uc_entries[loc];
535 	   *vicp != NULL && *vicp != victim;
536 	   vicp = &(*vicp)->cache_next)
537 	;
538       if (*vicp == NULL)
539 	{
540 	  CACHE_PERROR (_("cache_set: victim not found"));
541 	  return;
542 	}
543       *vicp = victim->cache_next;	/* remote from cache */
544       newbuf = victim->cache_reply;
545     }
546   else
547     {
548       victim = ALLOC (struct cache_node, 1);
549       if (victim == NULL)
550 	{
551 	  CACHE_PERROR (_("cache_set: victim alloc failed"));
552 	  return;
553 	}
554       newbuf = mem_alloc (su->su_iosz);
555       if (newbuf == NULL)
556 	{
557 	  mem_free (victim, sizeof (struct cache_node));
558 	  CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
559 	  return;
560 	}
561     }
562 
563   /*
564    * Store it away
565    */
566   victim->cache_replylen = replylen;
567   victim->cache_reply = rpc_buffer (xprt);
568   rpc_buffer (xprt) = newbuf;
569   xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
570   victim->cache_xid = su->su_xid;
571   victim->cache_proc = uc->uc_proc;
572   victim->cache_vers = uc->uc_vers;
573   victim->cache_prog = uc->uc_prog;
574   victim->cache_addr = uc->uc_addr;
575   loc = CACHE_LOC (xprt, victim->cache_xid);
576   victim->cache_next = uc->uc_entries[loc];
577   uc->uc_entries[loc] = victim;
578   uc->uc_fifo[uc->uc_nextvictim++] = victim;
579   uc->uc_nextvictim %= uc->uc_size;
580 }
581 
582 /*
583  * Try to get an entry from the cache
584  * return 1 if found, 0 if not found
585  */
586 static int
cache_get(SVCXPRT * xprt,struct rpc_msg * msg,char ** replyp,u_long * replylenp)587 cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp,
588 	   u_long *replylenp)
589 {
590   u_int loc;
591   cache_ptr ent;
592   struct svcudp_data *su = su_data (xprt);
593   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
594 
595 #define EQADDR(a1, a2)	(memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
596 
597   loc = CACHE_LOC (xprt, su->su_xid);
598   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
599     {
600       if (ent->cache_xid == su->su_xid &&
601 	  ent->cache_proc == uc->uc_proc &&
602 	  ent->cache_vers == uc->uc_vers &&
603 	  ent->cache_prog == uc->uc_prog &&
604 	  EQADDR (ent->cache_addr, uc->uc_addr))
605 	{
606 	  *replyp = ent->cache_reply;
607 	  *replylenp = ent->cache_replylen;
608 	  return 1;
609 	}
610     }
611   /*
612    * Failed to find entry
613    * Remember a few things so we can do a set later
614    */
615   uc->uc_proc = msg->rm_call.cb_proc;
616   uc->uc_vers = msg->rm_call.cb_vers;
617   uc->uc_prog = msg->rm_call.cb_prog;
618   memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
619   return 0;
620 }
621