Sun agreed to a change of the license for the RPC code to a BSD-like license.
[platform/upstream/glibc.git] / sunrpc / svc_udp.c
1 /*
2  * svc_udp.c,
3  * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
4  * achieving execute-at-most-once semantics.)
5  *
6  * Copyright (C) 1984, Sun Microsystems, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above
15  *       copyright notice, this list of conditions and the following
16  *       disclaimer in the documentation and/or other materials
17  *       provided with the distribution.
18  *     * Neither the name of Sun Microsystems, Inc. nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <rpc/rpc.h>
40 #include <sys/socket.h>
41 #include <errno.h>
42 #include <libintl.h>
43
44 #ifdef IP_PKTINFO
45 #include <sys/uio.h>
46 #endif
47
48 #ifdef USE_IN_LIBIO
49 # include <wchar.h>
50 # include <libio/iolibio.h>
51 #endif
52
53 #define rpc_buffer(xprt) ((xprt)->xp_p1)
54 #ifndef MAX
55 #define MAX(a, b)     ((a > b) ? a : b)
56 #endif
57
58 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
59 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
60 static enum xprt_stat svcudp_stat (SVCXPRT *);
61 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
62 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
63 static void svcudp_destroy (SVCXPRT *);
64
65 static const struct xp_ops svcudp_op =
66 {
67   svcudp_recv,
68   svcudp_stat,
69   svcudp_getargs,
70   svcudp_reply,
71   svcudp_freeargs,
72   svcudp_destroy
73 };
74
75 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
76                       u_long *replylenp);
77 static void cache_set (SVCXPRT *xprt, u_long replylen);
78
79 /*
80  * kept in xprt->xp_p2
81  */
82 struct svcudp_data
83   {
84     u_int su_iosz;              /* byte size of send.recv buffer */
85     u_long su_xid;              /* transaction id */
86     XDR su_xdrs;                /* XDR handle */
87     char su_verfbody[MAX_AUTH_BYTES];   /* verifier body */
88     char *su_cache;             /* cached data, NULL if no cache */
89   };
90 #define su_data(xprt)   ((struct svcudp_data *)(xprt->xp_p2))
91
92 /*
93  * Usage:
94  *      xprt = svcudp_create(sock);
95  *
96  * If sock<0 then a socket is created, else sock is used.
97  * If the socket, sock is not bound to a port then svcudp_create
98  * binds it to an arbitrary port.  In any (successful) case,
99  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
100  * associated port number.
101  * Once *xprt is initialized, it is registered as a transporter;
102  * see (svc.h, xprt_register).
103  * The routines returns NULL if a problem occurred.
104  */
105 SVCXPRT *
106 svcudp_bufcreate (sock, sendsz, recvsz)
107      int sock;
108      u_int sendsz, recvsz;
109 {
110   bool_t madesock = FALSE;
111   SVCXPRT *xprt;
112   struct svcudp_data *su;
113   struct sockaddr_in addr;
114   socklen_t len = sizeof (struct sockaddr_in);
115   int pad;
116   void *buf;
117
118   if (sock == RPC_ANYSOCK)
119     {
120       if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
121         {
122           perror (_("svcudp_create: socket creation problem"));
123           return (SVCXPRT *) NULL;
124         }
125       madesock = TRUE;
126     }
127   __bzero ((char *) &addr, sizeof (addr));
128   addr.sin_family = AF_INET;
129   if (bindresvport (sock, &addr))
130     {
131       addr.sin_port = 0;
132       (void) __bind (sock, (struct sockaddr *) &addr, len);
133     }
134   if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
135     {
136       perror (_("svcudp_create - cannot getsockname"));
137       if (madesock)
138         (void) __close (sock);
139       return (SVCXPRT *) NULL;
140     }
141   xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
142   su = (struct svcudp_data *) mem_alloc (sizeof (*su));
143   buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
144   if (xprt == NULL || su == NULL || buf == NULL)
145     {
146       (void) __fxprintf (NULL, "%s: %s",
147                          "svcudp_create",  _("out of memory\n"));
148       mem_free (xprt, sizeof (SVCXPRT));
149       mem_free (su, sizeof (*su));
150       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
151       return NULL;
152     }
153   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
154   rpc_buffer (xprt) = buf;
155   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
156                          XDR_DECODE);
157   su->su_cache = NULL;
158   xprt->xp_p2 = (caddr_t) su;
159   xprt->xp_verf.oa_base = su->su_verfbody;
160   xprt->xp_ops = &svcudp_op;
161   xprt->xp_port = ntohs (addr.sin_port);
162   xprt->xp_sock = sock;
163
164 #ifdef IP_PKTINFO
165   if ((sizeof (struct iovec) + sizeof (struct msghdr)
166        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
167       > sizeof (xprt->xp_pad))
168     {
169       (void) __fxprintf (NULL,"%s", _("\
170 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
171       return NULL;
172     }
173   pad = 1;
174   if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
175                     sizeof (pad)) == 0)
176     /* Set the padding to all 1s. */
177     pad = 0xff;
178   else
179 #endif
180     /* Clear the padding. */
181     pad = 0;
182   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
183
184   xprt_register (xprt);
185   return xprt;
186 }
187 INTDEF (svcudp_bufcreate)
188
189 SVCXPRT *
190 svcudp_create (sock)
191      int sock;
192 {
193   return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
194 }
195 INTDEF (svcudp_create)
196
197 static enum xprt_stat
198 svcudp_stat (xprt)
199      SVCXPRT *xprt;
200 {
201
202   return XPRT_IDLE;
203 }
204
205 static bool_t
206 svcudp_recv (xprt, msg)
207      SVCXPRT *xprt;
208      struct rpc_msg *msg;
209 {
210   struct svcudp_data *su = su_data (xprt);
211   XDR *xdrs = &(su->su_xdrs);
212   int rlen;
213   char *reply;
214   u_long replylen;
215   socklen_t len;
216
217   /* It is very tricky when you have IP aliases. We want to make sure
218      that we are sending the packet from the IP address where the
219      incoming packet is addressed to. H.J. */
220 #ifdef IP_PKTINFO
221   struct iovec *iovp;
222   struct msghdr *mesgp;
223 #endif
224
225 again:
226   /* FIXME -- should xp_addrlen be a size_t?  */
227   len = (socklen_t) sizeof(struct sockaddr_in);
228 #ifdef IP_PKTINFO
229   iovp = (struct iovec *) &xprt->xp_pad [0];
230   mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
231   if (mesgp->msg_iovlen)
232     {
233       iovp->iov_base = rpc_buffer (xprt);
234       iovp->iov_len = su->su_iosz;
235       mesgp->msg_iov = iovp;
236       mesgp->msg_iovlen = 1;
237       mesgp->msg_name = &(xprt->xp_raddr);
238       mesgp->msg_namelen = len;
239       mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
240                                           + sizeof (struct msghdr)];
241       mesgp->msg_controllen = sizeof(xprt->xp_pad)
242                               - sizeof (struct iovec) - sizeof (struct msghdr);
243       rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
244       if (rlen >= 0)
245         {
246           struct cmsghdr *cmsg;
247           len = mesgp->msg_namelen;
248           cmsg = CMSG_FIRSTHDR (mesgp);
249           if (cmsg == NULL
250               || CMSG_NXTHDR (mesgp, cmsg) != NULL
251               || cmsg->cmsg_level != SOL_IP
252               || cmsg->cmsg_type != IP_PKTINFO
253               || cmsg->cmsg_len < (sizeof (struct cmsghdr)
254                                    + sizeof (struct in_pktinfo)))
255             {
256               /* Not a simple IP_PKTINFO, ignore it.  */
257               mesgp->msg_control = NULL;
258               mesgp->msg_controllen = 0;
259             }
260           else
261             {
262               /* It was a simple IP_PKTIFO as we expected, discard the
263                  interface field.  */
264               struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
265               pkti->ipi_ifindex = 0;
266             }
267         }
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 (!INTUSE(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 (INTUSE(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 #define CACHE_PERROR(msg)       \
399         (void) __fxprintf(NULL, "%s\n", msg)
400
401 #define ALLOC(type, size)       \
402         (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
403
404 #define CALLOC(type, size)      \
405   (type *) calloc (sizeof (type), size)
406
407 /*
408  * An entry in the cache
409  */
410 typedef struct cache_node *cache_ptr;
411 struct cache_node
412   {
413     /*
414      * Index into cache is xid, proc, vers, prog and address
415      */
416     u_long cache_xid;
417     u_long cache_proc;
418     u_long cache_vers;
419     u_long cache_prog;
420     struct sockaddr_in cache_addr;
421     /*
422      * The cached reply and length
423      */
424     char *cache_reply;
425     u_long cache_replylen;
426     /*
427      * Next node on the list, if there is a collision
428      */
429     cache_ptr cache_next;
430   };
431
432
433
434 /*
435  * The entire cache
436  */
437 struct udp_cache
438   {
439     u_long uc_size;             /* size of cache */
440     cache_ptr *uc_entries;      /* hash table of entries in cache */
441     cache_ptr *uc_fifo;         /* fifo list of entries in cache */
442     u_long uc_nextvictim;       /* points to next victim in fifo list */
443     u_long uc_prog;             /* saved program number */
444     u_long uc_vers;             /* saved version number */
445     u_long uc_proc;             /* saved procedure number */
446     struct sockaddr_in uc_addr; /* saved caller's address */
447   };
448
449
450 /*
451  * the hashing function
452  */
453 #define CACHE_LOC(transp, xid)  \
454  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
455
456
457 /*
458  * Enable use of the cache.
459  * Note: there is no disable.
460  */
461 int
462 svcudp_enablecache (SVCXPRT *transp, u_long size)
463 {
464   struct svcudp_data *su = su_data (transp);
465   struct udp_cache *uc;
466
467   if (su->su_cache != NULL)
468     {
469       CACHE_PERROR (_("enablecache: cache already enabled"));
470       return 0;
471     }
472   uc = ALLOC (struct udp_cache, 1);
473   if (uc == NULL)
474     {
475       CACHE_PERROR (_("enablecache: could not allocate cache"));
476       return 0;
477     }
478   uc->uc_size = size;
479   uc->uc_nextvictim = 0;
480   uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
481   if (uc->uc_entries == NULL)
482     {
483       mem_free (uc, sizeof (struct udp_cache));
484       CACHE_PERROR (_("enablecache: could not allocate cache data"));
485       return 0;
486     }
487   uc->uc_fifo = CALLOC (cache_ptr, size);
488   if (uc->uc_fifo == NULL)
489     {
490       mem_free (uc->uc_entries, size * SPARSENESS);
491       mem_free (uc, sizeof (struct udp_cache));
492       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
493       return 0;
494     }
495   su->su_cache = (char *) uc;
496   return 1;
497 }
498
499
500 /*
501  * Set an entry in the cache
502  */
503 static void
504 cache_set (SVCXPRT *xprt, u_long replylen)
505 {
506   cache_ptr victim;
507   cache_ptr *vicp;
508   struct svcudp_data *su = su_data (xprt);
509   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
510   u_int loc;
511   char *newbuf;
512
513   /*
514    * Find space for the new entry, either by
515    * reusing an old entry, or by mallocing a new one
516    */
517   victim = uc->uc_fifo[uc->uc_nextvictim];
518   if (victim != NULL)
519     {
520       loc = CACHE_LOC (xprt, victim->cache_xid);
521       for (vicp = &uc->uc_entries[loc];
522            *vicp != NULL && *vicp != victim;
523            vicp = &(*vicp)->cache_next)
524         ;
525       if (*vicp == NULL)
526         {
527           CACHE_PERROR (_("cache_set: victim not found"));
528           return;
529         }
530       *vicp = victim->cache_next;       /* remote from cache */
531       newbuf = victim->cache_reply;
532     }
533   else
534     {
535       victim = ALLOC (struct cache_node, 1);
536       if (victim == NULL)
537         {
538           CACHE_PERROR (_("cache_set: victim alloc failed"));
539           return;
540         }
541       newbuf = mem_alloc (su->su_iosz);
542       if (newbuf == NULL)
543         {
544           mem_free (victim, sizeof (struct cache_node));
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 }