1 /* @(#)svc_udp.c	2.2 88/07/29 4.0 RPCSRC */
2 /*
3  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
4  * unrestricted use provided that this legend is included on all tape
5  * media and as a part of the software program in whole or part.  Users
6  * may copy or modify Sun RPC without charge, but are not authorized
7  * to license or distribute it to anyone else except as part of a product or
8  * program developed by the user.
9  *
10  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
11  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
13  *
14  * Sun RPC is provided with no support and without any obligation on the
15  * part of Sun Microsystems, Inc. to assist in its use, correction,
16  * modification or enhancement.
17  *
18  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
19  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
20  * OR ANY PART THEREOF.
21  *
22  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
23  * or profits or other special, indirect and consequential damages, even if
24  * Sun has been advised of the possibility of such damages.
25  *
26  * Sun Microsystems, Inc.
27  * 2550 Garcia Avenue
28  * Mountain View, California  94043
29  */
30 #if 0
31 static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
32 #endif
33 
34 /*
35  * svc_udp.c,
36  * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
37  * achieving execute-at-most-once semantics.)
38  *
39  * Copyright (C) 1984, Sun Microsystems, Inc.
40  */
41 
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include "rpc_private.h"
46 #include <sys/socket.h>
47 #include <errno.h>
48 
49 #ifdef IP_PKTINFO
50 #include <sys/uio.h>
51 #endif
52 
53 #define rpc_buffer(xprt) ((xprt)->xp_p1)
54 #ifndef MAX
55 #define MAX(a, b)     ((a > b) ? a : b)
56 #endif
57 
58 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
59 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
60 static enum xprt_stat svcudp_stat (SVCXPRT *);
61 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
62 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
63 static void svcudp_destroy (SVCXPRT *);
64 
65 static const struct xp_ops svcudp_op =
66 {
67   svcudp_recv,
68   svcudp_stat,
69   svcudp_getargs,
70   svcudp_reply,
71   svcudp_freeargs,
72   svcudp_destroy
73 };
74 
75 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
76 		      u_long *replylenp);
77 static void cache_set (SVCXPRT *xprt, u_long replylen);
78 
79 /*
80  * kept in xprt->xp_p2
81  */
82 struct svcudp_data
83   {
84     u_int su_iosz;		/* byte size of send.recv buffer */
85     u_long su_xid;		/* transaction id */
86     XDR su_xdrs;		/* XDR handle */
87     char su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
88     char *su_cache;		/* cached data, NULL if no cache */
89   };
90 #define	su_data(xprt)	((struct svcudp_data *)(xprt->xp_p2))
91 
92 /*
93  * Usage:
94  *      xprt = svcudp_create(sock);
95  *
96  * If sock<0 then a socket is created, else sock is used.
97  * If the socket, sock is not bound to a port then svcudp_create
98  * binds it to an arbitrary port.  In any (successful) case,
99  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
100  * associated port number.
101  * Once *xprt is initialized, it is registered as a transporter;
102  * see (svc.h, xprt_register).
103  * The routines returns NULL if a problem occurred.
104  */
105 SVCXPRT *
svcudp_bufcreate(int sock,u_int sendsz,u_int recvsz)106 svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
107 {
108   bool_t madesock = FALSE;
109   SVCXPRT *xprt;
110   struct svcudp_data *su;
111   struct sockaddr_in addr;
112   socklen_t len = sizeof (struct sockaddr_in);
113   int pad;
114   void *buf;
115 
116   if (sock == RPC_ANYSOCK)
117     {
118       if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
119 	{
120 	  perror (_("svcudp_create: socket creation problem"));
121 	  return (SVCXPRT *) NULL;
122 	}
123       madesock = TRUE;
124     }
125   memset ((char *) &addr, 0, sizeof (addr));
126   addr.sin_family = AF_INET;
127   if (bindresvport (sock, &addr))
128     {
129       addr.sin_port = 0;
130       (void) bind (sock, (struct sockaddr *) &addr, len);
131     }
132   if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
133     {
134       perror (_("svcudp_create - cannot getsockname"));
135       if (madesock)
136 	(void) close (sock);
137       return (SVCXPRT *) NULL;
138     }
139   xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
140   su = (struct svcudp_data *) mem_alloc (sizeof (*su));
141   buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
142   if (xprt == NULL || su == NULL || buf == NULL)
143     {
144       (void) fputs (_("svcudp_create: out of memory\n"), stderr);
145       mem_free (xprt, sizeof (SVCXPRT));
146       mem_free (su, sizeof (*su));
147       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
148       return NULL;
149     }
150   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
151   rpc_buffer (xprt) = buf;
152   xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE);
153   su->su_cache = NULL;
154   xprt->xp_p2 = (caddr_t) su;
155   xprt->xp_verf.oa_base = su->su_verfbody;
156   xprt->xp_ops = &svcudp_op;
157   xprt->xp_port = ntohs (addr.sin_port);
158   xprt->xp_sock = sock;
159 
160 #ifdef IP_PKTINFO
161   if ((sizeof (struct iovec) + sizeof (struct msghdr)
162        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
163       > sizeof (xprt->xp_pad))
164     {
165       (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
166 		      stderr);
167       return NULL;
168     }
169   pad = 1;
170   if (setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
171 		  sizeof (pad)) == 0)
172     /* Set the padding to all 1s. */
173     pad = 0xff;
174   else
175 #endif
176     /* Clear the padding. */
177     pad = 0;
178   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
179 
180   xprt_register (xprt);
181   return xprt;
182 }
libc_hidden_def(svcudp_bufcreate)183 libc_hidden_def(svcudp_bufcreate)
184 
185 SVCXPRT *
186 svcudp_create (int sock)
187 {
188 
189   return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
190 }
libc_hidden_def(svcudp_create)191 libc_hidden_def(svcudp_create)
192 
193 static enum xprt_stat
194 svcudp_stat (SVCXPRT *xprt attribute_unused)
195 {
196 
197   return XPRT_IDLE;
198 }
199 
200 static bool_t
svcudp_recv(SVCXPRT * xprt,struct rpc_msg * msg)201 svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
202 {
203   struct svcudp_data *su = su_data (xprt);
204   XDR *xdrs = &(su->su_xdrs);
205   int rlen;
206   char *reply;
207   u_long replylen;
208   socklen_t len;
209 
210   /* It is very tricky when you have IP aliases. We want to make sure
211      that we are sending the packet from the IP address where the
212      incoming packet is addressed to. H.J. */
213 #ifdef IP_PKTINFO
214   struct iovec *iovp;
215   struct msghdr *mesgp;
216 #endif
217 
218 again:
219   /* FIXME -- should xp_addrlen be a size_t?  */
220   len = (socklen_t) sizeof(struct sockaddr_in);
221 #ifdef IP_PKTINFO
222   iovp = (struct iovec *) &xprt->xp_pad [0];
223   mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
224   if (mesgp->msg_iovlen)
225     {
226       iovp->iov_base = rpc_buffer (xprt);
227       iovp->iov_len = su->su_iosz;
228       mesgp->msg_iov = iovp;
229       mesgp->msg_iovlen = 1;
230       mesgp->msg_name = &(xprt->xp_raddr);
231       mesgp->msg_namelen = len;
232       mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
233 					  + sizeof (struct msghdr)];
234       mesgp->msg_controllen = sizeof(xprt->xp_pad)
235 			      - sizeof (struct iovec) - sizeof (struct msghdr);
236       rlen = recvmsg (xprt->xp_sock, mesgp, 0);
237       if (rlen >= 0)
238 	len = mesgp->msg_namelen;
239     }
240   else
241 #endif
242     rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt),
243 		     (int) su->su_iosz, 0,
244 		     (struct sockaddr *) &(xprt->xp_raddr), &len);
245   xprt->xp_addrlen = len;
246   if (rlen == -1 && errno == EINTR)
247     goto again;
248   if (rlen < 16)		/* < 4 32-bit ints? */
249     return FALSE;
250   xdrs->x_op = XDR_DECODE;
251   XDR_SETPOS (xdrs, 0);
252   if (!xdr_callmsg (xdrs, msg))
253     return FALSE;
254   su->su_xid = msg->rm_xid;
255   if (su->su_cache != NULL)
256     {
257       if (cache_get (xprt, msg, &reply, &replylen))
258 	{
259 #ifdef IP_PKTINFO
260 	  if (mesgp->msg_iovlen)
261 	    {
262 	      iovp->iov_base = reply;
263 	      iovp->iov_len = replylen;
264 	      (void) sendmsg (xprt->xp_sock, mesgp, 0);
265 	    }
266 	  else
267 #endif
268 	    (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
269 			   (struct sockaddr *) &xprt->xp_raddr, len);
270 	  return TRUE;
271 	}
272     }
273   return TRUE;
274 }
275 
276 static bool_t
svcudp_reply(SVCXPRT * xprt,struct rpc_msg * msg)277 svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
278 {
279   struct svcudp_data *su = su_data (xprt);
280   XDR *xdrs = &(su->su_xdrs);
281   int slen, sent;
282   bool_t stat = FALSE;
283 #ifdef IP_PKTINFO
284   struct iovec *iovp;
285   struct msghdr *mesgp;
286 #endif
287 
288   xdrs->x_op = XDR_ENCODE;
289   XDR_SETPOS (xdrs, 0);
290   msg->rm_xid = su->su_xid;
291   if (xdr_replymsg (xdrs, msg))
292     {
293       slen = (int) XDR_GETPOS (xdrs);
294 #ifdef IP_PKTINFO
295       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
296       if (mesgp->msg_iovlen)
297 	{
298 	  iovp = (struct iovec *) &xprt->xp_pad [0];
299 	  iovp->iov_base = rpc_buffer (xprt);
300 	  iovp->iov_len = slen;
301 	  sent = sendmsg (xprt->xp_sock, mesgp, 0);
302 	}
303       else
304 #endif
305 	sent = sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
306 		       (struct sockaddr *) &(xprt->xp_raddr),
307 		       xprt->xp_addrlen);
308       if (sent == slen)
309 	{
310 	  stat = TRUE;
311 	  if (su->su_cache && slen >= 0)
312 	    {
313 	      cache_set (xprt, (u_long) slen);
314 	    }
315 	}
316     }
317   return stat;
318 }
319 
320 static bool_t
svcudp_getargs(SVCXPRT * xprt,xdrproc_t xdr_args,caddr_t args_ptr)321 svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
322 {
323 
324   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
325 }
326 
327 static bool_t
svcudp_freeargs(SVCXPRT * xprt,xdrproc_t xdr_args,caddr_t args_ptr)328 svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
329 {
330   XDR *xdrs = &(su_data (xprt)->su_xdrs);
331 
332   xdrs->x_op = XDR_FREE;
333   return (*xdr_args) (xdrs, args_ptr);
334 }
335 
336 static void
svcudp_destroy(SVCXPRT * xprt)337 svcudp_destroy (SVCXPRT *xprt)
338 {
339   struct svcudp_data *su = su_data (xprt);
340 
341   xprt_unregister (xprt);
342   (void) close (xprt->xp_sock);
343   XDR_DESTROY (&(su->su_xdrs));
344   mem_free (rpc_buffer (xprt), su->su_iosz);
345   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
346   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
347 }
348 
349 
350 /***********this could be a separate file*********************/
351 
352 /*
353  * Fifo cache for udp server
354  * Copies pointers to reply buffers into fifo cache
355  * Buffers are sent again if retransmissions are detected.
356  */
357 
358 #define SPARSENESS 4		/* 75% sparse */
359 
360 #define CACHE_PERROR(msg)	\
361 	(void) fprintf(stderr,"%s\n", msg)
362 
363 #define ALLOC(type, size)	\
364 	(type *) mem_alloc((unsigned) (sizeof(type) * (size)))
365 
366 #define BZERO(addr, type, size)	 \
367 	memset((char *) addr, 0, sizeof(type) * (int) (size))
368 
369 /*
370  * An entry in the cache
371  */
372 typedef struct cache_node *cache_ptr;
373 struct cache_node
374   {
375     /*
376      * Index into cache is xid, proc, vers, prog and address
377      */
378     u_long cache_xid;
379     u_long cache_proc;
380     u_long cache_vers;
381     u_long cache_prog;
382     struct sockaddr_in cache_addr;
383     /*
384      * The cached reply and length
385      */
386     char *cache_reply;
387     u_long cache_replylen;
388     /*
389      * Next node on the list, if there is a collision
390      */
391     cache_ptr cache_next;
392   };
393 
394 
395 
396 /*
397  * The entire cache
398  */
399 struct udp_cache
400   {
401     u_long uc_size;		/* size of cache */
402     cache_ptr *uc_entries;	/* hash table of entries in cache */
403     cache_ptr *uc_fifo;		/* fifo list of entries in cache */
404     u_long uc_nextvictim;	/* points to next victim in fifo list */
405     u_long uc_prog;		/* saved program number */
406     u_long uc_vers;		/* saved version number */
407     u_long uc_proc;		/* saved procedure number */
408     struct sockaddr_in uc_addr;	/* saved caller's address */
409   };
410 
411 
412 /*
413  * the hashing function
414  */
415 #define CACHE_LOC(transp, xid)	\
416  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
417 
418 
419 /*
420  * Enable use of the cache.
421  * Note: there is no disable.
422  */
423 int svcudp_enablecache (SVCXPRT *transp, u_long size);
424 int
svcudp_enablecache(SVCXPRT * transp,u_long size)425 svcudp_enablecache (SVCXPRT *transp, u_long size)
426 {
427   struct svcudp_data *su = su_data (transp);
428   struct udp_cache *uc;
429 
430   if (su->su_cache != NULL)
431     {
432       CACHE_PERROR (_("enablecache: cache already enabled"));
433       return 0;
434     }
435   uc = ALLOC (struct udp_cache, 1);
436   if (uc == NULL)
437     {
438       CACHE_PERROR (_("enablecache: could not allocate cache"));
439       return 0;
440     }
441   uc->uc_size = size;
442   uc->uc_nextvictim = 0;
443   uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
444   if (uc->uc_entries == NULL)
445     {
446       CACHE_PERROR (_("enablecache: could not allocate cache data"));
447       return 0;
448     }
449   BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
450   uc->uc_fifo = ALLOC (cache_ptr, size);
451   if (uc->uc_fifo == NULL)
452     {
453       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
454       return 0;
455     }
456   BZERO (uc->uc_fifo, cache_ptr, size);
457   su->su_cache = (char *) uc;
458   return 1;
459 }
460 
461 
462 /*
463  * Set an entry in the cache
464  */
465 static void
cache_set(SVCXPRT * xprt,u_long replylen)466 cache_set (SVCXPRT *xprt, u_long replylen)
467 {
468   cache_ptr victim;
469   cache_ptr *vicp;
470   struct svcudp_data *su = su_data (xprt);
471   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
472   u_int loc;
473   char *newbuf;
474 
475   /*
476    * Find space for the new entry, either by
477    * reusing an old entry, or by mallocing a new one
478    */
479   victim = uc->uc_fifo[uc->uc_nextvictim];
480   if (victim != NULL)
481     {
482       loc = CACHE_LOC (xprt, victim->cache_xid);
483       for (vicp = &uc->uc_entries[loc];
484 	   *vicp != NULL && *vicp != victim;
485 	   vicp = &(*vicp)->cache_next)
486 	;
487       if (*vicp == NULL)
488 	{
489 	  CACHE_PERROR (_("cache_set: victim not found"));
490 	  return;
491 	}
492       *vicp = victim->cache_next;	/* remote from cache */
493       newbuf = victim->cache_reply;
494     }
495   else
496     {
497       victim = ALLOC (struct cache_node, 1);
498       if (victim == NULL)
499 	{
500 	  CACHE_PERROR (_("cache_set: victim alloc failed"));
501 	  return;
502 	}
503       newbuf = mem_alloc (su->su_iosz);
504       if (newbuf == NULL)
505 	{
506 	  CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
507 	  return;
508 	}
509     }
510 
511   /*
512    * Store it away
513    */
514   victim->cache_replylen = replylen;
515   victim->cache_reply = rpc_buffer (xprt);
516   rpc_buffer (xprt) = newbuf;
517   xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
518   victim->cache_xid = su->su_xid;
519   victim->cache_proc = uc->uc_proc;
520   victim->cache_vers = uc->uc_vers;
521   victim->cache_prog = uc->uc_prog;
522   victim->cache_addr = uc->uc_addr;
523   loc = CACHE_LOC (xprt, victim->cache_xid);
524   victim->cache_next = uc->uc_entries[loc];
525   uc->uc_entries[loc] = victim;
526   uc->uc_fifo[uc->uc_nextvictim++] = victim;
527   uc->uc_nextvictim %= uc->uc_size;
528 }
529 
530 /*
531  * Try to get an entry from the cache
532  * return 1 if found, 0 if not found
533  */
534 static int
cache_get(SVCXPRT * xprt,struct rpc_msg * msg,char ** replyp,u_long * replylenp)535 cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp)
536 {
537   u_int loc;
538   cache_ptr ent;
539   struct svcudp_data *su = su_data (xprt);
540   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
541 
542 #define EQADDR(a1, a2)	(memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
543 
544   loc = CACHE_LOC (xprt, su->su_xid);
545   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
546     {
547       if (ent->cache_xid == su->su_xid &&
548 	  ent->cache_proc == uc->uc_proc &&
549 	  ent->cache_vers == uc->uc_vers &&
550 	  ent->cache_prog == uc->uc_prog &&
551 	  EQADDR (ent->cache_addr, uc->uc_addr))
552 	{
553 	  *replyp = ent->cache_reply;
554 	  *replylenp = ent->cache_replylen;
555 	  return 1;
556 	}
557     }
558   /*
559    * Failed to find entry
560    * Remember a few things so we can do a set later
561    */
562   uc->uc_proc = msg->rm_call.cb_proc;
563   uc->uc_vers = msg->rm_call.cb_vers;
564   uc->uc_prog = msg->rm_call.cb_prog;
565   memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
566   return 0;
567 }
568