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-2016 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
73static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
74static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
75static enum xprt_stat svcudp_stat (SVCXPRT *);
76static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
77static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
78static void svcudp_destroy (SVCXPRT *);
79
80static 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
90static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
91 u_long *replylenp);
92static void cache_set (SVCXPRT *xprt, u_long replylen);
93
94/*
95 * kept in xprt->xp_p2
96 */
97struct 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 */
120SVCXPRT *
121svcudp_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", _("\
182svcudp_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
200libc_hidden_def (svcudp_bufcreate)
201#else
202libc_hidden_nolink_sunrpc (svcudp_bufcreate, GLIBC_2_0)
203#endif
204
205SVCXPRT *
206svcudp_create (int sock)
207{
208 return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
209}
210#ifdef EXPORT_RPC_SYMBOLS
211libc_hidden_def (svcudp_create)
212#else
213libc_hidden_nolink_sunrpc (svcudp_create, GLIBC_2_0)
214#endif
215
216static enum xprt_stat
217svcudp_stat (SVCXPRT *xprt)
218{
219
220 return XPRT_IDLE;
221}
222
223static bool_t
224svcudp_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
241again:
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
325static bool_t
326svcudp_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
369static bool_t
370svcudp_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
376static bool_t
377svcudp_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
385static void
386svcudp_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 */
421typedef struct cache_node *cache_ptr;
422struct 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 */
448struct 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 */
472int
473svcudp_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}
509libc_hidden_nolink_sunrpc (svcudp_enablecache, GLIBC_2_0)
510
511
512/*
513 * Set an entry in the cache
514 */
515static void
516cache_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 */
585static int
586cache_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}
620