Update.
[glibc.git] / sunrpc / svc_udp.c
blob0410e9c008ebf3f11ad87481aacf5dada4df0d8d
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 #ifdef USE_IN_LIBIO
153 if (_IO_fwide (stderr, 0) > 0)
154 (void) __fwprintf (stderr, L"%s", _("svcudp_create: out of memory\n"));
155 else
156 #endif
157 (void) fputs (_("svcudp_create: out of memory\n"), stderr);
158 mem_free (xprt, sizeof (SVCXPRT));
159 mem_free (su, sizeof (*su));
160 mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
161 return NULL;
163 su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
164 rpc_buffer (xprt) = buf;
165 INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
166 XDR_DECODE);
167 su->su_cache = NULL;
168 xprt->xp_p2 = (caddr_t) su;
169 xprt->xp_verf.oa_base = su->su_verfbody;
170 xprt->xp_ops = &svcudp_op;
171 xprt->xp_port = ntohs (addr.sin_port);
172 xprt->xp_sock = sock;
174 #ifdef IP_PKTINFO
175 if ((sizeof (struct iovec) + sizeof (struct msghdr)
176 + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
177 > sizeof (xprt->xp_pad))
179 # ifdef USE_IN_LIBIO
180 if (_IO_fwide (stderr, 0) > 0)
181 (void) __fwprintf (stderr, L"%s",
182 _("svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
183 else
184 # endif
185 (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
186 stderr);
187 return NULL;
189 pad = 1;
190 if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
191 sizeof (pad)) == 0)
192 /* Set the padding to all 1s. */
193 pad = 0xff;
194 else
195 #endif
196 /* Clear the padding. */
197 pad = 0;
198 memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
200 xprt_register (xprt);
201 return xprt;
203 INTDEF (svcudp_bufcreate)
205 SVCXPRT *
206 svcudp_create (sock)
207 int sock;
209 return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
211 INTDEF (svcudp_create)
213 static enum xprt_stat
214 svcudp_stat (xprt)
215 SVCXPRT *xprt;
218 return XPRT_IDLE;
221 static bool_t
222 svcudp_recv (xprt, msg)
223 SVCXPRT *xprt;
224 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)
261 len = mesgp->msg_namelen;
263 else
264 #endif
265 rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
266 (int) su->su_iosz, 0,
267 (struct sockaddr *) &(xprt->xp_raddr), &len);
268 xprt->xp_addrlen = len;
269 if (rlen == -1 && errno == EINTR)
270 goto again;
271 if (rlen < 16) /* < 4 32-bit ints? */
272 return FALSE;
273 xdrs->x_op = XDR_DECODE;
274 XDR_SETPOS (xdrs, 0);
275 if (!INTUSE(xdr_callmsg) (xdrs, msg))
276 return FALSE;
277 su->su_xid = msg->rm_xid;
278 if (su->su_cache != NULL)
280 if (cache_get (xprt, msg, &reply, &replylen))
282 #ifdef IP_PKTINFO
283 if (mesgp->msg_iovlen)
285 iovp->iov_base = reply;
286 iovp->iov_len = replylen;
287 (void) __sendmsg (xprt->xp_sock, mesgp, 0);
289 else
290 #endif
291 (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
292 (struct sockaddr *) &xprt->xp_raddr, len);
293 return TRUE;
296 return TRUE;
299 static bool_t
300 svcudp_reply (xprt, msg)
301 SVCXPRT *xprt;
302 struct rpc_msg *msg;
304 struct svcudp_data *su = su_data (xprt);
305 XDR *xdrs = &(su->su_xdrs);
306 int slen, sent;
307 bool_t stat = FALSE;
308 #ifdef IP_PKTINFO
309 struct iovec *iovp;
310 struct msghdr *mesgp;
311 #endif
313 xdrs->x_op = XDR_ENCODE;
314 XDR_SETPOS (xdrs, 0);
315 msg->rm_xid = su->su_xid;
316 if (INTUSE(xdr_replymsg) (xdrs, msg))
318 slen = (int) XDR_GETPOS (xdrs);
319 #ifdef IP_PKTINFO
320 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
321 if (mesgp->msg_iovlen)
323 iovp = (struct iovec *) &xprt->xp_pad [0];
324 iovp->iov_base = rpc_buffer (xprt);
325 iovp->iov_len = slen;
326 sent = __sendmsg (xprt->xp_sock, mesgp, 0);
328 else
329 #endif
330 sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
331 (struct sockaddr *) &(xprt->xp_raddr),
332 xprt->xp_addrlen);
333 if (sent == slen)
335 stat = TRUE;
336 if (su->su_cache && slen >= 0)
338 cache_set (xprt, (u_long) slen);
342 return stat;
345 static bool_t
346 svcudp_getargs (xprt, xdr_args, args_ptr)
347 SVCXPRT *xprt;
348 xdrproc_t xdr_args;
349 caddr_t args_ptr;
352 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
355 static bool_t
356 svcudp_freeargs (xprt, xdr_args, args_ptr)
357 SVCXPRT *xprt;
358 xdrproc_t xdr_args;
359 caddr_t args_ptr;
361 XDR *xdrs = &(su_data (xprt)->su_xdrs);
363 xdrs->x_op = XDR_FREE;
364 return (*xdr_args) (xdrs, args_ptr);
367 static void
368 svcudp_destroy (xprt)
369 SVCXPRT *xprt;
371 struct svcudp_data *su = su_data (xprt);
373 xprt_unregister (xprt);
374 (void) __close (xprt->xp_sock);
375 XDR_DESTROY (&(su->su_xdrs));
376 mem_free (rpc_buffer (xprt), su->su_iosz);
377 mem_free ((caddr_t) su, sizeof (struct svcudp_data));
378 mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
382 /***********this could be a separate file*********************/
385 * Fifo cache for udp server
386 * Copies pointers to reply buffers into fifo cache
387 * Buffers are sent again if retransmissions are detected.
390 #define SPARSENESS 4 /* 75% sparse */
392 #ifdef USE_IN_LIBIO
393 # define CACHE_PERROR(msg) \
394 if (_IO_fwide (stderr, 0) > 0) \
395 (void) __fwprintf(stderr, L"%s\n", msg); \
396 else \
397 (void) fprintf(stderr, "%s\n", msg)
398 #else
399 # define CACHE_PERROR(msg) \
400 (void) fprintf(stderr,"%s\n", msg)
401 #endif
403 #define ALLOC(type, size) \
404 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
406 #define BZERO(addr, type, size) \
407 __bzero((char *) addr, sizeof(type) * (int) (size))
410 * An entry in the cache
412 typedef struct cache_node *cache_ptr;
413 struct cache_node
416 * Index into cache is xid, proc, vers, prog and address
418 u_long cache_xid;
419 u_long cache_proc;
420 u_long cache_vers;
421 u_long cache_prog;
422 struct sockaddr_in cache_addr;
424 * The cached reply and length
426 char *cache_reply;
427 u_long cache_replylen;
429 * Next node on the list, if there is a collision
431 cache_ptr cache_next;
437 * The entire cache
439 struct udp_cache
441 u_long uc_size; /* size of cache */
442 cache_ptr *uc_entries; /* hash table of entries in cache */
443 cache_ptr *uc_fifo; /* fifo list of entries in cache */
444 u_long uc_nextvictim; /* points to next victim in fifo list */
445 u_long uc_prog; /* saved program number */
446 u_long uc_vers; /* saved version number */
447 u_long uc_proc; /* saved procedure number */
448 struct sockaddr_in uc_addr; /* saved caller's address */
453 * the hashing function
455 #define CACHE_LOC(transp, xid) \
456 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
460 * Enable use of the cache.
461 * Note: there is no disable.
464 svcudp_enablecache (SVCXPRT *transp, u_long size)
466 struct svcudp_data *su = su_data (transp);
467 struct udp_cache *uc;
469 if (su->su_cache != NULL)
471 CACHE_PERROR (_("enablecache: cache already enabled"));
472 return 0;
474 uc = ALLOC (struct udp_cache, 1);
475 if (uc == NULL)
477 CACHE_PERROR (_("enablecache: could not allocate cache"));
478 return 0;
480 uc->uc_size = size;
481 uc->uc_nextvictim = 0;
482 uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
483 if (uc->uc_entries == NULL)
485 CACHE_PERROR (_("enablecache: could not allocate cache data"));
486 return 0;
488 BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
489 uc->uc_fifo = ALLOC (cache_ptr, size);
490 if (uc->uc_fifo == NULL)
492 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
493 return 0;
495 BZERO (uc->uc_fifo, cache_ptr, size);
496 su->su_cache = (char *) uc;
497 return 1;
502 * Set an entry in the cache
504 static void
505 cache_set (SVCXPRT *xprt, u_long replylen)
507 cache_ptr victim;
508 cache_ptr *vicp;
509 struct svcudp_data *su = su_data (xprt);
510 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
511 u_int loc;
512 char *newbuf;
515 * Find space for the new entry, either by
516 * reusing an old entry, or by mallocing a new one
518 victim = uc->uc_fifo[uc->uc_nextvictim];
519 if (victim != NULL)
521 loc = CACHE_LOC (xprt, victim->cache_xid);
522 for (vicp = &uc->uc_entries[loc];
523 *vicp != NULL && *vicp != victim;
524 vicp = &(*vicp)->cache_next)
526 if (*vicp == NULL)
528 CACHE_PERROR (_("cache_set: victim not found"));
529 return;
531 *vicp = victim->cache_next; /* remote from cache */
532 newbuf = victim->cache_reply;
534 else
536 victim = ALLOC (struct cache_node, 1);
537 if (victim == NULL)
539 CACHE_PERROR (_("cache_set: victim alloc failed"));
540 return;
542 newbuf = mem_alloc (su->su_iosz);
543 if (newbuf == NULL)
545 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
546 return;
551 * Store it away
553 victim->cache_replylen = replylen;
554 victim->cache_reply = rpc_buffer (xprt);
555 rpc_buffer (xprt) = newbuf;
556 INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
557 XDR_ENCODE);
558 victim->cache_xid = su->su_xid;
559 victim->cache_proc = uc->uc_proc;
560 victim->cache_vers = uc->uc_vers;
561 victim->cache_prog = uc->uc_prog;
562 victim->cache_addr = uc->uc_addr;
563 loc = CACHE_LOC (xprt, victim->cache_xid);
564 victim->cache_next = uc->uc_entries[loc];
565 uc->uc_entries[loc] = victim;
566 uc->uc_fifo[uc->uc_nextvictim++] = victim;
567 uc->uc_nextvictim %= uc->uc_size;
571 * Try to get an entry from the cache
572 * return 1 if found, 0 if not found
574 static int
575 cache_get (xprt, msg, replyp, replylenp)
576 SVCXPRT *xprt;
577 struct rpc_msg *msg;
578 char **replyp;
579 u_long *replylenp;
581 u_int loc;
582 cache_ptr ent;
583 struct svcudp_data *su = su_data (xprt);
584 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
586 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
588 loc = CACHE_LOC (xprt, su->su_xid);
589 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
591 if (ent->cache_xid == su->su_xid &&
592 ent->cache_proc == uc->uc_proc &&
593 ent->cache_vers == uc->uc_vers &&
594 ent->cache_prog == uc->uc_prog &&
595 EQADDR (ent->cache_addr, uc->uc_addr))
597 *replyp = ent->cache_reply;
598 *replylenp = ent->cache_replylen;
599 return 1;
603 * Failed to find entry
604 * Remember a few things so we can do a set later
606 uc->uc_proc = msg->rm_call.cb_proc;
607 uc->uc_vers = msg->rm_call.cb_vers;
608 uc->uc_prog = msg->rm_call.cb_prog;
609 memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
610 return 0;