Update.
[glibc.git] / sunrpc / svc_udp.c
blobf912260ef56be2273447bffedef3fb7749a6bf96
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 <libio/iolibio.h>
56 # define fputs(s, f) _IO_fputs (s, f)
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;
123 if (sock == RPC_ANYSOCK)
125 if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
127 perror (_("svcudp_create: socket creation problem"));
128 return (SVCXPRT *) NULL;
130 madesock = TRUE;
132 __bzero ((char *) &addr, sizeof (addr));
133 addr.sin_family = AF_INET;
134 if (bindresvport (sock, &addr))
136 addr.sin_port = 0;
137 (void) bind (sock, (struct sockaddr *) &addr, len);
139 if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
141 perror (_("svcudp_create - cannot getsockname"));
142 if (madesock)
143 (void) __close (sock);
144 return (SVCXPRT *) NULL;
146 xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
147 if (xprt == NULL)
149 (void) fputs (_("svcudp_create: out of memory\n"), stderr);
150 return NULL;
152 su = (struct svcudp_data *) mem_alloc (sizeof (*su));
153 if (su == NULL)
155 (void) fputs (_("svcudp_create: out of memory\n"), stderr);
156 return NULL;
158 su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
159 if ((rpc_buffer (xprt) = mem_alloc (su->su_iosz)) == NULL)
161 (void) fputs (_("svcudp_create: out of memory\n"), stderr);
162 return NULL;
164 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE);
165 su->su_cache = NULL;
166 xprt->xp_p2 = (caddr_t) su;
167 xprt->xp_verf.oa_base = su->su_verfbody;
168 xprt->xp_ops = &svcudp_op;
169 xprt->xp_port = ntohs (addr.sin_port);
170 xprt->xp_sock = sock;
172 #ifdef IP_PKTINFO
173 if ((sizeof (struct iovec) + sizeof (struct msghdr)
174 + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
175 > sizeof (xprt->xp_pad))
177 (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
178 stderr);
179 return NULL;
181 pad = 1;
182 if (setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
183 sizeof (pad)) == 0)
184 /* Set the padding to all 1s. */
185 pad = 0xff;
186 else
187 #endif
188 /* Clear the padding. */
189 pad = 0;
190 memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
192 xprt_register (xprt);
193 return xprt;
196 SVCXPRT *
197 svcudp_create (sock)
198 int sock;
201 return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
204 static enum xprt_stat
205 svcudp_stat (xprt)
206 SVCXPRT *xprt;
209 return XPRT_IDLE;
212 static bool_t
213 svcudp_recv (xprt, msg)
214 SVCXPRT *xprt;
215 struct rpc_msg *msg;
217 struct svcudp_data *su = su_data (xprt);
218 XDR *xdrs = &(su->su_xdrs);
219 int rlen;
220 char *reply;
221 u_long replylen;
222 socklen_t len;
224 /* It is very tricky when you have IP aliases. We want to make sure
225 that we are sending the packet from the IP address where the
226 incoming packet is addressed to. H.J. */
227 #ifdef IP_PKTINFO
228 struct iovec *iovp;
229 struct msghdr *mesgp;
230 #endif
232 again:
233 /* FIXME -- should xp_addrlen be a size_t? */
234 len = (socklen_t) sizeof(struct sockaddr_in);
235 #ifdef IP_PKTINFO
236 iovp = (struct iovec *) &xprt->xp_pad [0];
237 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
238 if (mesgp->msg_iovlen)
240 iovp->iov_base = rpc_buffer (xprt);
241 iovp->iov_len = su->su_iosz;
242 mesgp->msg_iov = iovp;
243 mesgp->msg_iovlen = 1;
244 mesgp->msg_name = &(xprt->xp_raddr);
245 mesgp->msg_namelen = len;
246 mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
247 + sizeof (struct msghdr)];
248 mesgp->msg_controllen = sizeof(xprt->xp_pad)
249 - sizeof (struct iovec) - sizeof (struct msghdr);
250 rlen = recvmsg (xprt->xp_sock, mesgp, 0);
251 if (rlen >= 0)
252 len = mesgp->msg_namelen;
254 else
255 #endif
256 rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt),
257 (int) su->su_iosz, 0,
258 (struct sockaddr *) &(xprt->xp_raddr), &len);
259 xprt->xp_addrlen = len;
260 if (rlen == -1 && errno == EINTR)
261 goto again;
262 if (rlen < 16) /* < 4 32-bit ints? */
263 return FALSE;
264 xdrs->x_op = XDR_DECODE;
265 XDR_SETPOS (xdrs, 0);
266 if (!xdr_callmsg (xdrs, msg))
267 return FALSE;
268 su->su_xid = msg->rm_xid;
269 if (su->su_cache != NULL)
271 if (cache_get (xprt, msg, &reply, &replylen))
273 #ifdef IP_PKTINFO
274 if (mesgp->msg_iovlen)
276 iovp->iov_base = reply;
277 iovp->iov_len = replylen;
278 (void) sendmsg (xprt->xp_sock, mesgp, 0);
280 else
281 #endif
282 (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
283 (struct sockaddr *) &xprt->xp_raddr, len);
284 return TRUE;
287 return TRUE;
290 static bool_t
291 svcudp_reply (xprt, msg)
292 SVCXPRT *xprt;
293 struct rpc_msg *msg;
295 struct svcudp_data *su = su_data (xprt);
296 XDR *xdrs = &(su->su_xdrs);
297 int slen, sent;
298 bool_t stat = FALSE;
299 #ifdef IP_PKTINFO
300 struct iovec *iovp;
301 struct msghdr *mesgp;
302 #endif
304 xdrs->x_op = XDR_ENCODE;
305 XDR_SETPOS (xdrs, 0);
306 msg->rm_xid = su->su_xid;
307 if (xdr_replymsg (xdrs, msg))
309 slen = (int) XDR_GETPOS (xdrs);
310 #ifdef IP_PKTINFO
311 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
312 if (mesgp->msg_iovlen)
314 iovp = (struct iovec *) &xprt->xp_pad [0];
315 iovp->iov_base = rpc_buffer (xprt);
316 iovp->iov_len = slen;
317 sent = sendmsg (xprt->xp_sock, mesgp, 0);
319 else
320 #endif
321 sent = sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
322 (struct sockaddr *) &(xprt->xp_raddr),
323 xprt->xp_addrlen);
324 if (sent == slen)
326 stat = TRUE;
327 if (su->su_cache && slen >= 0)
329 cache_set (xprt, (u_long) slen);
333 return stat;
336 static bool_t
337 svcudp_getargs (xprt, xdr_args, args_ptr)
338 SVCXPRT *xprt;
339 xdrproc_t xdr_args;
340 caddr_t args_ptr;
343 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
346 static bool_t
347 svcudp_freeargs (xprt, xdr_args, args_ptr)
348 SVCXPRT *xprt;
349 xdrproc_t xdr_args;
350 caddr_t args_ptr;
352 XDR *xdrs = &(su_data (xprt)->su_xdrs);
354 xdrs->x_op = XDR_FREE;
355 return (*xdr_args) (xdrs, args_ptr);
358 static void
359 svcudp_destroy (xprt)
360 SVCXPRT *xprt;
362 struct svcudp_data *su = su_data (xprt);
364 xprt_unregister (xprt);
365 (void) __close (xprt->xp_sock);
366 XDR_DESTROY (&(su->su_xdrs));
367 mem_free (rpc_buffer (xprt), su->su_iosz);
368 mem_free ((caddr_t) su, sizeof (struct svcudp_data));
369 mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
373 /***********this could be a separate file*********************/
376 * Fifo cache for udp server
377 * Copies pointers to reply buffers into fifo cache
378 * Buffers are sent again if retransmissions are detected.
381 #define SPARSENESS 4 /* 75% sparse */
383 #define CACHE_PERROR(msg) \
384 (void) fprintf(stderr,"%s\n", msg)
386 #define ALLOC(type, size) \
387 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
389 #define BZERO(addr, type, size) \
390 __bzero((char *) addr, sizeof(type) * (int) (size))
393 * An entry in the cache
395 typedef struct cache_node *cache_ptr;
396 struct cache_node
399 * Index into cache is xid, proc, vers, prog and address
401 u_long cache_xid;
402 u_long cache_proc;
403 u_long cache_vers;
404 u_long cache_prog;
405 struct sockaddr_in cache_addr;
407 * The cached reply and length
409 char *cache_reply;
410 u_long cache_replylen;
412 * Next node on the list, if there is a collision
414 cache_ptr cache_next;
420 * The entire cache
422 struct udp_cache
424 u_long uc_size; /* size of cache */
425 cache_ptr *uc_entries; /* hash table of entries in cache */
426 cache_ptr *uc_fifo; /* fifo list of entries in cache */
427 u_long uc_nextvictim; /* points to next victim in fifo list */
428 u_long uc_prog; /* saved program number */
429 u_long uc_vers; /* saved version number */
430 u_long uc_proc; /* saved procedure number */
431 struct sockaddr_in uc_addr; /* saved caller's address */
436 * the hashing function
438 #define CACHE_LOC(transp, xid) \
439 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
443 * Enable use of the cache.
444 * Note: there is no disable.
447 svcudp_enablecache (SVCXPRT *transp, u_long size)
449 struct svcudp_data *su = su_data (transp);
450 struct udp_cache *uc;
452 if (su->su_cache != NULL)
454 CACHE_PERROR (_("enablecache: cache already enabled"));
455 return 0;
457 uc = ALLOC (struct udp_cache, 1);
458 if (uc == NULL)
460 CACHE_PERROR (_("enablecache: could not allocate cache"));
461 return 0;
463 uc->uc_size = size;
464 uc->uc_nextvictim = 0;
465 uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
466 if (uc->uc_entries == NULL)
468 CACHE_PERROR (_("enablecache: could not allocate cache data"));
469 return 0;
471 BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
472 uc->uc_fifo = ALLOC (cache_ptr, size);
473 if (uc->uc_fifo == NULL)
475 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
476 return 0;
478 BZERO (uc->uc_fifo, cache_ptr, size);
479 su->su_cache = (char *) uc;
480 return 1;
485 * Set an entry in the cache
487 static void
488 cache_set (SVCXPRT *xprt, u_long replylen)
490 cache_ptr victim;
491 cache_ptr *vicp;
492 struct svcudp_data *su = su_data (xprt);
493 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
494 u_int loc;
495 char *newbuf;
498 * Find space for the new entry, either by
499 * reusing an old entry, or by mallocing a new one
501 victim = uc->uc_fifo[uc->uc_nextvictim];
502 if (victim != NULL)
504 loc = CACHE_LOC (xprt, victim->cache_xid);
505 for (vicp = &uc->uc_entries[loc];
506 *vicp != NULL && *vicp != victim;
507 vicp = &(*vicp)->cache_next)
509 if (*vicp == NULL)
511 CACHE_PERROR (_("cache_set: victim not found"));
512 return;
514 *vicp = victim->cache_next; /* remote from cache */
515 newbuf = victim->cache_reply;
517 else
519 victim = ALLOC (struct cache_node, 1);
520 if (victim == NULL)
522 CACHE_PERROR (_("cache_set: victim alloc failed"));
523 return;
525 newbuf = mem_alloc (su->su_iosz);
526 if (newbuf == NULL)
528 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
529 return;
534 * Store it away
536 victim->cache_replylen = replylen;
537 victim->cache_reply = rpc_buffer (xprt);
538 rpc_buffer (xprt) = newbuf;
539 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
540 victim->cache_xid = su->su_xid;
541 victim->cache_proc = uc->uc_proc;
542 victim->cache_vers = uc->uc_vers;
543 victim->cache_prog = uc->uc_prog;
544 victim->cache_addr = uc->uc_addr;
545 loc = CACHE_LOC (xprt, victim->cache_xid);
546 victim->cache_next = uc->uc_entries[loc];
547 uc->uc_entries[loc] = victim;
548 uc->uc_fifo[uc->uc_nextvictim++] = victim;
549 uc->uc_nextvictim %= uc->uc_size;
553 * Try to get an entry from the cache
554 * return 1 if found, 0 if not found
556 static int
557 cache_get (xprt, msg, replyp, replylenp)
558 SVCXPRT *xprt;
559 struct rpc_msg *msg;
560 char **replyp;
561 u_long *replylenp;
563 u_int loc;
564 cache_ptr ent;
565 struct svcudp_data *su = su_data (xprt);
566 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
568 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
570 loc = CACHE_LOC (xprt, su->su_xid);
571 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
573 if (ent->cache_xid == su->su_xid &&
574 ent->cache_proc == uc->uc_proc &&
575 ent->cache_vers == uc->uc_vers &&
576 ent->cache_prog == uc->uc_prog &&
577 EQADDR (ent->cache_addr, uc->uc_addr))
579 *replyp = ent->cache_reply;
580 *replylenp = ent->cache_replylen;
581 return 1;
585 * Failed to find entry
586 * Remember a few things so we can do a set later
588 uc->uc_proc = msg->rm_call.cb_proc;
589 uc->uc_vers = msg->rm_call.cb_vers;
590 uc->uc_prog = msg->rm_call.cb_prog;
591 memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
592 return 0;