OSDN Git Service

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