1 /* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */
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.
28 * Mountain View, California 94043
31 static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
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 #define xprt_register __xprt_register
43 #define xdrmem_create __xdrmem_create
44 #define xdr_callmsg __xdr_callmsg
45 #define xdr_replymsg __xdr_replymsg
55 #include <sys/socket.h>
64 # include <libio/iolibio.h>
65 # define fputs(s, f) _IO_fputs (s, f)
68 #define rpc_buffer(xprt) ((xprt)->xp_p1)
70 #define MAX(a, b) ((a > b) ? a : b)
73 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
74 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
75 static enum xprt_stat svcudp_stat (SVCXPRT *);
76 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
77 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
78 static void svcudp_destroy (SVCXPRT *);
80 static const struct xp_ops svcudp_op =
90 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
92 static void cache_set (SVCXPRT *xprt, u_long replylen);
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 */
105 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
109 * xprt = svcudp_create(sock);
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.
120 SVCXPRT attribute_hidden *
121 __svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
123 bool_t madesock = FALSE;
125 struct svcudp_data *su;
126 struct sockaddr_in addr;
127 socklen_t len = sizeof (struct sockaddr_in);
131 if (sock == RPC_ANYSOCK)
133 if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
135 __perror (_("svcudp_create: socket creation problem"));
136 return (SVCXPRT *) NULL;
140 __memset ((char *) &addr, 0, sizeof (addr));
141 addr.sin_family = AF_INET;
142 if (bindresvport (sock, &addr))
145 (void) bind (sock, (struct sockaddr *) &addr, len);
147 if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
149 __perror (_("svcudp_create - cannot getsockname"));
151 (void) __close (sock);
152 return (SVCXPRT *) NULL;
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)
160 if (_IO_fwide (stderr, 0) > 0)
161 (void) __fwprintf (stderr, L"%s", _("svcudp_create: out of memory\n"));
164 (void) fputs (_("svcudp_create: out of memory\n"), stderr);
165 mem_free (xprt, sizeof (SVCXPRT));
166 mem_free (su, sizeof (*su));
167 mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
170 su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
171 rpc_buffer (xprt) = buf;
172 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE);
174 xprt->xp_p2 = (caddr_t) su;
175 xprt->xp_verf.oa_base = su->su_verfbody;
176 xprt->xp_ops = &svcudp_op;
177 xprt->xp_port = ntohs (addr.sin_port);
178 xprt->xp_sock = sock;
181 if ((sizeof (struct iovec) + sizeof (struct msghdr)
182 + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
183 > sizeof (xprt->xp_pad))
186 if (_IO_fwide (stderr, 0) > 0)
187 (void) __fwprintf (stderr, L"%s",
188 _("svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
191 (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
196 if (setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
198 /* Set the padding to all 1s. */
202 /* Clear the padding. */
204 __memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
206 xprt_register (xprt);
209 strong_alias(__svcudp_bufcreate,svcudp_bufcreate)
211 SVCXPRT attribute_hidden *
212 __svcudp_create (int sock)
215 return __svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
217 strong_alias(__svcudp_create,svcudp_create)
219 static enum xprt_stat
228 svcudp_recv (xprt, msg)
232 struct svcudp_data *su = su_data (xprt);
233 XDR *xdrs = &(su->su_xdrs);
239 /* It is very tricky when you have IP aliases. We want to make sure
240 that we are sending the packet from the IP address where the
241 incoming packet is addressed to. H.J. */
244 struct msghdr *mesgp;
248 /* FIXME -- should xp_addrlen be a size_t? */
249 len = (socklen_t) sizeof(struct sockaddr_in);
251 iovp = (struct iovec *) &xprt->xp_pad [0];
252 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
253 if (mesgp->msg_iovlen)
255 iovp->iov_base = rpc_buffer (xprt);
256 iovp->iov_len = su->su_iosz;
257 mesgp->msg_iov = iovp;
258 mesgp->msg_iovlen = 1;
259 mesgp->msg_name = &(xprt->xp_raddr);
260 mesgp->msg_namelen = len;
261 mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
262 + sizeof (struct msghdr)];
263 mesgp->msg_controllen = sizeof(xprt->xp_pad)
264 - sizeof (struct iovec) - sizeof (struct msghdr);
265 rlen = recvmsg (xprt->xp_sock, mesgp, 0);
267 len = mesgp->msg_namelen;
271 rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt),
272 (int) su->su_iosz, 0,
273 (struct sockaddr *) &(xprt->xp_raddr), &len);
274 xprt->xp_addrlen = len;
275 if (rlen == -1 && errno == EINTR)
277 if (rlen < 16) /* < 4 32-bit ints? */
279 xdrs->x_op = XDR_DECODE;
280 XDR_SETPOS (xdrs, 0);
281 if (!xdr_callmsg (xdrs, msg))
283 su->su_xid = msg->rm_xid;
284 if (su->su_cache != NULL)
286 if (cache_get (xprt, msg, &reply, &replylen))
289 if (mesgp->msg_iovlen)
291 iovp->iov_base = reply;
292 iovp->iov_len = replylen;
293 (void) sendmsg (xprt->xp_sock, mesgp, 0);
297 (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
298 (struct sockaddr *) &xprt->xp_raddr, len);
306 svcudp_reply (xprt, msg)
310 struct svcudp_data *su = su_data (xprt);
311 XDR *xdrs = &(su->su_xdrs);
316 struct msghdr *mesgp;
319 xdrs->x_op = XDR_ENCODE;
320 XDR_SETPOS (xdrs, 0);
321 msg->rm_xid = su->su_xid;
322 if (xdr_replymsg (xdrs, msg))
324 slen = (int) XDR_GETPOS (xdrs);
326 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
327 if (mesgp->msg_iovlen)
329 iovp = (struct iovec *) &xprt->xp_pad [0];
330 iovp->iov_base = rpc_buffer (xprt);
331 iovp->iov_len = slen;
332 sent = sendmsg (xprt->xp_sock, mesgp, 0);
336 sent = sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
337 (struct sockaddr *) &(xprt->xp_raddr),
342 if (su->su_cache && slen >= 0)
344 cache_set (xprt, (u_long) slen);
352 svcudp_getargs (xprt, xdr_args, args_ptr)
358 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
362 svcudp_freeargs (xprt, xdr_args, args_ptr)
367 XDR *xdrs = &(su_data (xprt)->su_xdrs);
369 xdrs->x_op = XDR_FREE;
370 return (*xdr_args) (xdrs, args_ptr);
374 svcudp_destroy (xprt)
377 struct svcudp_data *su = su_data (xprt);
379 xprt_unregister (xprt);
380 (void) __close (xprt->xp_sock);
381 XDR_DESTROY (&(su->su_xdrs));
382 mem_free (rpc_buffer (xprt), su->su_iosz);
383 mem_free ((caddr_t) su, sizeof (struct svcudp_data));
384 mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
388 /***********this could be a separate file*********************/
391 * Fifo cache for udp server
392 * Copies pointers to reply buffers into fifo cache
393 * Buffers are sent again if retransmissions are detected.
396 #define SPARSENESS 4 /* 75% sparse */
399 # define CACHE_PERROR(msg) \
400 if (_IO_fwide (stderr, 0) > 0) \
401 (void) __fwprintf(stderr, L"%s\n", msg); \
403 (void) fprintf(stderr, "%s\n", msg)
405 # define CACHE_PERROR(msg) \
406 (void) fprintf(stderr,"%s\n", msg)
409 #define ALLOC(type, size) \
410 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
412 #define BZERO(addr, type, size) \
413 __memset((char *) addr, 0, sizeof(type) * (int) (size))
416 * An entry in the cache
418 typedef struct cache_node *cache_ptr;
422 * Index into cache is xid, proc, vers, prog and address
428 struct sockaddr_in cache_addr;
430 * The cached reply and length
433 u_long cache_replylen;
435 * Next node on the list, if there is a collision
437 cache_ptr cache_next;
447 u_long uc_size; /* size of cache */
448 cache_ptr *uc_entries; /* hash table of entries in cache */
449 cache_ptr *uc_fifo; /* fifo list of entries in cache */
450 u_long uc_nextvictim; /* points to next victim in fifo list */
451 u_long uc_prog; /* saved program number */
452 u_long uc_vers; /* saved version number */
453 u_long uc_proc; /* saved procedure number */
454 struct sockaddr_in uc_addr; /* saved caller's address */
459 * the hashing function
461 #define CACHE_LOC(transp, xid) \
462 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
466 * Enable use of the cache.
467 * Note: there is no disable.
470 svcudp_enablecache (SVCXPRT *transp, u_long size)
472 struct svcudp_data *su = su_data (transp);
473 struct udp_cache *uc;
475 if (su->su_cache != NULL)
477 CACHE_PERROR (_("enablecache: cache already enabled"));
480 uc = ALLOC (struct udp_cache, 1);
483 CACHE_PERROR (_("enablecache: could not allocate cache"));
487 uc->uc_nextvictim = 0;
488 uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
489 if (uc->uc_entries == NULL)
491 CACHE_PERROR (_("enablecache: could not allocate cache data"));
494 BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
495 uc->uc_fifo = ALLOC (cache_ptr, size);
496 if (uc->uc_fifo == NULL)
498 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
501 BZERO (uc->uc_fifo, cache_ptr, size);
502 su->su_cache = (char *) uc;
508 * Set an entry in the cache
511 cache_set (SVCXPRT *xprt, u_long replylen)
515 struct svcudp_data *su = su_data (xprt);
516 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
521 * Find space for the new entry, either by
522 * reusing an old entry, or by mallocing a new one
524 victim = uc->uc_fifo[uc->uc_nextvictim];
527 loc = CACHE_LOC (xprt, victim->cache_xid);
528 for (vicp = &uc->uc_entries[loc];
529 *vicp != NULL && *vicp != victim;
530 vicp = &(*vicp)->cache_next)
534 CACHE_PERROR (_("cache_set: victim not found"));
537 *vicp = victim->cache_next; /* remote from cache */
538 newbuf = victim->cache_reply;
542 victim = ALLOC (struct cache_node, 1);
545 CACHE_PERROR (_("cache_set: victim alloc failed"));
548 newbuf = mem_alloc (su->su_iosz);
551 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
559 victim->cache_replylen = replylen;
560 victim->cache_reply = rpc_buffer (xprt);
561 rpc_buffer (xprt) = newbuf;
562 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
563 victim->cache_xid = su->su_xid;
564 victim->cache_proc = uc->uc_proc;
565 victim->cache_vers = uc->uc_vers;
566 victim->cache_prog = uc->uc_prog;
567 victim->cache_addr = uc->uc_addr;
568 loc = CACHE_LOC (xprt, victim->cache_xid);
569 victim->cache_next = uc->uc_entries[loc];
570 uc->uc_entries[loc] = victim;
571 uc->uc_fifo[uc->uc_nextvictim++] = victim;
572 uc->uc_nextvictim %= uc->uc_size;
576 * Try to get an entry from the cache
577 * return 1 if found, 0 if not found
580 cache_get (xprt, msg, replyp, replylenp)
588 struct svcudp_data *su = su_data (xprt);
589 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
591 #define EQADDR(a1, a2) (__memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
593 loc = CACHE_LOC (xprt, su->su_xid);
594 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
596 if (ent->cache_xid == su->su_xid &&
597 ent->cache_proc == uc->uc_proc &&
598 ent->cache_vers == uc->uc_vers &&
599 ent->cache_prog == uc->uc_prog &&
600 EQADDR (ent->cache_addr, uc->uc_addr))
602 *replyp = ent->cache_reply;
603 *replylenp = ent->cache_replylen;
608 * Failed to find entry
609 * Remember a few things so we can do a set later
611 uc->uc_proc = msg->rm_call.cb_proc;
612 uc->uc_vers = msg->rm_call.cb_vers;
613 uc->uc_prog = msg->rm_call.cb_prog;
614 __memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));