conformtest: Add mips XFAIL for struct stat st_rdev type (bug 21278).
[glibc.git] / sunrpc / svc_udp.c
blob5f6219f91ceb3f094439b83aa964cd698bef9a57
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.)
6 * Copyright (C) 2012-2017 Free Software Foundation, Inc.
7 * This file is part of the GNU C Library.
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.
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.
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/>.
23 * Copyright (c) 2010, Oracle America, Inc.
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions are
27 * met:
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.
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.
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>
61 #ifdef IP_PKTINFO
62 #include <sys/uio.h>
63 #endif
65 #include <wchar.h>
66 #include <libio/iolibio.h>
68 #define rpc_buffer(xprt) ((xprt)->xp_p1)
69 #ifndef MAX
70 #define MAX(a, b) ((a > b) ? a : b)
71 #endif
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 *);
80 static const struct xp_ops svcudp_op =
82 svcudp_recv,
83 svcudp_stat,
84 svcudp_getargs,
85 svcudp_reply,
86 svcudp_freeargs,
87 svcudp_destroy
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);
95 * kept in xprt->xp_p2
97 struct svcudp_data
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 */
105 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
108 * Usage:
109 * xprt = svcudp_create(sock);
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.
120 SVCXPRT *
121 svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
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;
131 if (sock == RPC_ANYSOCK)
133 if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
135 perror (_("svcudp_create: socket creation problem"));
136 return (SVCXPRT *) NULL;
138 madesock = TRUE;
140 __bzero ((char *) &addr, sizeof (addr));
141 addr.sin_family = AF_INET;
142 if (bindresvport (sock, &addr))
144 addr.sin_port = 0;
145 (void) __bind (sock, (struct sockaddr *) &addr, len);
147 if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
149 perror (_("svcudp_create - cannot getsockname"));
150 if (madesock)
151 (void) __close (sock);
152 return (SVCXPRT *) NULL;
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)
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;
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;
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))
181 (void) __fxprintf (NULL,"%s", _("\
182 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
183 return NULL;
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));
196 xprt_register (xprt);
197 return xprt;
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
205 SVCXPRT *
206 svcudp_create (int sock)
208 return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
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
216 static enum xprt_stat
217 svcudp_stat (SVCXPRT *xprt)
220 return XPRT_IDLE;
223 static bool_t
224 svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
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;
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
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)
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)
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)))
272 /* Not a simple IP_PKTINFO, ignore it. */
273 mesgp->msg_control = NULL;
274 mesgp->msg_controllen = 0;
276 else
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;
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)
293 if (errno == EINTR)
294 goto again;
295 __svc_accept_failed ();
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)
306 if (cache_get (xprt, msg, &reply, &replylen))
308 #ifdef IP_PKTINFO
309 if (mesgp->msg_iovlen)
311 iovp->iov_base = reply;
312 iovp->iov_len = replylen;
313 (void) __sendmsg (xprt->xp_sock, mesgp, 0);
315 else
316 #endif
317 (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
318 (struct sockaddr *) &xprt->xp_raddr, len);
319 return TRUE;
322 return TRUE;
325 static bool_t
326 svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
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
337 xdrs->x_op = XDR_ENCODE;
338 XDR_SETPOS (xdrs, 0);
339 msg->rm_xid = su->su_xid;
340 if (xdr_replymsg (xdrs, msg))
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)
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);
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)
359 stat = TRUE;
360 if (su->su_cache && slen >= 0)
362 cache_set (xprt, (u_long) slen);
366 return stat;
369 static bool_t
370 svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
373 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
376 static bool_t
377 svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
379 XDR *xdrs = &(su_data (xprt)->su_xdrs);
381 xdrs->x_op = XDR_FREE;
382 return (*xdr_args) (xdrs, args_ptr);
385 static void
386 svcudp_destroy (SVCXPRT *xprt)
388 struct svcudp_data *su = su_data (xprt);
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));
399 /***********this could be a separate file*********************/
402 * Fifo cache for udp server
403 * Copies pointers to reply buffers into fifo cache
404 * Buffers are sent again if retransmissions are detected.
407 #define SPARSENESS 4 /* 75% sparse */
409 #define CACHE_PERROR(msg) \
410 (void) __fxprintf(NULL, "%s\n", msg)
412 #define ALLOC(type, size) \
413 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
415 #define CALLOC(type, size) \
416 (type *) calloc (sizeof (type), size)
419 * An entry in the cache
421 typedef struct cache_node *cache_ptr;
422 struct cache_node
425 * Index into cache is xid, proc, vers, prog and address
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;
433 * The cached reply and length
435 char *cache_reply;
436 u_long cache_replylen;
438 * Next node on the list, if there is a collision
440 cache_ptr cache_next;
446 * The entire cache
448 struct udp_cache
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 */
462 * the hashing function
464 #define CACHE_LOC(transp, xid) \
465 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
469 * Enable use of the cache.
470 * Note: there is no disable.
473 svcudp_enablecache (SVCXPRT *transp, u_long size)
475 struct svcudp_data *su = su_data (transp);
476 struct udp_cache *uc;
478 if (su->su_cache != NULL)
480 CACHE_PERROR (_("enablecache: cache already enabled"));
481 return 0;
483 uc = ALLOC (struct udp_cache, 1);
484 if (uc == NULL)
486 CACHE_PERROR (_("enablecache: could not allocate cache"));
487 return 0;
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)
494 mem_free (uc, sizeof (struct udp_cache));
495 CACHE_PERROR (_("enablecache: could not allocate cache data"));
496 return 0;
498 uc->uc_fifo = CALLOC (cache_ptr, size);
499 if (uc->uc_fifo == NULL)
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;
506 su->su_cache = (char *) uc;
507 return 1;
509 libc_hidden_nolink_sunrpc (svcudp_enablecache, GLIBC_2_0)
513 * Set an entry in the cache
515 static void
516 cache_set (SVCXPRT *xprt, u_long replylen)
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;
526 * Find space for the new entry, either by
527 * reusing an old entry, or by mallocing a new one
529 victim = uc->uc_fifo[uc->uc_nextvictim];
530 if (victim != NULL)
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)
537 if (*vicp == NULL)
539 CACHE_PERROR (_("cache_set: victim not found"));
540 return;
542 *vicp = victim->cache_next; /* remote from cache */
543 newbuf = victim->cache_reply;
545 else
547 victim = ALLOC (struct cache_node, 1);
548 if (victim == NULL)
550 CACHE_PERROR (_("cache_set: victim alloc failed"));
551 return;
553 newbuf = mem_alloc (su->su_iosz);
554 if (newbuf == NULL)
556 mem_free (victim, sizeof (struct cache_node));
557 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
558 return;
563 * Store it away
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;
582 * Try to get an entry from the cache
583 * return 1 if found, 0 if not found
585 static int
586 cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp,
587 u_long *replylenp)
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;
594 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
596 loc = CACHE_LOC (xprt, su->su_xid);
597 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
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))
605 *replyp = ent->cache_reply;
606 *replylenp = ent->cache_replylen;
607 return 1;
611 * Failed to find entry
612 * Remember a few things so we can do a set later
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;