0410e9c008ebf3f11ad87481aacf5dada4df0d8d
[platform/upstream/linaro-glibc.git] / sunrpc / svc_udp.c
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 !defined(lint) && defined(SCCSIDS)
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/rpc.h>
46 #include <sys/socket.h>
47 #include <errno.h>
48 #include <libintl.h>
49
50 #ifdef IP_PKTINFO
51 #include <sys/uio.h>
52 #endif
53
54 #ifdef USE_IN_LIBIO
55 # include <wchar.h>
56 # include <libio/iolibio.h>
57 #endif
58
59 #define rpc_buffer(xprt) ((xprt)->xp_p1)
60 #ifndef MAX
61 #define MAX(a, b)     ((a > b) ? a : b)
62 #endif
63
64 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
65 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
66 static enum xprt_stat svcudp_stat (SVCXPRT *);
67 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
68 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
69 static void svcudp_destroy (SVCXPRT *);
70
71 static const struct xp_ops svcudp_op =
72 {
73   svcudp_recv,
74   svcudp_stat,
75   svcudp_getargs,
76   svcudp_reply,
77   svcudp_freeargs,
78   svcudp_destroy
79 };
80
81 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
82                       u_long *replylenp);
83 static void cache_set (SVCXPRT *xprt, u_long replylen);
84
85 /*
86  * kept in xprt->xp_p2
87  */
88 struct svcudp_data
89   {
90     u_int su_iosz;              /* byte size of send.recv buffer */
91     u_long su_xid;              /* transaction id */
92     XDR su_xdrs;                /* XDR handle */
93     char su_verfbody[MAX_AUTH_BYTES];   /* verifier body */
94     char *su_cache;             /* cached data, NULL if no cache */
95   };
96 #define su_data(xprt)   ((struct svcudp_data *)(xprt->xp_p2))
97
98 /*
99  * Usage:
100  *      xprt = svcudp_create(sock);
101  *
102  * If sock<0 then a socket is created, else sock is used.
103  * If the socket, sock is not bound to a port then svcudp_create
104  * binds it to an arbitrary port.  In any (successful) case,
105  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
106  * associated port number.
107  * Once *xprt is initialized, it is registered as a transporter;
108  * see (svc.h, xprt_register).
109  * The routines returns NULL if a problem occurred.
110  */
111 SVCXPRT *
112 svcudp_bufcreate (sock, sendsz, recvsz)
113      int sock;
114      u_int sendsz, recvsz;
115 {
116   bool_t madesock = FALSE;
117   SVCXPRT *xprt;
118   struct svcudp_data *su;
119   struct sockaddr_in addr;
120   socklen_t len = sizeof (struct sockaddr_in);
121   int pad;
122   void *buf;
123
124   if (sock == RPC_ANYSOCK)
125     {
126       if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
127         {
128           perror (_("svcudp_create: socket creation problem"));
129           return (SVCXPRT *) NULL;
130         }
131       madesock = TRUE;
132     }
133   __bzero ((char *) &addr, sizeof (addr));
134   addr.sin_family = AF_INET;
135   if (bindresvport (sock, &addr))
136     {
137       addr.sin_port = 0;
138       (void) __bind (sock, (struct sockaddr *) &addr, len);
139     }
140   if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
141     {
142       perror (_("svcudp_create - cannot getsockname"));
143       if (madesock)
144         (void) __close (sock);
145       return (SVCXPRT *) NULL;
146     }
147   xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
148   su = (struct svcudp_data *) mem_alloc (sizeof (*su));
149   buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
150   if (xprt == NULL || su == NULL || buf == NULL)
151     {
152 #ifdef USE_IN_LIBIO
153       if (_IO_fwide (stderr, 0) > 0)
154         (void) __fwprintf (stderr, L"%s", _("svcudp_create: out of memory\n"));
155       else
156 #endif
157         (void) fputs (_("svcudp_create: out of memory\n"), stderr);
158       mem_free (xprt, sizeof (SVCXPRT));
159       mem_free (su, sizeof (*su));
160       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
161       return NULL;
162     }
163   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
164   rpc_buffer (xprt) = buf;
165   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
166                          XDR_DECODE);
167   su->su_cache = NULL;
168   xprt->xp_p2 = (caddr_t) su;
169   xprt->xp_verf.oa_base = su->su_verfbody;
170   xprt->xp_ops = &svcudp_op;
171   xprt->xp_port = ntohs (addr.sin_port);
172   xprt->xp_sock = sock;
173
174 #ifdef IP_PKTINFO
175   if ((sizeof (struct iovec) + sizeof (struct msghdr)
176        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
177       > sizeof (xprt->xp_pad))
178     {
179 # ifdef USE_IN_LIBIO
180       if (_IO_fwide (stderr, 0) > 0)
181         (void) __fwprintf (stderr, L"%s",
182                            _("svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
183       else
184 # endif
185         (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
186                       stderr);
187       return NULL;
188     }
189   pad = 1;
190   if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
191                     sizeof (pad)) == 0)
192     /* Set the padding to all 1s. */
193     pad = 0xff;
194   else
195 #endif
196     /* Clear the padding. */
197     pad = 0;
198   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
199
200   xprt_register (xprt);
201   return xprt;
202 }
203 INTDEF (svcudp_bufcreate)
204
205 SVCXPRT *
206 svcudp_create (sock)
207      int sock;
208 {
209   return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
210 }
211 INTDEF (svcudp_create)
212
213 static enum xprt_stat
214 svcudp_stat (xprt)
215      SVCXPRT *xprt;
216 {
217
218   return XPRT_IDLE;
219 }
220
221 static bool_t
222 svcudp_recv (xprt, msg)
223      SVCXPRT *xprt;
224      struct rpc_msg *msg;
225 {
226   struct svcudp_data *su = su_data (xprt);
227   XDR *xdrs = &(su->su_xdrs);
228   int rlen;
229   char *reply;
230   u_long replylen;
231   socklen_t len;
232
233   /* It is very tricky when you have IP aliases. We want to make sure
234      that we are sending the packet from the IP address where the
235      incoming packet is addressed to. H.J. */
236 #ifdef IP_PKTINFO
237   struct iovec *iovp;
238   struct msghdr *mesgp;
239 #endif
240
241 again:
242   /* FIXME -- should xp_addrlen be a size_t?  */
243   len = (socklen_t) sizeof(struct sockaddr_in);
244 #ifdef IP_PKTINFO
245   iovp = (struct iovec *) &xprt->xp_pad [0];
246   mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
247   if (mesgp->msg_iovlen)
248     {
249       iovp->iov_base = rpc_buffer (xprt);
250       iovp->iov_len = su->su_iosz;
251       mesgp->msg_iov = iovp;
252       mesgp->msg_iovlen = 1;
253       mesgp->msg_name = &(xprt->xp_raddr);
254       mesgp->msg_namelen = len;
255       mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
256                                           + sizeof (struct msghdr)];
257       mesgp->msg_controllen = sizeof(xprt->xp_pad)
258                               - sizeof (struct iovec) - sizeof (struct msghdr);
259       rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
260       if (rlen >= 0)
261         len = mesgp->msg_namelen;
262     }
263   else
264 #endif
265     rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
266                        (int) su->su_iosz, 0,
267                        (struct sockaddr *) &(xprt->xp_raddr), &len);
268   xprt->xp_addrlen = len;
269   if (rlen == -1 && errno == EINTR)
270     goto again;
271   if (rlen < 16)                /* < 4 32-bit ints? */
272     return FALSE;
273   xdrs->x_op = XDR_DECODE;
274   XDR_SETPOS (xdrs, 0);
275   if (!INTUSE(xdr_callmsg) (xdrs, msg))
276     return FALSE;
277   su->su_xid = msg->rm_xid;
278   if (su->su_cache != NULL)
279     {
280       if (cache_get (xprt, msg, &reply, &replylen))
281         {
282 #ifdef IP_PKTINFO
283           if (mesgp->msg_iovlen)
284             {
285               iovp->iov_base = reply;
286               iovp->iov_len = replylen;
287               (void) __sendmsg (xprt->xp_sock, mesgp, 0);
288             }
289           else
290 #endif
291             (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
292                              (struct sockaddr *) &xprt->xp_raddr, len);
293           return TRUE;
294         }
295     }
296   return TRUE;
297 }
298
299 static bool_t
300 svcudp_reply (xprt, msg)
301      SVCXPRT *xprt;
302      struct rpc_msg *msg;
303 {
304   struct svcudp_data *su = su_data (xprt);
305   XDR *xdrs = &(su->su_xdrs);
306   int slen, sent;
307   bool_t stat = FALSE;
308 #ifdef IP_PKTINFO
309   struct iovec *iovp;
310   struct msghdr *mesgp;
311 #endif
312
313   xdrs->x_op = XDR_ENCODE;
314   XDR_SETPOS (xdrs, 0);
315   msg->rm_xid = su->su_xid;
316   if (INTUSE(xdr_replymsg) (xdrs, msg))
317     {
318       slen = (int) XDR_GETPOS (xdrs);
319 #ifdef IP_PKTINFO
320       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
321       if (mesgp->msg_iovlen)
322         {
323           iovp = (struct iovec *) &xprt->xp_pad [0];
324           iovp->iov_base = rpc_buffer (xprt);
325           iovp->iov_len = slen;
326           sent = __sendmsg (xprt->xp_sock, mesgp, 0);
327         }
328       else
329 #endif
330         sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
331                          (struct sockaddr *) &(xprt->xp_raddr),
332                          xprt->xp_addrlen);
333       if (sent == slen)
334         {
335           stat = TRUE;
336           if (su->su_cache && slen >= 0)
337             {
338               cache_set (xprt, (u_long) slen);
339             }
340         }
341     }
342   return stat;
343 }
344
345 static bool_t
346 svcudp_getargs (xprt, xdr_args, args_ptr)
347      SVCXPRT *xprt;
348      xdrproc_t xdr_args;
349      caddr_t args_ptr;
350 {
351
352   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
353 }
354
355 static bool_t
356 svcudp_freeargs (xprt, xdr_args, args_ptr)
357      SVCXPRT *xprt;
358      xdrproc_t xdr_args;
359      caddr_t args_ptr;
360 {
361   XDR *xdrs = &(su_data (xprt)->su_xdrs);
362
363   xdrs->x_op = XDR_FREE;
364   return (*xdr_args) (xdrs, args_ptr);
365 }
366
367 static void
368 svcudp_destroy (xprt)
369      SVCXPRT *xprt;
370 {
371   struct svcudp_data *su = su_data (xprt);
372
373   xprt_unregister (xprt);
374   (void) __close (xprt->xp_sock);
375   XDR_DESTROY (&(su->su_xdrs));
376   mem_free (rpc_buffer (xprt), su->su_iosz);
377   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
378   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
379 }
380
381
382 /***********this could be a separate file*********************/
383
384 /*
385  * Fifo cache for udp server
386  * Copies pointers to reply buffers into fifo cache
387  * Buffers are sent again if retransmissions are detected.
388  */
389
390 #define SPARSENESS 4            /* 75% sparse */
391
392 #ifdef USE_IN_LIBIO
393 # define CACHE_PERROR(msg)      \
394         if (_IO_fwide (stderr, 0) > 0)                                        \
395                 (void) __fwprintf(stderr, L"%s\n", msg);                      \
396         else                                                                  \
397                 (void) fprintf(stderr, "%s\n", msg)
398 #else
399 # define CACHE_PERROR(msg)      \
400         (void) fprintf(stderr,"%s\n", msg)
401 #endif
402
403 #define ALLOC(type, size)       \
404         (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
405
406 #define BZERO(addr, type, size)  \
407         __bzero((char *) addr, sizeof(type) * (int) (size))
408
409 /*
410  * An entry in the cache
411  */
412 typedef struct cache_node *cache_ptr;
413 struct cache_node
414   {
415     /*
416      * Index into cache is xid, proc, vers, prog and address
417      */
418     u_long cache_xid;
419     u_long cache_proc;
420     u_long cache_vers;
421     u_long cache_prog;
422     struct sockaddr_in cache_addr;
423     /*
424      * The cached reply and length
425      */
426     char *cache_reply;
427     u_long cache_replylen;
428     /*
429      * Next node on the list, if there is a collision
430      */
431     cache_ptr cache_next;
432   };
433
434
435
436 /*
437  * The entire cache
438  */
439 struct udp_cache
440   {
441     u_long uc_size;             /* size of cache */
442     cache_ptr *uc_entries;      /* hash table of entries in cache */
443     cache_ptr *uc_fifo;         /* fifo list of entries in cache */
444     u_long uc_nextvictim;       /* points to next victim in fifo list */
445     u_long uc_prog;             /* saved program number */
446     u_long uc_vers;             /* saved version number */
447     u_long uc_proc;             /* saved procedure number */
448     struct sockaddr_in uc_addr; /* saved caller's address */
449   };
450
451
452 /*
453  * the hashing function
454  */
455 #define CACHE_LOC(transp, xid)  \
456  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
457
458
459 /*
460  * Enable use of the cache.
461  * Note: there is no disable.
462  */
463 int
464 svcudp_enablecache (SVCXPRT *transp, u_long size)
465 {
466   struct svcudp_data *su = su_data (transp);
467   struct udp_cache *uc;
468
469   if (su->su_cache != NULL)
470     {
471       CACHE_PERROR (_("enablecache: cache already enabled"));
472       return 0;
473     }
474   uc = ALLOC (struct udp_cache, 1);
475   if (uc == NULL)
476     {
477       CACHE_PERROR (_("enablecache: could not allocate cache"));
478       return 0;
479     }
480   uc->uc_size = size;
481   uc->uc_nextvictim = 0;
482   uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
483   if (uc->uc_entries == NULL)
484     {
485       CACHE_PERROR (_("enablecache: could not allocate cache data"));
486       return 0;
487     }
488   BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
489   uc->uc_fifo = ALLOC (cache_ptr, size);
490   if (uc->uc_fifo == NULL)
491     {
492       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
493       return 0;
494     }
495   BZERO (uc->uc_fifo, cache_ptr, size);
496   su->su_cache = (char *) uc;
497   return 1;
498 }
499
500
501 /*
502  * Set an entry in the cache
503  */
504 static void
505 cache_set (SVCXPRT *xprt, u_long replylen)
506 {
507   cache_ptr victim;
508   cache_ptr *vicp;
509   struct svcudp_data *su = su_data (xprt);
510   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
511   u_int loc;
512   char *newbuf;
513
514   /*
515    * Find space for the new entry, either by
516    * reusing an old entry, or by mallocing a new one
517    */
518   victim = uc->uc_fifo[uc->uc_nextvictim];
519   if (victim != NULL)
520     {
521       loc = CACHE_LOC (xprt, victim->cache_xid);
522       for (vicp = &uc->uc_entries[loc];
523            *vicp != NULL && *vicp != victim;
524            vicp = &(*vicp)->cache_next)
525         ;
526       if (*vicp == NULL)
527         {
528           CACHE_PERROR (_("cache_set: victim not found"));
529           return;
530         }
531       *vicp = victim->cache_next;       /* remote from cache */
532       newbuf = victim->cache_reply;
533     }
534   else
535     {
536       victim = ALLOC (struct cache_node, 1);
537       if (victim == NULL)
538         {
539           CACHE_PERROR (_("cache_set: victim alloc failed"));
540           return;
541         }
542       newbuf = mem_alloc (su->su_iosz);
543       if (newbuf == NULL)
544         {
545           CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
546           return;
547         }
548     }
549
550   /*
551    * Store it away
552    */
553   victim->cache_replylen = replylen;
554   victim->cache_reply = rpc_buffer (xprt);
555   rpc_buffer (xprt) = newbuf;
556   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
557                          XDR_ENCODE);
558   victim->cache_xid = su->su_xid;
559   victim->cache_proc = uc->uc_proc;
560   victim->cache_vers = uc->uc_vers;
561   victim->cache_prog = uc->uc_prog;
562   victim->cache_addr = uc->uc_addr;
563   loc = CACHE_LOC (xprt, victim->cache_xid);
564   victim->cache_next = uc->uc_entries[loc];
565   uc->uc_entries[loc] = victim;
566   uc->uc_fifo[uc->uc_nextvictim++] = victim;
567   uc->uc_nextvictim %= uc->uc_size;
568 }
569
570 /*
571  * Try to get an entry from the cache
572  * return 1 if found, 0 if not found
573  */
574 static int
575 cache_get (xprt, msg, replyp, replylenp)
576      SVCXPRT *xprt;
577      struct rpc_msg *msg;
578      char **replyp;
579      u_long *replylenp;
580 {
581   u_int loc;
582   cache_ptr ent;
583   struct svcudp_data *su = su_data (xprt);
584   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
585
586 #define EQADDR(a1, a2)  (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
587
588   loc = CACHE_LOC (xprt, su->su_xid);
589   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
590     {
591       if (ent->cache_xid == su->su_xid &&
592           ent->cache_proc == uc->uc_proc &&
593           ent->cache_vers == uc->uc_vers &&
594           ent->cache_prog == uc->uc_prog &&
595           EQADDR (ent->cache_addr, uc->uc_addr))
596         {
597           *replyp = ent->cache_reply;
598           *replylenp = ent->cache_replylen;
599           return 1;
600         }
601     }
602   /*
603    * Failed to find entry
604    * Remember a few things so we can do a set later
605    */
606   uc->uc_proc = msg->rm_call.cb_proc;
607   uc->uc_vers = msg->rm_call.cb_vers;
608   uc->uc_prog = msg->rm_call.cb_prog;
609   memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
610   return 0;
611 }