Convert 29 more function definitions to prototype style (multiple parameters in one...
[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) 2012-2015 Free Software Foundation, Inc.
7  * This file is part of the GNU C Library.
8  *
9  * The GNU C Library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * The GNU C Library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with the GNU C Library; if not, see
21  * <http://www.gnu.org/licenses/>.
22  *
23  * Copyright (c) 2010, Oracle America, Inc.
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions are
27  * met:
28  *
29  *     * Redistributions of source code must retain the above copyright
30  *       notice, this list of conditions and the following disclaimer.
31  *     * Redistributions in binary form must reproduce the above
32  *       copyright notice, this list of conditions and the following
33  *       disclaimer in the documentation and/or other materials
34  *       provided with the distribution.
35  *     * Neither the name of the "Oracle America, Inc." nor the names of its
36  *       contributors may be used to endorse or promote products derived
37  *       from this software without specific prior written permission.
38  *
39  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
40  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
41  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
42  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
43  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
44  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
46  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
48  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
49  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51  */
52
53 #include <stdio.h>
54 #include <unistd.h>
55 #include <string.h>
56 #include <rpc/rpc.h>
57 #include <sys/socket.h>
58 #include <errno.h>
59 #include <libintl.h>
60
61 #ifdef IP_PKTINFO
62 #include <sys/uio.h>
63 #endif
64
65 #include <wchar.h>
66 #include <libio/iolibio.h>
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 *
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   __bzero ((char *) &addr, 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       (void) __fxprintf (NULL, "%s: %s",
160                          "svcudp_create",  _("out of memory\n"));
161       mem_free (xprt, sizeof (SVCXPRT));
162       mem_free (su, sizeof (*su));
163       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
164       return NULL;
165     }
166   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
167   rpc_buffer (xprt) = buf;
168   xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE);
169   su->su_cache = NULL;
170   xprt->xp_p2 = (caddr_t) su;
171   xprt->xp_verf.oa_base = su->su_verfbody;
172   xprt->xp_ops = &svcudp_op;
173   xprt->xp_port = ntohs (addr.sin_port);
174   xprt->xp_sock = sock;
175
176 #ifdef IP_PKTINFO
177   if ((sizeof (struct iovec) + sizeof (struct msghdr)
178        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
179       > sizeof (xprt->xp_pad))
180     {
181       (void) __fxprintf (NULL,"%s", _("\
182 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
183       return NULL;
184     }
185   pad = 1;
186   if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
187                     sizeof (pad)) == 0)
188     /* Set the padding to all 1s. */
189     pad = 0xff;
190   else
191 #endif
192     /* Clear the padding. */
193     pad = 0;
194   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
195
196   xprt_register (xprt);
197   return xprt;
198 }
199 #ifdef EXPORT_RPC_SYMBOLS
200 libc_hidden_def (svcudp_bufcreate)
201 #else
202 libc_hidden_nolink_sunrpc (svcudp_bufcreate, GLIBC_2_0)
203 #endif
204
205 SVCXPRT *
206 svcudp_create (int sock)
207 {
208   return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
209 }
210 #ifdef EXPORT_RPC_SYMBOLS
211 libc_hidden_def (svcudp_create)
212 #else
213 libc_hidden_nolink_sunrpc (svcudp_create, GLIBC_2_0)
214 #endif
215
216 static enum xprt_stat
217 svcudp_stat (SVCXPRT *xprt)
218 {
219
220   return XPRT_IDLE;
221 }
222
223 static bool_t
224 svcudp_recv (SVCXPRT *xprt, 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 = (struct in_pktinfo *) 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)
292     {
293       if (errno == EINTR)
294         goto again;
295       __svc_accept_failed ();
296     }
297   if (rlen < 16)                /* < 4 32-bit ints? */
298     return FALSE;
299   xdrs->x_op = XDR_DECODE;
300   XDR_SETPOS (xdrs, 0);
301   if (!xdr_callmsg (xdrs, msg))
302     return FALSE;
303   su->su_xid = msg->rm_xid;
304   if (su->su_cache != NULL)
305     {
306       if (cache_get (xprt, msg, &reply, &replylen))
307         {
308 #ifdef IP_PKTINFO
309           if (mesgp->msg_iovlen)
310             {
311               iovp->iov_base = reply;
312               iovp->iov_len = replylen;
313               (void) __sendmsg (xprt->xp_sock, mesgp, 0);
314             }
315           else
316 #endif
317             (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
318                              (struct sockaddr *) &xprt->xp_raddr, len);
319           return TRUE;
320         }
321     }
322   return TRUE;
323 }
324
325 static bool_t
326 svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
327 {
328   struct svcudp_data *su = su_data (xprt);
329   XDR *xdrs = &(su->su_xdrs);
330   int slen, sent;
331   bool_t stat = FALSE;
332 #ifdef IP_PKTINFO
333   struct iovec *iovp;
334   struct msghdr *mesgp;
335 #endif
336
337   xdrs->x_op = XDR_ENCODE;
338   XDR_SETPOS (xdrs, 0);
339   msg->rm_xid = su->su_xid;
340   if (xdr_replymsg (xdrs, msg))
341     {
342       slen = (int) XDR_GETPOS (xdrs);
343 #ifdef IP_PKTINFO
344       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
345       if (mesgp->msg_iovlen)
346         {
347           iovp = (struct iovec *) &xprt->xp_pad [0];
348           iovp->iov_base = rpc_buffer (xprt);
349           iovp->iov_len = slen;
350           sent = __sendmsg (xprt->xp_sock, mesgp, 0);
351         }
352       else
353 #endif
354         sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
355                          (struct sockaddr *) &(xprt->xp_raddr),
356                          xprt->xp_addrlen);
357       if (sent == slen)
358         {
359           stat = TRUE;
360           if (su->su_cache && slen >= 0)
361             {
362               cache_set (xprt, (u_long) slen);
363             }
364         }
365     }
366   return stat;
367 }
368
369 static bool_t
370 svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
371 {
372
373   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
374 }
375
376 static bool_t
377 svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
378 {
379   XDR *xdrs = &(su_data (xprt)->su_xdrs);
380
381   xdrs->x_op = XDR_FREE;
382   return (*xdr_args) (xdrs, args_ptr);
383 }
384
385 static void
386 svcudp_destroy (SVCXPRT *xprt)
387 {
388   struct svcudp_data *su = su_data (xprt);
389
390   xprt_unregister (xprt);
391   (void) __close (xprt->xp_sock);
392   XDR_DESTROY (&(su->su_xdrs));
393   mem_free (rpc_buffer (xprt), su->su_iosz);
394   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
395   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
396 }
397
398
399 /***********this could be a separate file*********************/
400
401 /*
402  * Fifo cache for udp server
403  * Copies pointers to reply buffers into fifo cache
404  * Buffers are sent again if retransmissions are detected.
405  */
406
407 #define SPARSENESS 4            /* 75% sparse */
408
409 #define CACHE_PERROR(msg)       \
410         (void) __fxprintf(NULL, "%s\n", msg)
411
412 #define ALLOC(type, size)       \
413         (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
414
415 #define CALLOC(type, size)      \
416   (type *) calloc (sizeof (type), size)
417
418 /*
419  * An entry in the cache
420  */
421 typedef struct cache_node *cache_ptr;
422 struct cache_node
423   {
424     /*
425      * Index into cache is xid, proc, vers, prog and address
426      */
427     u_long cache_xid;
428     u_long cache_proc;
429     u_long cache_vers;
430     u_long cache_prog;
431     struct sockaddr_in cache_addr;
432     /*
433      * The cached reply and length
434      */
435     char *cache_reply;
436     u_long cache_replylen;
437     /*
438      * Next node on the list, if there is a collision
439      */
440     cache_ptr cache_next;
441   };
442
443
444
445 /*
446  * The entire cache
447  */
448 struct udp_cache
449   {
450     u_long uc_size;             /* size of cache */
451     cache_ptr *uc_entries;      /* hash table of entries in cache */
452     cache_ptr *uc_fifo;         /* fifo list of entries in cache */
453     u_long uc_nextvictim;       /* points to next victim in fifo list */
454     u_long uc_prog;             /* saved program number */
455     u_long uc_vers;             /* saved version number */
456     u_long uc_proc;             /* saved procedure number */
457     struct sockaddr_in uc_addr; /* saved caller's address */
458   };
459
460
461 /*
462  * the hashing function
463  */
464 #define CACHE_LOC(transp, xid)  \
465  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
466
467
468 /*
469  * Enable use of the cache.
470  * Note: there is no disable.
471  */
472 int
473 svcudp_enablecache (SVCXPRT *transp, u_long size)
474 {
475   struct svcudp_data *su = su_data (transp);
476   struct udp_cache *uc;
477
478   if (su->su_cache != NULL)
479     {
480       CACHE_PERROR (_("enablecache: cache already enabled"));
481       return 0;
482     }
483   uc = ALLOC (struct udp_cache, 1);
484   if (uc == NULL)
485     {
486       CACHE_PERROR (_("enablecache: could not allocate cache"));
487       return 0;
488     }
489   uc->uc_size = size;
490   uc->uc_nextvictim = 0;
491   uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
492   if (uc->uc_entries == NULL)
493     {
494       mem_free (uc, sizeof (struct udp_cache));
495       CACHE_PERROR (_("enablecache: could not allocate cache data"));
496       return 0;
497     }
498   uc->uc_fifo = CALLOC (cache_ptr, size);
499   if (uc->uc_fifo == NULL)
500     {
501       mem_free (uc->uc_entries, size * SPARSENESS);
502       mem_free (uc, sizeof (struct udp_cache));
503       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
504       return 0;
505     }
506   su->su_cache = (char *) uc;
507   return 1;
508 }
509 libc_hidden_nolink_sunrpc (svcudp_enablecache, GLIBC_2_0)
510
511
512 /*
513  * Set an entry in the cache
514  */
515 static void
516 cache_set (SVCXPRT *xprt, u_long replylen)
517 {
518   cache_ptr victim;
519   cache_ptr *vicp;
520   struct svcudp_data *su = su_data (xprt);
521   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
522   u_int loc;
523   char *newbuf;
524
525   /*
526    * Find space for the new entry, either by
527    * reusing an old entry, or by mallocing a new one
528    */
529   victim = uc->uc_fifo[uc->uc_nextvictim];
530   if (victim != NULL)
531     {
532       loc = CACHE_LOC (xprt, victim->cache_xid);
533       for (vicp = &uc->uc_entries[loc];
534            *vicp != NULL && *vicp != victim;
535            vicp = &(*vicp)->cache_next)
536         ;
537       if (*vicp == NULL)
538         {
539           CACHE_PERROR (_("cache_set: victim not found"));
540           return;
541         }
542       *vicp = victim->cache_next;       /* remote from cache */
543       newbuf = victim->cache_reply;
544     }
545   else
546     {
547       victim = ALLOC (struct cache_node, 1);
548       if (victim == NULL)
549         {
550           CACHE_PERROR (_("cache_set: victim alloc failed"));
551           return;
552         }
553       newbuf = mem_alloc (su->su_iosz);
554       if (newbuf == NULL)
555         {
556           mem_free (victim, sizeof (struct cache_node));
557           CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
558           return;
559         }
560     }
561
562   /*
563    * Store it away
564    */
565   victim->cache_replylen = replylen;
566   victim->cache_reply = rpc_buffer (xprt);
567   rpc_buffer (xprt) = newbuf;
568   xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
569   victim->cache_xid = su->su_xid;
570   victim->cache_proc = uc->uc_proc;
571   victim->cache_vers = uc->uc_vers;
572   victim->cache_prog = uc->uc_prog;
573   victim->cache_addr = uc->uc_addr;
574   loc = CACHE_LOC (xprt, victim->cache_xid);
575   victim->cache_next = uc->uc_entries[loc];
576   uc->uc_entries[loc] = victim;
577   uc->uc_fifo[uc->uc_nextvictim++] = victim;
578   uc->uc_nextvictim %= uc->uc_size;
579 }
580
581 /*
582  * Try to get an entry from the cache
583  * return 1 if found, 0 if not found
584  */
585 static int
586 cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp,
587            u_long *replylenp)
588 {
589   u_int loc;
590   cache_ptr ent;
591   struct svcudp_data *su = su_data (xprt);
592   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
593
594 #define EQADDR(a1, a2)  (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
595
596   loc = CACHE_LOC (xprt, su->su_xid);
597   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
598     {
599       if (ent->cache_xid == su->su_xid &&
600           ent->cache_proc == uc->uc_proc &&
601           ent->cache_vers == uc->uc_vers &&
602           ent->cache_prog == uc->uc_prog &&
603           EQADDR (ent->cache_addr, uc->uc_addr))
604         {
605           *replyp = ent->cache_reply;
606           *replylenp = ent->cache_replylen;
607           return 1;
608         }
609     }
610   /*
611    * Failed to find entry
612    * Remember a few things so we can do a set later
613    */
614   uc->uc_proc = msg->rm_call.cb_proc;
615   uc->uc_vers = msg->rm_call.cb_vers;
616   uc->uc_prog = msg->rm_call.cb_prog;
617   memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
618   return 0;
619 }