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