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