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.
49 #include <sys/socket.h>
58 # include <libio/iolibio.h>
59 # define fputs(s, f) _IO_fputs (s, f)
63 #define rpc_buffer(xprt) ((xprt)->xp_p1)
65 #define MAX(a, b) ((a > b) ? a : b)
68 static bool_t
svcudp_recv (SVCXPRT
*, struct rpc_msg
*);
69 static bool_t
svcudp_reply (SVCXPRT
*, struct rpc_msg
*);
70 static enum xprt_stat
svcudp_stat (SVCXPRT
*);
71 static bool_t
svcudp_getargs (SVCXPRT
*, xdrproc_t
, caddr_t
);
72 static bool_t
svcudp_freeargs (SVCXPRT
*, xdrproc_t
, caddr_t
);
73 static void svcudp_destroy (SVCXPRT
*);
75 static const struct xp_ops svcudp_op
=
85 static int cache_get (SVCXPRT
*, struct rpc_msg
*, char **replyp
,
87 static void cache_set (SVCXPRT
*xprt
, u_long replylen
);
94 u_int su_iosz
; /* byte size of send.recv buffer */
95 u_long su_xid
; /* transaction id */
96 XDR su_xdrs
; /* XDR handle */
97 char su_verfbody
[MAX_AUTH_BYTES
]; /* verifier body */
98 char *su_cache
; /* cached data, NULL if no cache */
100 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
104 * xprt = svcudp_create(sock);
106 * If sock<0 then a socket is created, else sock is used.
107 * If the socket, sock is not bound to a port then svcudp_create
108 * binds it to an arbitrary port. In any (successful) case,
109 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
110 * associated port number.
111 * Once *xprt is initialized, it is registered as a transporter;
112 * see (svc.h, xprt_register).
113 * The routines returns NULL if a problem occurred.
116 svcudp_bufcreate (int sock
, u_int sendsz
, u_int recvsz
)
118 bool_t madesock
= FALSE
;
120 struct svcudp_data
*su
;
121 struct sockaddr_in addr
;
122 socklen_t len
= sizeof (struct sockaddr_in
);
126 if (sock
== RPC_ANYSOCK
)
128 if ((sock
= socket (AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) < 0)
130 perror (_("svcudp_create: socket creation problem"));
131 return (SVCXPRT
*) NULL
;
135 memset ((char *) &addr
, 0, sizeof (addr
));
136 addr
.sin_family
= AF_INET
;
137 if (bindresvport (sock
, &addr
))
140 (void) bind (sock
, (struct sockaddr
*) &addr
, len
);
142 if (getsockname (sock
, (struct sockaddr
*) &addr
, &len
) != 0)
144 perror (_("svcudp_create - cannot getsockname"));
147 return (SVCXPRT
*) NULL
;
149 xprt
= (SVCXPRT
*) mem_alloc (sizeof (SVCXPRT
));
150 su
= (struct svcudp_data
*) mem_alloc (sizeof (*su
));
151 buf
= mem_alloc (((MAX (sendsz
, recvsz
) + 3) / 4) * 4);
152 if (xprt
== NULL
|| su
== NULL
|| buf
== NULL
)
155 if (_IO_fwide (stderr
, 0) > 0)
156 (void) fwprintf (stderr
, L
"%s", _("svcudp_create: out of memory\n"));
159 (void) fputs (_("svcudp_create: out of memory\n"), stderr
);
160 mem_free (xprt
, sizeof (SVCXPRT
));
161 mem_free (su
, sizeof (*su
));
162 mem_free (buf
, ((MAX (sendsz
, recvsz
) + 3) / 4) * 4);
165 su
->su_iosz
= ((MAX (sendsz
, recvsz
) + 3) / 4) * 4;
166 rpc_buffer (xprt
) = buf
;
167 xdrmem_create (&(su
->su_xdrs
), rpc_buffer (xprt
), su
->su_iosz
, XDR_DECODE
);
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
;
176 if ((sizeof (struct iovec
) + sizeof (struct msghdr
)
177 + sizeof(struct cmsghdr
) + sizeof (struct in_pktinfo
))
178 > sizeof (xprt
->xp_pad
))
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"));
186 (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
191 if (setsockopt (sock
, SOL_IP
, IP_PKTINFO
, (void *) &pad
,
193 /* Set the padding to all 1s. */
197 /* Clear the padding. */
199 memset (&xprt
->xp_pad
[0], pad
, sizeof (xprt
->xp_pad
));
201 xprt_register (xprt
);
204 libc_hidden_def(svcudp_bufcreate
)
207 svcudp_create (int sock
)
210 return svcudp_bufcreate (sock
, UDPMSGSIZE
, UDPMSGSIZE
);
212 libc_hidden_def(svcudp_create
)
214 static enum xprt_stat
215 svcudp_stat (SVCXPRT
*xprt attribute_unused
)
222 svcudp_recv (SVCXPRT
*xprt
, struct rpc_msg
*msg
)
224 struct svcudp_data
*su
= su_data (xprt
);
225 XDR
*xdrs
= &(su
->su_xdrs
);
231 /* It is very tricky when you have IP aliases. We want to make sure
232 that we are sending the packet from the IP address where the
233 incoming packet is addressed to. H.J. */
236 struct msghdr
*mesgp
;
240 /* FIXME -- should xp_addrlen be a size_t? */
241 len
= (socklen_t
) sizeof(struct sockaddr_in
);
243 iovp
= (struct iovec
*) &xprt
->xp_pad
[0];
244 mesgp
= (struct msghdr
*) &xprt
->xp_pad
[sizeof (struct iovec
)];
245 if (mesgp
->msg_iovlen
)
247 iovp
->iov_base
= rpc_buffer (xprt
);
248 iovp
->iov_len
= su
->su_iosz
;
249 mesgp
->msg_iov
= iovp
;
250 mesgp
->msg_iovlen
= 1;
251 mesgp
->msg_name
= &(xprt
->xp_raddr
);
252 mesgp
->msg_namelen
= len
;
253 mesgp
->msg_control
= &xprt
->xp_pad
[sizeof (struct iovec
)
254 + sizeof (struct msghdr
)];
255 mesgp
->msg_controllen
= sizeof(xprt
->xp_pad
)
256 - sizeof (struct iovec
) - sizeof (struct msghdr
);
257 rlen
= recvmsg (xprt
->xp_sock
, mesgp
, 0);
259 len
= mesgp
->msg_namelen
;
263 rlen
= recvfrom (xprt
->xp_sock
, rpc_buffer (xprt
),
264 (int) su
->su_iosz
, 0,
265 (struct sockaddr
*) &(xprt
->xp_raddr
), &len
);
266 xprt
->xp_addrlen
= len
;
267 if (rlen
== -1 && errno
== EINTR
)
269 if (rlen
< 16) /* < 4 32-bit ints? */
271 xdrs
->x_op
= XDR_DECODE
;
272 XDR_SETPOS (xdrs
, 0);
273 if (!xdr_callmsg (xdrs
, msg
))
275 su
->su_xid
= msg
->rm_xid
;
276 if (su
->su_cache
!= NULL
)
278 if (cache_get (xprt
, msg
, &reply
, &replylen
))
281 if (mesgp
->msg_iovlen
)
283 iovp
->iov_base
= reply
;
284 iovp
->iov_len
= replylen
;
285 (void) sendmsg (xprt
->xp_sock
, mesgp
, 0);
289 (void) sendto (xprt
->xp_sock
, reply
, (int) replylen
, 0,
290 (struct sockaddr
*) &xprt
->xp_raddr
, len
);
298 svcudp_reply (SVCXPRT
*xprt
, struct rpc_msg
*msg
)
300 struct svcudp_data
*su
= su_data (xprt
);
301 XDR
*xdrs
= &(su
->su_xdrs
);
306 struct msghdr
*mesgp
;
309 xdrs
->x_op
= XDR_ENCODE
;
310 XDR_SETPOS (xdrs
, 0);
311 msg
->rm_xid
= su
->su_xid
;
312 if (xdr_replymsg (xdrs
, msg
))
314 slen
= (int) XDR_GETPOS (xdrs
);
316 mesgp
= (struct msghdr
*) &xprt
->xp_pad
[sizeof (struct iovec
)];
317 if (mesgp
->msg_iovlen
)
319 iovp
= (struct iovec
*) &xprt
->xp_pad
[0];
320 iovp
->iov_base
= rpc_buffer (xprt
);
321 iovp
->iov_len
= slen
;
322 sent
= sendmsg (xprt
->xp_sock
, mesgp
, 0);
326 sent
= sendto (xprt
->xp_sock
, rpc_buffer (xprt
), slen
, 0,
327 (struct sockaddr
*) &(xprt
->xp_raddr
),
332 if (su
->su_cache
&& slen
>= 0)
334 cache_set (xprt
, (u_long
) slen
);
342 svcudp_getargs (SVCXPRT
*xprt
, xdrproc_t xdr_args
, caddr_t args_ptr
)
345 return (*xdr_args
) (&(su_data (xprt
)->su_xdrs
), args_ptr
);
349 svcudp_freeargs (SVCXPRT
*xprt
, xdrproc_t xdr_args
, caddr_t args_ptr
)
351 XDR
*xdrs
= &(su_data (xprt
)->su_xdrs
);
353 xdrs
->x_op
= XDR_FREE
;
354 return (*xdr_args
) (xdrs
, args_ptr
);
358 svcudp_destroy (SVCXPRT
*xprt
)
360 struct svcudp_data
*su
= su_data (xprt
);
362 xprt_unregister (xprt
);
363 (void) close (xprt
->xp_sock
);
364 XDR_DESTROY (&(su
->su_xdrs
));
365 mem_free (rpc_buffer (xprt
), su
->su_iosz
);
366 mem_free ((caddr_t
) su
, sizeof (struct svcudp_data
));
367 mem_free ((caddr_t
) xprt
, sizeof (SVCXPRT
));
371 /***********this could be a separate file*********************/
374 * Fifo cache for udp server
375 * Copies pointers to reply buffers into fifo cache
376 * Buffers are sent again if retransmissions are detected.
379 #define SPARSENESS 4 /* 75% sparse */
382 # define CACHE_PERROR(msg) \
383 if (_IO_fwide (stderr, 0) > 0) \
384 (void) __fwprintf(stderr, L"%s\n", msg); \
386 (void) fprintf(stderr, "%s\n", msg)
388 # define CACHE_PERROR(msg) \
389 (void) fprintf(stderr,"%s\n", msg)
392 #define ALLOC(type, size) \
393 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
395 #define BZERO(addr, type, size) \
396 memset((char *) addr, 0, sizeof(type) * (int) (size))
399 * An entry in the cache
401 typedef struct cache_node
*cache_ptr
;
405 * Index into cache is xid, proc, vers, prog and address
411 struct sockaddr_in cache_addr
;
413 * The cached reply and length
416 u_long cache_replylen
;
418 * Next node on the list, if there is a collision
420 cache_ptr cache_next
;
430 u_long uc_size
; /* size of cache */
431 cache_ptr
*uc_entries
; /* hash table of entries in cache */
432 cache_ptr
*uc_fifo
; /* fifo list of entries in cache */
433 u_long uc_nextvictim
; /* points to next victim in fifo list */
434 u_long uc_prog
; /* saved program number */
435 u_long uc_vers
; /* saved version number */
436 u_long uc_proc
; /* saved procedure number */
437 struct sockaddr_in uc_addr
; /* saved caller's address */
442 * the hashing function
444 #define CACHE_LOC(transp, xid) \
445 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
449 * Enable use of the cache.
450 * Note: there is no disable.
452 int svcudp_enablecache (SVCXPRT
*transp
, u_long size
);
454 svcudp_enablecache (SVCXPRT
*transp
, u_long size
)
456 struct svcudp_data
*su
= su_data (transp
);
457 struct udp_cache
*uc
;
459 if (su
->su_cache
!= NULL
)
461 CACHE_PERROR (_("enablecache: cache already enabled"));
464 uc
= ALLOC (struct udp_cache
, 1);
467 CACHE_PERROR (_("enablecache: could not allocate cache"));
471 uc
->uc_nextvictim
= 0;
472 uc
->uc_entries
= ALLOC (cache_ptr
, size
* SPARSENESS
);
473 if (uc
->uc_entries
== NULL
)
475 CACHE_PERROR (_("enablecache: could not allocate cache data"));
478 BZERO (uc
->uc_entries
, cache_ptr
, size
* SPARSENESS
);
479 uc
->uc_fifo
= ALLOC (cache_ptr
, size
);
480 if (uc
->uc_fifo
== NULL
)
482 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
485 BZERO (uc
->uc_fifo
, cache_ptr
, size
);
486 su
->su_cache
= (char *) uc
;
492 * Set an entry in the cache
495 cache_set (SVCXPRT
*xprt
, u_long replylen
)
499 struct svcudp_data
*su
= su_data (xprt
);
500 struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
505 * Find space for the new entry, either by
506 * reusing an old entry, or by mallocing a new one
508 victim
= uc
->uc_fifo
[uc
->uc_nextvictim
];
511 loc
= CACHE_LOC (xprt
, victim
->cache_xid
);
512 for (vicp
= &uc
->uc_entries
[loc
];
513 *vicp
!= NULL
&& *vicp
!= victim
;
514 vicp
= &(*vicp
)->cache_next
)
518 CACHE_PERROR (_("cache_set: victim not found"));
521 *vicp
= victim
->cache_next
; /* remote from cache */
522 newbuf
= victim
->cache_reply
;
526 victim
= ALLOC (struct cache_node
, 1);
529 CACHE_PERROR (_("cache_set: victim alloc failed"));
532 newbuf
= mem_alloc (su
->su_iosz
);
535 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
543 victim
->cache_replylen
= replylen
;
544 victim
->cache_reply
= rpc_buffer (xprt
);
545 rpc_buffer (xprt
) = newbuf
;
546 xdrmem_create (&(su
->su_xdrs
), rpc_buffer (xprt
), su
->su_iosz
, XDR_ENCODE
);
547 victim
->cache_xid
= su
->su_xid
;
548 victim
->cache_proc
= uc
->uc_proc
;
549 victim
->cache_vers
= uc
->uc_vers
;
550 victim
->cache_prog
= uc
->uc_prog
;
551 victim
->cache_addr
= uc
->uc_addr
;
552 loc
= CACHE_LOC (xprt
, victim
->cache_xid
);
553 victim
->cache_next
= uc
->uc_entries
[loc
];
554 uc
->uc_entries
[loc
] = victim
;
555 uc
->uc_fifo
[uc
->uc_nextvictim
++] = victim
;
556 uc
->uc_nextvictim
%= uc
->uc_size
;
560 * Try to get an entry from the cache
561 * return 1 if found, 0 if not found
564 cache_get (SVCXPRT
*xprt
, struct rpc_msg
*msg
, char **replyp
, u_long
*replylenp
)
568 struct svcudp_data
*su
= su_data (xprt
);
569 struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
571 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
573 loc
= CACHE_LOC (xprt
, su
->su_xid
);
574 for (ent
= uc
->uc_entries
[loc
]; ent
!= NULL
; ent
= ent
->cache_next
)
576 if (ent
->cache_xid
== su
->su_xid
&&
577 ent
->cache_proc
== uc
->uc_proc
&&
578 ent
->cache_vers
== uc
->uc_vers
&&
579 ent
->cache_prog
== uc
->uc_prog
&&
580 EQADDR (ent
->cache_addr
, uc
->uc_addr
))
582 *replyp
= ent
->cache_reply
;
583 *replylenp
= ent
->cache_replylen
;
588 * Failed to find entry
589 * Remember a few things so we can do a set later
591 uc
->uc_proc
= msg
->rm_call
.cb_proc
;
592 uc
->uc_vers
= msg
->rm_call
.cb_vers
;
593 uc
->uc_prog
= msg
->rm_call
.cb_prog
;
594 memcpy (&uc
->uc_addr
, &xprt
->xp_raddr
, sizeof (uc
->uc_addr
));