cb1d70558e24195eee80d8344b0653800c4b0dcd
[platform/upstream/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         {
262           struct cmsghdr *cmsg;
263           len = mesgp->msg_namelen;
264           cmsg = CMSG_FIRSTHDR (mesgp);
265           if (cmsg == NULL
266               || CMSG_NXTHDR (mesgp, cmsg) != NULL
267               || cmsg->cmsg_level != SOL_IP
268               || cmsg->cmsg_type != IP_PKTINFO
269               || cmsg->cmsg_len < (sizeof (struct cmsghdr)
270                                    + sizeof (struct in_pktinfo)))
271             {
272               /* Not a simple IP_PKTINFO, ignore it.  */
273               mesgp->msg_control = NULL;
274               mesgp->msg_controllen = 0;
275             }
276           else
277             {
278               /* It was a simple IP_PKTIFO as we expected, discard the
279                  interface field.  */
280               struct in_pktinfo *pkti = CMSG_DATA (cmsg);
281               pkti->ipi_ifindex = 0;
282             }
283         }
284     }
285   else
286 #endif
287     rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
288                        (int) su->su_iosz, 0,
289                        (struct sockaddr *) &(xprt->xp_raddr), &len);
290   xprt->xp_addrlen = len;
291   if (rlen == -1 && errno == EINTR)
292     goto again;
293   if (rlen < 16)                /* < 4 32-bit ints? */
294     return FALSE;
295   xdrs->x_op = XDR_DECODE;
296   XDR_SETPOS (xdrs, 0);
297   if (!INTUSE(xdr_callmsg) (xdrs, msg))
298     return FALSE;
299   su->su_xid = msg->rm_xid;
300   if (su->su_cache != NULL)
301     {
302       if (cache_get (xprt, msg, &reply, &replylen))
303         {
304 #ifdef IP_PKTINFO
305           if (mesgp->msg_iovlen)
306             {
307               iovp->iov_base = reply;
308               iovp->iov_len = replylen;
309               (void) __sendmsg (xprt->xp_sock, mesgp, 0);
310             }
311           else
312 #endif
313             (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
314                              (struct sockaddr *) &xprt->xp_raddr, len);
315           return TRUE;
316         }
317     }
318   return TRUE;
319 }
320
321 static bool_t
322 svcudp_reply (xprt, msg)
323      SVCXPRT *xprt;
324      struct rpc_msg *msg;
325 {
326   struct svcudp_data *su = su_data (xprt);
327   XDR *xdrs = &(su->su_xdrs);
328   int slen, sent;
329   bool_t stat = FALSE;
330 #ifdef IP_PKTINFO
331   struct iovec *iovp;
332   struct msghdr *mesgp;
333 #endif
334
335   xdrs->x_op = XDR_ENCODE;
336   XDR_SETPOS (xdrs, 0);
337   msg->rm_xid = su->su_xid;
338   if (INTUSE(xdr_replymsg) (xdrs, msg))
339     {
340       slen = (int) XDR_GETPOS (xdrs);
341 #ifdef IP_PKTINFO
342       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
343       if (mesgp->msg_iovlen)
344         {
345           iovp = (struct iovec *) &xprt->xp_pad [0];
346           iovp->iov_base = rpc_buffer (xprt);
347           iovp->iov_len = slen;
348           sent = __sendmsg (xprt->xp_sock, mesgp, 0);
349         }
350       else
351 #endif
352         sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
353                          (struct sockaddr *) &(xprt->xp_raddr),
354                          xprt->xp_addrlen);
355       if (sent == slen)
356         {
357           stat = TRUE;
358           if (su->su_cache && slen >= 0)
359             {
360               cache_set (xprt, (u_long) slen);
361             }
362         }
363     }
364   return stat;
365 }
366
367 static bool_t
368 svcudp_getargs (xprt, xdr_args, args_ptr)
369      SVCXPRT *xprt;
370      xdrproc_t xdr_args;
371      caddr_t args_ptr;
372 {
373
374   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
375 }
376
377 static bool_t
378 svcudp_freeargs (xprt, xdr_args, args_ptr)
379      SVCXPRT *xprt;
380      xdrproc_t xdr_args;
381      caddr_t args_ptr;
382 {
383   XDR *xdrs = &(su_data (xprt)->su_xdrs);
384
385   xdrs->x_op = XDR_FREE;
386   return (*xdr_args) (xdrs, args_ptr);
387 }
388
389 static void
390 svcudp_destroy (xprt)
391      SVCXPRT *xprt;
392 {
393   struct svcudp_data *su = su_data (xprt);
394
395   xprt_unregister (xprt);
396   (void) __close (xprt->xp_sock);
397   XDR_DESTROY (&(su->su_xdrs));
398   mem_free (rpc_buffer (xprt), su->su_iosz);
399   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
400   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
401 }
402
403
404 /***********this could be a separate file*********************/
405
406 /*
407  * Fifo cache for udp server
408  * Copies pointers to reply buffers into fifo cache
409  * Buffers are sent again if retransmissions are detected.
410  */
411
412 #define SPARSENESS 4            /* 75% sparse */
413
414 #ifdef USE_IN_LIBIO
415 # define CACHE_PERROR(msg)      \
416         if (_IO_fwide (stderr, 0) > 0)                                        \
417                 (void) __fwprintf(stderr, L"%s\n", msg);                      \
418         else                                                                  \
419                 (void) fprintf(stderr, "%s\n", msg)
420 #else
421 # define CACHE_PERROR(msg)      \
422         (void) fprintf(stderr,"%s\n", msg)
423 #endif
424
425 #define ALLOC(type, size)       \
426         (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
427
428 #define BZERO(addr, type, size)  \
429         __bzero((char *) addr, sizeof(type) * (int) (size))
430
431 /*
432  * An entry in the cache
433  */
434 typedef struct cache_node *cache_ptr;
435 struct cache_node
436   {
437     /*
438      * Index into cache is xid, proc, vers, prog and address
439      */
440     u_long cache_xid;
441     u_long cache_proc;
442     u_long cache_vers;
443     u_long cache_prog;
444     struct sockaddr_in cache_addr;
445     /*
446      * The cached reply and length
447      */
448     char *cache_reply;
449     u_long cache_replylen;
450     /*
451      * Next node on the list, if there is a collision
452      */
453     cache_ptr cache_next;
454   };
455
456
457
458 /*
459  * The entire cache
460  */
461 struct udp_cache
462   {
463     u_long uc_size;             /* size of cache */
464     cache_ptr *uc_entries;      /* hash table of entries in cache */
465     cache_ptr *uc_fifo;         /* fifo list of entries in cache */
466     u_long uc_nextvictim;       /* points to next victim in fifo list */
467     u_long uc_prog;             /* saved program number */
468     u_long uc_vers;             /* saved version number */
469     u_long uc_proc;             /* saved procedure number */
470     struct sockaddr_in uc_addr; /* saved caller's address */
471   };
472
473
474 /*
475  * the hashing function
476  */
477 #define CACHE_LOC(transp, xid)  \
478  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
479
480
481 /*
482  * Enable use of the cache.
483  * Note: there is no disable.
484  */
485 int
486 svcudp_enablecache (SVCXPRT *transp, u_long size)
487 {
488   struct svcudp_data *su = su_data (transp);
489   struct udp_cache *uc;
490
491   if (su->su_cache != NULL)
492     {
493       CACHE_PERROR (_("enablecache: cache already enabled"));
494       return 0;
495     }
496   uc = ALLOC (struct udp_cache, 1);
497   if (uc == NULL)
498     {
499       CACHE_PERROR (_("enablecache: could not allocate cache"));
500       return 0;
501     }
502   uc->uc_size = size;
503   uc->uc_nextvictim = 0;
504   uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
505   if (uc->uc_entries == NULL)
506     {
507       CACHE_PERROR (_("enablecache: could not allocate cache data"));
508       return 0;
509     }
510   BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
511   uc->uc_fifo = ALLOC (cache_ptr, size);
512   if (uc->uc_fifo == NULL)
513     {
514       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
515       return 0;
516     }
517   BZERO (uc->uc_fifo, cache_ptr, size);
518   su->su_cache = (char *) uc;
519   return 1;
520 }
521
522
523 /*
524  * Set an entry in the cache
525  */
526 static void
527 cache_set (SVCXPRT *xprt, u_long replylen)
528 {
529   cache_ptr victim;
530   cache_ptr *vicp;
531   struct svcudp_data *su = su_data (xprt);
532   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
533   u_int loc;
534   char *newbuf;
535
536   /*
537    * Find space for the new entry, either by
538    * reusing an old entry, or by mallocing a new one
539    */
540   victim = uc->uc_fifo[uc->uc_nextvictim];
541   if (victim != NULL)
542     {
543       loc = CACHE_LOC (xprt, victim->cache_xid);
544       for (vicp = &uc->uc_entries[loc];
545            *vicp != NULL && *vicp != victim;
546            vicp = &(*vicp)->cache_next)
547         ;
548       if (*vicp == NULL)
549         {
550           CACHE_PERROR (_("cache_set: victim not found"));
551           return;
552         }
553       *vicp = victim->cache_next;       /* remote from cache */
554       newbuf = victim->cache_reply;
555     }
556   else
557     {
558       victim = ALLOC (struct cache_node, 1);
559       if (victim == NULL)
560         {
561           CACHE_PERROR (_("cache_set: victim alloc failed"));
562           return;
563         }
564       newbuf = mem_alloc (su->su_iosz);
565       if (newbuf == NULL)
566         {
567           CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
568           return;
569         }
570     }
571
572   /*
573    * Store it away
574    */
575   victim->cache_replylen = replylen;
576   victim->cache_reply = rpc_buffer (xprt);
577   rpc_buffer (xprt) = newbuf;
578   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
579                          XDR_ENCODE);
580   victim->cache_xid = su->su_xid;
581   victim->cache_proc = uc->uc_proc;
582   victim->cache_vers = uc->uc_vers;
583   victim->cache_prog = uc->uc_prog;
584   victim->cache_addr = uc->uc_addr;
585   loc = CACHE_LOC (xprt, victim->cache_xid);
586   victim->cache_next = uc->uc_entries[loc];
587   uc->uc_entries[loc] = victim;
588   uc->uc_fifo[uc->uc_nextvictim++] = victim;
589   uc->uc_nextvictim %= uc->uc_size;
590 }
591
592 /*
593  * Try to get an entry from the cache
594  * return 1 if found, 0 if not found
595  */
596 static int
597 cache_get (xprt, msg, replyp, replylenp)
598      SVCXPRT *xprt;
599      struct rpc_msg *msg;
600      char **replyp;
601      u_long *replylenp;
602 {
603   u_int loc;
604   cache_ptr ent;
605   struct svcudp_data *su = su_data (xprt);
606   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
607
608 #define EQADDR(a1, a2)  (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
609
610   loc = CACHE_LOC (xprt, su->su_xid);
611   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
612     {
613       if (ent->cache_xid == su->su_xid &&
614           ent->cache_proc == uc->uc_proc &&
615           ent->cache_vers == uc->uc_vers &&
616           ent->cache_prog == uc->uc_prog &&
617           EQADDR (ent->cache_addr, uc->uc_addr))
618         {
619           *replyp = ent->cache_reply;
620           *replylenp = ent->cache_replylen;
621           return 1;
622         }
623     }
624   /*
625    * Failed to find entry
626    * Remember a few things so we can do a set later
627    */
628   uc->uc_proc = msg->rm_call.cb_proc;
629   uc->uc_vers = msg->rm_call.cb_vers;
630   uc->uc_prog = msg->rm_call.cb_prog;
631   memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
632   return 0;
633 }