[BZ #5979]
[glibc.git] / sunrpc / svc_udp.c
blob496d6d96024658b3cc31883f2cff97b7b7405596
1 /* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */
2 /*
3 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
4 * unrestricted use provided that this legend is included on all tape
5 * media and as a part of the software program in whole or part. Users
6 * may copy or modify Sun RPC without charge, but are not authorized
7 * to license or distribute it to anyone else except as part of a product or
8 * program developed by the user.
10 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
11 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
12 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14 * Sun RPC is provided with no support and without any obligation on the
15 * part of Sun Microsystems, Inc. to assist in its use, correction,
16 * modification or enhancement.
18 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
19 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
20 * OR ANY PART THEREOF.
22 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
23 * or profits or other special, indirect and consequential damages, even if
24 * Sun has been advised of the possibility of such damages.
26 * Sun Microsystems, Inc.
27 * 2550 Garcia Avenue
28 * Mountain View, California 94043
30 #if !defined(lint) && defined(SCCSIDS)
31 static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
32 #endif
35 * svc_udp.c,
36 * Server side for UDP/IP based RPC. (Does some caching in the hopes of
37 * achieving execute-at-most-once semantics.)
39 * Copyright (C) 1984, Sun Microsystems, Inc.
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <rpc/rpc.h>
46 #include <sys/socket.h>
47 #include <errno.h>
48 #include <libintl.h>
50 #ifdef IP_PKTINFO
51 #include <sys/uio.h>
52 #endif
54 #ifdef USE_IN_LIBIO
55 # include <wchar.h>
56 # include <libio/iolibio.h>
57 #endif
59 #define rpc_buffer(xprt) ((xprt)->xp_p1)
60 #ifndef MAX
61 #define MAX(a, b) ((a > b) ? a : b)
62 #endif
64 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
65 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
66 static enum xprt_stat svcudp_stat (SVCXPRT *);
67 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
68 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
69 static void svcudp_destroy (SVCXPRT *);
71 static const struct xp_ops svcudp_op =
73 svcudp_recv,
74 svcudp_stat,
75 svcudp_getargs,
76 svcudp_reply,
77 svcudp_freeargs,
78 svcudp_destroy
81 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
82 u_long *replylenp);
83 static void cache_set (SVCXPRT *xprt, u_long replylen);
86 * kept in xprt->xp_p2
88 struct svcudp_data
90 u_int su_iosz; /* byte size of send.recv buffer */
91 u_long su_xid; /* transaction id */
92 XDR su_xdrs; /* XDR handle */
93 char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
94 char *su_cache; /* cached data, NULL if no cache */
96 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
99 * Usage:
100 * xprt = svcudp_create(sock);
102 * If sock<0 then a socket is created, else sock is used.
103 * If the socket, sock is not bound to a port then svcudp_create
104 * binds it to an arbitrary port. In any (successful) case,
105 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
106 * associated port number.
107 * Once *xprt is initialized, it is registered as a transporter;
108 * see (svc.h, xprt_register).
109 * The routines returns NULL if a problem occurred.
111 SVCXPRT *
112 svcudp_bufcreate (sock, sendsz, recvsz)
113 int sock;
114 u_int sendsz, recvsz;
116 bool_t madesock = FALSE;
117 SVCXPRT *xprt;
118 struct svcudp_data *su;
119 struct sockaddr_in addr;
120 socklen_t len = sizeof (struct sockaddr_in);
121 int pad;
122 void *buf;
124 if (sock == RPC_ANYSOCK)
126 if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
128 perror (_("svcudp_create: socket creation problem"));
129 return (SVCXPRT *) NULL;
131 madesock = TRUE;
133 __bzero ((char *) &addr, sizeof (addr));
134 addr.sin_family = AF_INET;
135 if (bindresvport (sock, &addr))
137 addr.sin_port = 0;
138 (void) __bind (sock, (struct sockaddr *) &addr, len);
140 if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
142 perror (_("svcudp_create - cannot getsockname"));
143 if (madesock)
144 (void) __close (sock);
145 return (SVCXPRT *) NULL;
147 xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
148 su = (struct svcudp_data *) mem_alloc (sizeof (*su));
149 buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
150 if (xprt == NULL || su == NULL || buf == NULL)
152 (void) __fxprintf (NULL, "%s: %s",
153 "svcudp_create", _("out of memory\n"));
154 mem_free (xprt, sizeof (SVCXPRT));
155 mem_free (su, sizeof (*su));
156 mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
157 return NULL;
159 su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
160 rpc_buffer (xprt) = buf;
161 INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
162 XDR_DECODE);
163 su->su_cache = NULL;
164 xprt->xp_p2 = (caddr_t) su;
165 xprt->xp_verf.oa_base = su->su_verfbody;
166 xprt->xp_ops = &svcudp_op;
167 xprt->xp_port = ntohs (addr.sin_port);
168 xprt->xp_sock = sock;
170 #ifdef IP_PKTINFO
171 if ((sizeof (struct iovec) + sizeof (struct msghdr)
172 + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
173 > sizeof (xprt->xp_pad))
175 (void) __fxprintf (NULL,"%s", _("\
176 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
177 return NULL;
179 pad = 1;
180 if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
181 sizeof (pad)) == 0)
182 /* Set the padding to all 1s. */
183 pad = 0xff;
184 else
185 #endif
186 /* Clear the padding. */
187 pad = 0;
188 memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
190 xprt_register (xprt);
191 return xprt;
193 INTDEF (svcudp_bufcreate)
195 SVCXPRT *
196 svcudp_create (sock)
197 int sock;
199 return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
201 INTDEF (svcudp_create)
203 static enum xprt_stat
204 svcudp_stat (xprt)
205 SVCXPRT *xprt;
208 return XPRT_IDLE;
211 static bool_t
212 svcudp_recv (xprt, msg)
213 SVCXPRT *xprt;
214 struct rpc_msg *msg;
216 struct svcudp_data *su = su_data (xprt);
217 XDR *xdrs = &(su->su_xdrs);
218 int rlen;
219 char *reply;
220 u_long replylen;
221 socklen_t len;
223 /* It is very tricky when you have IP aliases. We want to make sure
224 that we are sending the packet from the IP address where the
225 incoming packet is addressed to. H.J. */
226 #ifdef IP_PKTINFO
227 struct iovec *iovp;
228 struct msghdr *mesgp;
229 #endif
231 again:
232 /* FIXME -- should xp_addrlen be a size_t? */
233 len = (socklen_t) sizeof(struct sockaddr_in);
234 #ifdef IP_PKTINFO
235 iovp = (struct iovec *) &xprt->xp_pad [0];
236 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
237 if (mesgp->msg_iovlen)
239 iovp->iov_base = rpc_buffer (xprt);
240 iovp->iov_len = su->su_iosz;
241 mesgp->msg_iov = iovp;
242 mesgp->msg_iovlen = 1;
243 mesgp->msg_name = &(xprt->xp_raddr);
244 mesgp->msg_namelen = len;
245 mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
246 + sizeof (struct msghdr)];
247 mesgp->msg_controllen = sizeof(xprt->xp_pad)
248 - sizeof (struct iovec) - sizeof (struct msghdr);
249 rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
250 if (rlen >= 0)
252 struct cmsghdr *cmsg;
253 len = mesgp->msg_namelen;
254 cmsg = CMSG_FIRSTHDR (mesgp);
255 if (cmsg == NULL
256 || CMSG_NXTHDR (mesgp, cmsg) != NULL
257 || cmsg->cmsg_level != SOL_IP
258 || cmsg->cmsg_type != IP_PKTINFO
259 || cmsg->cmsg_len < (sizeof (struct cmsghdr)
260 + sizeof (struct in_pktinfo)))
262 /* Not a simple IP_PKTINFO, ignore it. */
263 mesgp->msg_control = NULL;
264 mesgp->msg_controllen = 0;
266 else
268 /* It was a simple IP_PKTIFO as we expected, discard the
269 interface field. */
270 struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
271 pkti->ipi_ifindex = 0;
275 else
276 #endif
277 rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
278 (int) su->su_iosz, 0,
279 (struct sockaddr *) &(xprt->xp_raddr), &len);
280 xprt->xp_addrlen = len;
281 if (rlen == -1 && errno == EINTR)
282 goto again;
283 if (rlen < 16) /* < 4 32-bit ints? */
284 return FALSE;
285 xdrs->x_op = XDR_DECODE;
286 XDR_SETPOS (xdrs, 0);
287 if (!INTUSE(xdr_callmsg) (xdrs, msg))
288 return FALSE;
289 su->su_xid = msg->rm_xid;
290 if (su->su_cache != NULL)
292 if (cache_get (xprt, msg, &reply, &replylen))
294 #ifdef IP_PKTINFO
295 if (mesgp->msg_iovlen)
297 iovp->iov_base = reply;
298 iovp->iov_len = replylen;
299 (void) __sendmsg (xprt->xp_sock, mesgp, 0);
301 else
302 #endif
303 (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
304 (struct sockaddr *) &xprt->xp_raddr, len);
305 return TRUE;
308 return TRUE;
311 static bool_t
312 svcudp_reply (xprt, msg)
313 SVCXPRT *xprt;
314 struct rpc_msg *msg;
316 struct svcudp_data *su = su_data (xprt);
317 XDR *xdrs = &(su->su_xdrs);
318 int slen, sent;
319 bool_t stat = FALSE;
320 #ifdef IP_PKTINFO
321 struct iovec *iovp;
322 struct msghdr *mesgp;
323 #endif
325 xdrs->x_op = XDR_ENCODE;
326 XDR_SETPOS (xdrs, 0);
327 msg->rm_xid = su->su_xid;
328 if (INTUSE(xdr_replymsg) (xdrs, msg))
330 slen = (int) XDR_GETPOS (xdrs);
331 #ifdef IP_PKTINFO
332 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
333 if (mesgp->msg_iovlen)
335 iovp = (struct iovec *) &xprt->xp_pad [0];
336 iovp->iov_base = rpc_buffer (xprt);
337 iovp->iov_len = slen;
338 sent = __sendmsg (xprt->xp_sock, mesgp, 0);
340 else
341 #endif
342 sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
343 (struct sockaddr *) &(xprt->xp_raddr),
344 xprt->xp_addrlen);
345 if (sent == slen)
347 stat = TRUE;
348 if (su->su_cache && slen >= 0)
350 cache_set (xprt, (u_long) slen);
354 return stat;
357 static bool_t
358 svcudp_getargs (xprt, xdr_args, args_ptr)
359 SVCXPRT *xprt;
360 xdrproc_t xdr_args;
361 caddr_t args_ptr;
364 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
367 static bool_t
368 svcudp_freeargs (xprt, xdr_args, args_ptr)
369 SVCXPRT *xprt;
370 xdrproc_t xdr_args;
371 caddr_t args_ptr;
373 XDR *xdrs = &(su_data (xprt)->su_xdrs);
375 xdrs->x_op = XDR_FREE;
376 return (*xdr_args) (xdrs, args_ptr);
379 static void
380 svcudp_destroy (xprt)
381 SVCXPRT *xprt;
383 struct svcudp_data *su = su_data (xprt);
385 xprt_unregister (xprt);
386 (void) __close (xprt->xp_sock);
387 XDR_DESTROY (&(su->su_xdrs));
388 mem_free (rpc_buffer (xprt), su->su_iosz);
389 mem_free ((caddr_t) su, sizeof (struct svcudp_data));
390 mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
394 /***********this could be a separate file*********************/
397 * Fifo cache for udp server
398 * Copies pointers to reply buffers into fifo cache
399 * Buffers are sent again if retransmissions are detected.
402 #define SPARSENESS 4 /* 75% sparse */
404 #define CACHE_PERROR(msg) \
405 (void) __fxprintf(NULL, "%s\n", msg)
407 #define ALLOC(type, size) \
408 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
410 #define CALLOC(type, size) \
411 (type *) calloc (sizeof (type), size)
414 * An entry in the cache
416 typedef struct cache_node *cache_ptr;
417 struct cache_node
420 * Index into cache is xid, proc, vers, prog and address
422 u_long cache_xid;
423 u_long cache_proc;
424 u_long cache_vers;
425 u_long cache_prog;
426 struct sockaddr_in cache_addr;
428 * The cached reply and length
430 char *cache_reply;
431 u_long cache_replylen;
433 * Next node on the list, if there is a collision
435 cache_ptr cache_next;
441 * The entire cache
443 struct udp_cache
445 u_long uc_size; /* size of cache */
446 cache_ptr *uc_entries; /* hash table of entries in cache */
447 cache_ptr *uc_fifo; /* fifo list of entries in cache */
448 u_long uc_nextvictim; /* points to next victim in fifo list */
449 u_long uc_prog; /* saved program number */
450 u_long uc_vers; /* saved version number */
451 u_long uc_proc; /* saved procedure number */
452 struct sockaddr_in uc_addr; /* saved caller's address */
457 * the hashing function
459 #define CACHE_LOC(transp, xid) \
460 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
464 * Enable use of the cache.
465 * Note: there is no disable.
468 svcudp_enablecache (SVCXPRT *transp, u_long size)
470 struct svcudp_data *su = su_data (transp);
471 struct udp_cache *uc;
473 if (su->su_cache != NULL)
475 CACHE_PERROR (_("enablecache: cache already enabled"));
476 return 0;
478 uc = ALLOC (struct udp_cache, 1);
479 if (uc == NULL)
481 CACHE_PERROR (_("enablecache: could not allocate cache"));
482 return 0;
484 uc->uc_size = size;
485 uc->uc_nextvictim = 0;
486 uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
487 if (uc->uc_entries == NULL)
489 mem_free (uc, sizeof (struct udp_cache));
490 CACHE_PERROR (_("enablecache: could not allocate cache data"));
491 return 0;
493 uc->uc_fifo = CALLOC (cache_ptr, size);
494 if (uc->uc_fifo == NULL)
496 mem_free (uc->uc_entries, size * SPARSENESS);
497 mem_free (uc, sizeof (struct udp_cache));
498 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
499 return 0;
501 su->su_cache = (char *) uc;
502 return 1;
507 * Set an entry in the cache
509 static void
510 cache_set (SVCXPRT *xprt, u_long replylen)
512 cache_ptr victim;
513 cache_ptr *vicp;
514 struct svcudp_data *su = su_data (xprt);
515 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
516 u_int loc;
517 char *newbuf;
520 * Find space for the new entry, either by
521 * reusing an old entry, or by mallocing a new one
523 victim = uc->uc_fifo[uc->uc_nextvictim];
524 if (victim != NULL)
526 loc = CACHE_LOC (xprt, victim->cache_xid);
527 for (vicp = &uc->uc_entries[loc];
528 *vicp != NULL && *vicp != victim;
529 vicp = &(*vicp)->cache_next)
531 if (*vicp == NULL)
533 CACHE_PERROR (_("cache_set: victim not found"));
534 return;
536 *vicp = victim->cache_next; /* remote from cache */
537 newbuf = victim->cache_reply;
539 else
541 victim = ALLOC (struct cache_node, 1);
542 if (victim == NULL)
544 CACHE_PERROR (_("cache_set: victim alloc failed"));
545 return;
547 newbuf = mem_alloc (su->su_iosz);
548 if (newbuf == NULL)
550 mem_free (victim, sizeof (struct cache_node));
551 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
552 return;
557 * Store it away
559 victim->cache_replylen = replylen;
560 victim->cache_reply = rpc_buffer (xprt);
561 rpc_buffer (xprt) = newbuf;
562 INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
563 XDR_ENCODE);
564 victim->cache_xid = su->su_xid;
565 victim->cache_proc = uc->uc_proc;
566 victim->cache_vers = uc->uc_vers;
567 victim->cache_prog = uc->uc_prog;
568 victim->cache_addr = uc->uc_addr;
569 loc = CACHE_LOC (xprt, victim->cache_xid);
570 victim->cache_next = uc->uc_entries[loc];
571 uc->uc_entries[loc] = victim;
572 uc->uc_fifo[uc->uc_nextvictim++] = victim;
573 uc->uc_nextvictim %= uc->uc_size;
577 * Try to get an entry from the cache
578 * return 1 if found, 0 if not found
580 static int
581 cache_get (xprt, msg, replyp, replylenp)
582 SVCXPRT *xprt;
583 struct rpc_msg *msg;
584 char **replyp;
585 u_long *replylenp;
587 u_int loc;
588 cache_ptr ent;
589 struct svcudp_data *su = su_data (xprt);
590 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
592 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
594 loc = CACHE_LOC (xprt, su->su_xid);
595 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
597 if (ent->cache_xid == su->su_xid &&
598 ent->cache_proc == uc->uc_proc &&
599 ent->cache_vers == uc->uc_vers &&
600 ent->cache_prog == uc->uc_prog &&
601 EQADDR (ent->cache_addr, uc->uc_addr))
603 *replyp = ent->cache_reply;
604 *replylenp = ent->cache_replylen;
605 return 1;
609 * Failed to find entry
610 * Remember a few things so we can do a set later
612 uc->uc_proc = msg->rm_call.cb_proc;
613 uc->uc_vers = msg->rm_call.cb_vers;
614 uc->uc_prog = msg->rm_call.cb_prog;
615 memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
616 return 0;