* libio/stdio.h (vscanf): Fix -std=c99 redirect.
[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 #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       (void) __fxprintf (NULL, "%s: %s",
153                          "svcudp_create",  _("out of memory\n"));
154       mem_free (xprt, sizeof (SVCXPRT));
155       mem_free (su, sizeof (*su));
156       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
157       return NULL;
158     }
159   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
160   rpc_buffer (xprt) = buf;
161   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
162                          XDR_DECODE);
163   su->su_cache = NULL;
164   xprt->xp_p2 = (caddr_t) su;
165   xprt->xp_verf.oa_base = su->su_verfbody;
166   xprt->xp_ops = &svcudp_op;
167   xprt->xp_port = ntohs (addr.sin_port);
168   xprt->xp_sock = sock;
169
170 #ifdef IP_PKTINFO
171   if ((sizeof (struct iovec) + sizeof (struct msghdr)
172        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
173       > sizeof (xprt->xp_pad))
174     {
175       (void) __fxprintf (NULL,"%s", _("\
176 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
177       return NULL;
178     }
179   pad = 1;
180   if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
181                     sizeof (pad)) == 0)
182     /* Set the padding to all 1s. */
183     pad = 0xff;
184   else
185 #endif
186     /* Clear the padding. */
187     pad = 0;
188   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
189
190   xprt_register (xprt);
191   return xprt;
192 }
193 INTDEF (svcudp_bufcreate)
194
195 SVCXPRT *
196 svcudp_create (sock)
197      int sock;
198 {
199   return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
200 }
201 INTDEF (svcudp_create)
202
203 static enum xprt_stat
204 svcudp_stat (xprt)
205      SVCXPRT *xprt;
206 {
207
208   return XPRT_IDLE;
209 }
210
211 static bool_t
212 svcudp_recv (xprt, msg)
213      SVCXPRT *xprt;
214      struct rpc_msg *msg;
215 {
216   struct svcudp_data *su = su_data (xprt);
217   XDR *xdrs = &(su->su_xdrs);
218   int rlen;
219   char *reply;
220   u_long replylen;
221   socklen_t len;
222
223   /* It is very tricky when you have IP aliases. We want to make sure
224      that we are sending the packet from the IP address where the
225      incoming packet is addressed to. H.J. */
226 #ifdef IP_PKTINFO
227   struct iovec *iovp;
228   struct msghdr *mesgp;
229 #endif
230
231 again:
232   /* FIXME -- should xp_addrlen be a size_t?  */
233   len = (socklen_t) sizeof(struct sockaddr_in);
234 #ifdef IP_PKTINFO
235   iovp = (struct iovec *) &xprt->xp_pad [0];
236   mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
237   if (mesgp->msg_iovlen)
238     {
239       iovp->iov_base = rpc_buffer (xprt);
240       iovp->iov_len = su->su_iosz;
241       mesgp->msg_iov = iovp;
242       mesgp->msg_iovlen = 1;
243       mesgp->msg_name = &(xprt->xp_raddr);
244       mesgp->msg_namelen = len;
245       mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
246                                           + sizeof (struct msghdr)];
247       mesgp->msg_controllen = sizeof(xprt->xp_pad)
248                               - sizeof (struct iovec) - sizeof (struct msghdr);
249       rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
250       if (rlen >= 0)
251         {
252           struct cmsghdr *cmsg;
253           len = mesgp->msg_namelen;
254           cmsg = CMSG_FIRSTHDR (mesgp);
255           if (cmsg == NULL
256               || CMSG_NXTHDR (mesgp, cmsg) != NULL
257               || cmsg->cmsg_level != SOL_IP
258               || cmsg->cmsg_type != IP_PKTINFO
259               || cmsg->cmsg_len < (sizeof (struct cmsghdr)
260                                    + sizeof (struct in_pktinfo)))
261             {
262               /* Not a simple IP_PKTINFO, ignore it.  */
263               mesgp->msg_control = NULL;
264               mesgp->msg_controllen = 0;
265             }
266           else
267             {
268               /* It was a simple IP_PKTIFO as we expected, discard the
269                  interface field.  */
270               struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
271               pkti->ipi_ifindex = 0;
272             }
273         }
274     }
275   else
276 #endif
277     rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
278                        (int) su->su_iosz, 0,
279                        (struct sockaddr *) &(xprt->xp_raddr), &len);
280   xprt->xp_addrlen = len;
281   if (rlen == -1 && errno == EINTR)
282     goto again;
283   if (rlen < 16)                /* < 4 32-bit ints? */
284     return FALSE;
285   xdrs->x_op = XDR_DECODE;
286   XDR_SETPOS (xdrs, 0);
287   if (!INTUSE(xdr_callmsg) (xdrs, msg))
288     return FALSE;
289   su->su_xid = msg->rm_xid;
290   if (su->su_cache != NULL)
291     {
292       if (cache_get (xprt, msg, &reply, &replylen))
293         {
294 #ifdef IP_PKTINFO
295           if (mesgp->msg_iovlen)
296             {
297               iovp->iov_base = reply;
298               iovp->iov_len = replylen;
299               (void) __sendmsg (xprt->xp_sock, mesgp, 0);
300             }
301           else
302 #endif
303             (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
304                              (struct sockaddr *) &xprt->xp_raddr, len);
305           return TRUE;
306         }
307     }
308   return TRUE;
309 }
310
311 static bool_t
312 svcudp_reply (xprt, msg)
313      SVCXPRT *xprt;
314      struct rpc_msg *msg;
315 {
316   struct svcudp_data *su = su_data (xprt);
317   XDR *xdrs = &(su->su_xdrs);
318   int slen, sent;
319   bool_t stat = FALSE;
320 #ifdef IP_PKTINFO
321   struct iovec *iovp;
322   struct msghdr *mesgp;
323 #endif
324
325   xdrs->x_op = XDR_ENCODE;
326   XDR_SETPOS (xdrs, 0);
327   msg->rm_xid = su->su_xid;
328   if (INTUSE(xdr_replymsg) (xdrs, msg))
329     {
330       slen = (int) XDR_GETPOS (xdrs);
331 #ifdef IP_PKTINFO
332       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
333       if (mesgp->msg_iovlen)
334         {
335           iovp = (struct iovec *) &xprt->xp_pad [0];
336           iovp->iov_base = rpc_buffer (xprt);
337           iovp->iov_len = slen;
338           sent = __sendmsg (xprt->xp_sock, mesgp, 0);
339         }
340       else
341 #endif
342         sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
343                          (struct sockaddr *) &(xprt->xp_raddr),
344                          xprt->xp_addrlen);
345       if (sent == slen)
346         {
347           stat = TRUE;
348           if (su->su_cache && slen >= 0)
349             {
350               cache_set (xprt, (u_long) slen);
351             }
352         }
353     }
354   return stat;
355 }
356
357 static bool_t
358 svcudp_getargs (xprt, xdr_args, args_ptr)
359      SVCXPRT *xprt;
360      xdrproc_t xdr_args;
361      caddr_t args_ptr;
362 {
363
364   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
365 }
366
367 static bool_t
368 svcudp_freeargs (xprt, xdr_args, args_ptr)
369      SVCXPRT *xprt;
370      xdrproc_t xdr_args;
371      caddr_t args_ptr;
372 {
373   XDR *xdrs = &(su_data (xprt)->su_xdrs);
374
375   xdrs->x_op = XDR_FREE;
376   return (*xdr_args) (xdrs, args_ptr);
377 }
378
379 static void
380 svcudp_destroy (xprt)
381      SVCXPRT *xprt;
382 {
383   struct svcudp_data *su = su_data (xprt);
384
385   xprt_unregister (xprt);
386   (void) __close (xprt->xp_sock);
387   XDR_DESTROY (&(su->su_xdrs));
388   mem_free (rpc_buffer (xprt), su->su_iosz);
389   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
390   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
391 }
392
393
394 /***********this could be a separate file*********************/
395
396 /*
397  * Fifo cache for udp server
398  * Copies pointers to reply buffers into fifo cache
399  * Buffers are sent again if retransmissions are detected.
400  */
401
402 #define SPARSENESS 4            /* 75% sparse */
403
404 #define CACHE_PERROR(msg)       \
405         (void) __fxprintf(NULL, "%s\n", msg)
406
407 #define ALLOC(type, size)       \
408         (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
409
410 #define CALLOC(type, size)      \
411   (type *) calloc (sizeof (type), size)
412
413 /*
414  * An entry in the cache
415  */
416 typedef struct cache_node *cache_ptr;
417 struct cache_node
418   {
419     /*
420      * Index into cache is xid, proc, vers, prog and address
421      */
422     u_long cache_xid;
423     u_long cache_proc;
424     u_long cache_vers;
425     u_long cache_prog;
426     struct sockaddr_in cache_addr;
427     /*
428      * The cached reply and length
429      */
430     char *cache_reply;
431     u_long cache_replylen;
432     /*
433      * Next node on the list, if there is a collision
434      */
435     cache_ptr cache_next;
436   };
437
438
439
440 /*
441  * The entire cache
442  */
443 struct udp_cache
444   {
445     u_long uc_size;             /* size of cache */
446     cache_ptr *uc_entries;      /* hash table of entries in cache */
447     cache_ptr *uc_fifo;         /* fifo list of entries in cache */
448     u_long uc_nextvictim;       /* points to next victim in fifo list */
449     u_long uc_prog;             /* saved program number */
450     u_long uc_vers;             /* saved version number */
451     u_long uc_proc;             /* saved procedure number */
452     struct sockaddr_in uc_addr; /* saved caller's address */
453   };
454
455
456 /*
457  * the hashing function
458  */
459 #define CACHE_LOC(transp, xid)  \
460  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
461
462
463 /*
464  * Enable use of the cache.
465  * Note: there is no disable.
466  */
467 int
468 svcudp_enablecache (SVCXPRT *transp, u_long size)
469 {
470   struct svcudp_data *su = su_data (transp);
471   struct udp_cache *uc;
472
473   if (su->su_cache != NULL)
474     {
475       CACHE_PERROR (_("enablecache: cache already enabled"));
476       return 0;
477     }
478   uc = ALLOC (struct udp_cache, 1);
479   if (uc == NULL)
480     {
481       CACHE_PERROR (_("enablecache: could not allocate cache"));
482       return 0;
483     }
484   uc->uc_size = size;
485   uc->uc_nextvictim = 0;
486   uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
487   if (uc->uc_entries == NULL)
488     {
489       mem_free (uc, sizeof (struct udp_cache));
490       CACHE_PERROR (_("enablecache: could not allocate cache data"));
491       return 0;
492     }
493   uc->uc_fifo = CALLOC (cache_ptr, size);
494   if (uc->uc_fifo == NULL)
495     {
496       mem_free (uc->uc_entries, size * SPARSENESS);
497       mem_free (uc, sizeof (struct udp_cache));
498       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
499       return 0;
500     }
501   su->su_cache = (char *) uc;
502   return 1;
503 }
504
505
506 /*
507  * Set an entry in the cache
508  */
509 static void
510 cache_set (SVCXPRT *xprt, u_long replylen)
511 {
512   cache_ptr victim;
513   cache_ptr *vicp;
514   struct svcudp_data *su = su_data (xprt);
515   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
516   u_int loc;
517   char *newbuf;
518
519   /*
520    * Find space for the new entry, either by
521    * reusing an old entry, or by mallocing a new one
522    */
523   victim = uc->uc_fifo[uc->uc_nextvictim];
524   if (victim != NULL)
525     {
526       loc = CACHE_LOC (xprt, victim->cache_xid);
527       for (vicp = &uc->uc_entries[loc];
528            *vicp != NULL && *vicp != victim;
529            vicp = &(*vicp)->cache_next)
530         ;
531       if (*vicp == NULL)
532         {
533           CACHE_PERROR (_("cache_set: victim not found"));
534           return;
535         }
536       *vicp = victim->cache_next;       /* remote from cache */
537       newbuf = victim->cache_reply;
538     }
539   else
540     {
541       victim = ALLOC (struct cache_node, 1);
542       if (victim == NULL)
543         {
544           CACHE_PERROR (_("cache_set: victim alloc failed"));
545           return;
546         }
547       newbuf = mem_alloc (su->su_iosz);
548       if (newbuf == NULL)
549         {
550           mem_free (victim, sizeof (struct cache_node));
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   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
563                          XDR_ENCODE);
564   victim->cache_xid = su->su_xid;
565   victim->cache_proc = uc->uc_proc;
566   victim->cache_vers = uc->uc_vers;
567   victim->cache_prog = uc->uc_prog;
568   victim->cache_addr = uc->uc_addr;
569   loc = CACHE_LOC (xprt, victim->cache_xid);
570   victim->cache_next = uc->uc_entries[loc];
571   uc->uc_entries[loc] = victim;
572   uc->uc_fifo[uc->uc_nextvictim++] = victim;
573   uc->uc_nextvictim %= uc->uc_size;
574 }
575
576 /*
577  * Try to get an entry from the cache
578  * return 1 if found, 0 if not found
579  */
580 static int
581 cache_get (xprt, msg, replyp, replylenp)
582      SVCXPRT *xprt;
583      struct rpc_msg *msg;
584      char **replyp;
585      u_long *replylenp;
586 {
587   u_int loc;
588   cache_ptr ent;
589   struct svcudp_data *su = su_data (xprt);
590   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
591
592 #define EQADDR(a1, a2)  (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
593
594   loc = CACHE_LOC (xprt, su->su_xid);
595   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
596     {
597       if (ent->cache_xid == su->su_xid &&
598           ent->cache_proc == uc->uc_proc &&
599           ent->cache_vers == uc->uc_vers &&
600           ent->cache_prog == uc->uc_prog &&
601           EQADDR (ent->cache_addr, uc->uc_addr))
602         {
603           *replyp = ent->cache_reply;
604           *replylenp = ent->cache_replylen;
605           return 1;
606         }
607     }
608   /*
609    * Failed to find entry
610    * Remember a few things so we can do a set later
611    */
612   uc->uc_proc = msg->rm_call.cb_proc;
613   uc->uc_vers = msg->rm_call.cb_vers;
614   uc->uc_prog = msg->rm_call.cb_prog;
615   memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
616   return 0;
617 }