Update.
[glibc.git] / sunrpc / svc_udp.c
blobb42d6144b9de56fff6f7830a57f8ac974132e34c
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 # define fputs(s, f) INTUSE(_IO_fputs) (s, f)
58 #endif
60 #define rpc_buffer(xprt) ((xprt)->xp_p1)
61 #ifndef MAX
62 #define MAX(a, b) ((a > b) ? a : b)
63 #endif
65 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
66 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
67 static enum xprt_stat svcudp_stat (SVCXPRT *);
68 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
69 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
70 static void svcudp_destroy (SVCXPRT *);
72 static const struct xp_ops svcudp_op =
74 svcudp_recv,
75 svcudp_stat,
76 svcudp_getargs,
77 svcudp_reply,
78 svcudp_freeargs,
79 svcudp_destroy
82 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
83 u_long *replylenp);
84 static void cache_set (SVCXPRT *xprt, u_long replylen);
87 * kept in xprt->xp_p2
89 struct svcudp_data
91 u_int su_iosz; /* byte size of send.recv buffer */
92 u_long su_xid; /* transaction id */
93 XDR su_xdrs; /* XDR handle */
94 char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
95 char *su_cache; /* cached data, NULL if no cache */
97 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
100 * Usage:
101 * xprt = svcudp_create(sock);
103 * If sock<0 then a socket is created, else sock is used.
104 * If the socket, sock is not bound to a port then svcudp_create
105 * binds it to an arbitrary port. In any (successful) case,
106 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
107 * associated port number.
108 * Once *xprt is initialized, it is registered as a transporter;
109 * see (svc.h, xprt_register).
110 * The routines returns NULL if a problem occurred.
112 SVCXPRT *
113 svcudp_bufcreate (sock, sendsz, recvsz)
114 int sock;
115 u_int sendsz, recvsz;
117 bool_t madesock = FALSE;
118 SVCXPRT *xprt;
119 struct svcudp_data *su;
120 struct sockaddr_in addr;
121 socklen_t len = sizeof (struct sockaddr_in);
122 int pad;
123 void *buf;
125 if (sock == RPC_ANYSOCK)
127 if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
129 perror (_("svcudp_create: socket creation problem"));
130 return (SVCXPRT *) NULL;
132 madesock = TRUE;
134 __bzero ((char *) &addr, sizeof (addr));
135 addr.sin_family = AF_INET;
136 if (INTUSE(bindresvport) (sock, &addr))
138 addr.sin_port = 0;
139 (void) __bind (sock, (struct sockaddr *) &addr, len);
141 if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
143 perror (_("svcudp_create - cannot getsockname"));
144 if (madesock)
145 (void) __close (sock);
146 return (SVCXPRT *) NULL;
148 xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
149 su = (struct svcudp_data *) mem_alloc (sizeof (*su));
150 buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
151 if (xprt == NULL || su == NULL || buf == NULL)
153 #ifdef USE_IN_LIBIO
154 if (_IO_fwide (stderr, 0) > 0)
155 (void) __fwprintf (stderr, L"%s", _("svcudp_create: out of memory\n"));
156 else
157 #endif
158 (void) fputs (_("svcudp_create: out of memory\n"), stderr);
159 mem_free (xprt, sizeof (SVCXPRT));
160 mem_free (su, sizeof (*su));
161 mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
162 return NULL;
164 su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
165 rpc_buffer (xprt) = buf;
166 INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
167 XDR_DECODE);
168 su->su_cache = NULL;
169 xprt->xp_p2 = (caddr_t) su;
170 xprt->xp_verf.oa_base = su->su_verfbody;
171 xprt->xp_ops = &svcudp_op;
172 xprt->xp_port = ntohs (addr.sin_port);
173 xprt->xp_sock = sock;
175 #ifdef IP_PKTINFO
176 if ((sizeof (struct iovec) + sizeof (struct msghdr)
177 + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
178 > sizeof (xprt->xp_pad))
180 # ifdef USE_IN_LIBIO
181 if (_IO_fwide (stderr, 0) > 0)
182 (void) __fwprintf (stderr, L"%s",
183 _("svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
184 else
185 # endif
186 (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
187 stderr);
188 return NULL;
190 pad = 1;
191 if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
192 sizeof (pad)) == 0)
193 /* Set the padding to all 1s. */
194 pad = 0xff;
195 else
196 #endif
197 /* Clear the padding. */
198 pad = 0;
199 memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
201 xprt_register (xprt);
202 return xprt;
204 INTDEF (svcudp_bufcreate)
206 SVCXPRT *
207 svcudp_create (sock)
208 int sock;
210 return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
212 INTDEF (svcudp_create)
214 static enum xprt_stat
215 svcudp_stat (xprt)
216 SVCXPRT *xprt;
219 return XPRT_IDLE;
222 static bool_t
223 svcudp_recv (xprt, msg)
224 SVCXPRT *xprt;
225 struct rpc_msg *msg;
227 struct svcudp_data *su = su_data (xprt);
228 XDR *xdrs = &(su->su_xdrs);
229 int rlen;
230 char *reply;
231 u_long replylen;
232 socklen_t len;
234 /* It is very tricky when you have IP aliases. We want to make sure
235 that we are sending the packet from the IP address where the
236 incoming packet is addressed to. H.J. */
237 #ifdef IP_PKTINFO
238 struct iovec *iovp;
239 struct msghdr *mesgp;
240 #endif
242 again:
243 /* FIXME -- should xp_addrlen be a size_t? */
244 len = (socklen_t) sizeof(struct sockaddr_in);
245 #ifdef IP_PKTINFO
246 iovp = (struct iovec *) &xprt->xp_pad [0];
247 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
248 if (mesgp->msg_iovlen)
250 iovp->iov_base = rpc_buffer (xprt);
251 iovp->iov_len = su->su_iosz;
252 mesgp->msg_iov = iovp;
253 mesgp->msg_iovlen = 1;
254 mesgp->msg_name = &(xprt->xp_raddr);
255 mesgp->msg_namelen = len;
256 mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
257 + sizeof (struct msghdr)];
258 mesgp->msg_controllen = sizeof(xprt->xp_pad)
259 - sizeof (struct iovec) - sizeof (struct msghdr);
260 rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
261 if (rlen >= 0)
262 len = mesgp->msg_namelen;
264 else
265 #endif
266 rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
267 (int) su->su_iosz, 0,
268 (struct sockaddr *) &(xprt->xp_raddr), &len);
269 xprt->xp_addrlen = len;
270 if (rlen == -1 && errno == EINTR)
271 goto again;
272 if (rlen < 16) /* < 4 32-bit ints? */
273 return FALSE;
274 xdrs->x_op = XDR_DECODE;
275 XDR_SETPOS (xdrs, 0);
276 if (!INTUSE(xdr_callmsg) (xdrs, msg))
277 return FALSE;
278 su->su_xid = msg->rm_xid;
279 if (su->su_cache != NULL)
281 if (cache_get (xprt, msg, &reply, &replylen))
283 #ifdef IP_PKTINFO
284 if (mesgp->msg_iovlen)
286 iovp->iov_base = reply;
287 iovp->iov_len = replylen;
288 (void) __sendmsg (xprt->xp_sock, mesgp, 0);
290 else
291 #endif
292 (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
293 (struct sockaddr *) &xprt->xp_raddr, len);
294 return TRUE;
297 return TRUE;
300 static bool_t
301 svcudp_reply (xprt, msg)
302 SVCXPRT *xprt;
303 struct rpc_msg *msg;
305 struct svcudp_data *su = su_data (xprt);
306 XDR *xdrs = &(su->su_xdrs);
307 int slen, sent;
308 bool_t stat = FALSE;
309 #ifdef IP_PKTINFO
310 struct iovec *iovp;
311 struct msghdr *mesgp;
312 #endif
314 xdrs->x_op = XDR_ENCODE;
315 XDR_SETPOS (xdrs, 0);
316 msg->rm_xid = su->su_xid;
317 if (INTUSE(xdr_replymsg) (xdrs, msg))
319 slen = (int) XDR_GETPOS (xdrs);
320 #ifdef IP_PKTINFO
321 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
322 if (mesgp->msg_iovlen)
324 iovp = (struct iovec *) &xprt->xp_pad [0];
325 iovp->iov_base = rpc_buffer (xprt);
326 iovp->iov_len = slen;
327 sent = __sendmsg (xprt->xp_sock, mesgp, 0);
329 else
330 #endif
331 sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
332 (struct sockaddr *) &(xprt->xp_raddr),
333 xprt->xp_addrlen);
334 if (sent == slen)
336 stat = TRUE;
337 if (su->su_cache && slen >= 0)
339 cache_set (xprt, (u_long) slen);
343 return stat;
346 static bool_t
347 svcudp_getargs (xprt, xdr_args, args_ptr)
348 SVCXPRT *xprt;
349 xdrproc_t xdr_args;
350 caddr_t args_ptr;
353 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
356 static bool_t
357 svcudp_freeargs (xprt, xdr_args, args_ptr)
358 SVCXPRT *xprt;
359 xdrproc_t xdr_args;
360 caddr_t args_ptr;
362 XDR *xdrs = &(su_data (xprt)->su_xdrs);
364 xdrs->x_op = XDR_FREE;
365 return (*xdr_args) (xdrs, args_ptr);
368 static void
369 svcudp_destroy (xprt)
370 SVCXPRT *xprt;
372 struct svcudp_data *su = su_data (xprt);
374 xprt_unregister (xprt);
375 (void) __close (xprt->xp_sock);
376 XDR_DESTROY (&(su->su_xdrs));
377 mem_free (rpc_buffer (xprt), su->su_iosz);
378 mem_free ((caddr_t) su, sizeof (struct svcudp_data));
379 mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
383 /***********this could be a separate file*********************/
386 * Fifo cache for udp server
387 * Copies pointers to reply buffers into fifo cache
388 * Buffers are sent again if retransmissions are detected.
391 #define SPARSENESS 4 /* 75% sparse */
393 #ifdef USE_IN_LIBIO
394 # define CACHE_PERROR(msg) \
395 if (_IO_fwide (stderr, 0) > 0) \
396 (void) __fwprintf(stderr, L"%s\n", msg); \
397 else \
398 (void) fprintf(stderr, "%s\n", msg)
399 #else
400 # define CACHE_PERROR(msg) \
401 (void) fprintf(stderr,"%s\n", msg)
402 #endif
404 #define ALLOC(type, size) \
405 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
407 #define BZERO(addr, type, size) \
408 __bzero((char *) addr, sizeof(type) * (int) (size))
411 * An entry in the cache
413 typedef struct cache_node *cache_ptr;
414 struct cache_node
417 * Index into cache is xid, proc, vers, prog and address
419 u_long cache_xid;
420 u_long cache_proc;
421 u_long cache_vers;
422 u_long cache_prog;
423 struct sockaddr_in cache_addr;
425 * The cached reply and length
427 char *cache_reply;
428 u_long cache_replylen;
430 * Next node on the list, if there is a collision
432 cache_ptr cache_next;
438 * The entire cache
440 struct udp_cache
442 u_long uc_size; /* size of cache */
443 cache_ptr *uc_entries; /* hash table of entries in cache */
444 cache_ptr *uc_fifo; /* fifo list of entries in cache */
445 u_long uc_nextvictim; /* points to next victim in fifo list */
446 u_long uc_prog; /* saved program number */
447 u_long uc_vers; /* saved version number */
448 u_long uc_proc; /* saved procedure number */
449 struct sockaddr_in uc_addr; /* saved caller's address */
454 * the hashing function
456 #define CACHE_LOC(transp, xid) \
457 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
461 * Enable use of the cache.
462 * Note: there is no disable.
465 svcudp_enablecache (SVCXPRT *transp, u_long size)
467 struct svcudp_data *su = su_data (transp);
468 struct udp_cache *uc;
470 if (su->su_cache != NULL)
472 CACHE_PERROR (_("enablecache: cache already enabled"));
473 return 0;
475 uc = ALLOC (struct udp_cache, 1);
476 if (uc == NULL)
478 CACHE_PERROR (_("enablecache: could not allocate cache"));
479 return 0;
481 uc->uc_size = size;
482 uc->uc_nextvictim = 0;
483 uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
484 if (uc->uc_entries == NULL)
486 CACHE_PERROR (_("enablecache: could not allocate cache data"));
487 return 0;
489 BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
490 uc->uc_fifo = ALLOC (cache_ptr, size);
491 if (uc->uc_fifo == NULL)
493 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
494 return 0;
496 BZERO (uc->uc_fifo, cache_ptr, size);
497 su->su_cache = (char *) uc;
498 return 1;
503 * Set an entry in the cache
505 static void
506 cache_set (SVCXPRT *xprt, u_long replylen)
508 cache_ptr victim;
509 cache_ptr *vicp;
510 struct svcudp_data *su = su_data (xprt);
511 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
512 u_int loc;
513 char *newbuf;
516 * Find space for the new entry, either by
517 * reusing an old entry, or by mallocing a new one
519 victim = uc->uc_fifo[uc->uc_nextvictim];
520 if (victim != NULL)
522 loc = CACHE_LOC (xprt, victim->cache_xid);
523 for (vicp = &uc->uc_entries[loc];
524 *vicp != NULL && *vicp != victim;
525 vicp = &(*vicp)->cache_next)
527 if (*vicp == NULL)
529 CACHE_PERROR (_("cache_set: victim not found"));
530 return;
532 *vicp = victim->cache_next; /* remote from cache */
533 newbuf = victim->cache_reply;
535 else
537 victim = ALLOC (struct cache_node, 1);
538 if (victim == NULL)
540 CACHE_PERROR (_("cache_set: victim alloc failed"));
541 return;
543 newbuf = mem_alloc (su->su_iosz);
544 if (newbuf == NULL)
546 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
547 return;
552 * Store it away
554 victim->cache_replylen = replylen;
555 victim->cache_reply = rpc_buffer (xprt);
556 rpc_buffer (xprt) = newbuf;
557 INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
558 XDR_ENCODE);
559 victim->cache_xid = su->su_xid;
560 victim->cache_proc = uc->uc_proc;
561 victim->cache_vers = uc->uc_vers;
562 victim->cache_prog = uc->uc_prog;
563 victim->cache_addr = uc->uc_addr;
564 loc = CACHE_LOC (xprt, victim->cache_xid);
565 victim->cache_next = uc->uc_entries[loc];
566 uc->uc_entries[loc] = victim;
567 uc->uc_fifo[uc->uc_nextvictim++] = victim;
568 uc->uc_nextvictim %= uc->uc_size;
572 * Try to get an entry from the cache
573 * return 1 if found, 0 if not found
575 static int
576 cache_get (xprt, msg, replyp, replylenp)
577 SVCXPRT *xprt;
578 struct rpc_msg *msg;
579 char **replyp;
580 u_long *replylenp;
582 u_int loc;
583 cache_ptr ent;
584 struct svcudp_data *su = su_data (xprt);
585 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
587 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
589 loc = CACHE_LOC (xprt, su->su_xid);
590 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
592 if (ent->cache_xid == su->su_xid &&
593 ent->cache_proc == uc->uc_proc &&
594 ent->cache_vers == uc->uc_vers &&
595 ent->cache_prog == uc->uc_prog &&
596 EQADDR (ent->cache_addr, uc->uc_addr))
598 *replyp = ent->cache_reply;
599 *replylenp = ent->cache_replylen;
600 return 1;
604 * Failed to find entry
605 * Remember a few things so we can do a set later
607 uc->uc_proc = msg->rm_call.cb_proc;
608 uc->uc_vers = msg->rm_call.cb_vers;
609 uc->uc_prog = msg->rm_call.cb_prog;
610 memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
611 return 0;