Update.
[glibc.git] / sunrpc / svc_udp.c
blob17c4977e967f51d960399b3cea647ee5f06d6178
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) _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 (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 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, 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;
204 SVCXPRT *
205 svcudp_create (sock)
206 int sock;
209 return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
212 static enum xprt_stat
213 svcudp_stat (xprt)
214 SVCXPRT *xprt;
217 return XPRT_IDLE;
220 static bool_t
221 svcudp_recv (xprt, msg)
222 SVCXPRT *xprt;
223 struct rpc_msg *msg;
225 struct svcudp_data *su = su_data (xprt);
226 XDR *xdrs = &(su->su_xdrs);
227 int rlen;
228 char *reply;
229 u_long replylen;
230 socklen_t len;
232 /* It is very tricky when you have IP aliases. We want to make sure
233 that we are sending the packet from the IP address where the
234 incoming packet is addressed to. H.J. */
235 #ifdef IP_PKTINFO
236 struct iovec *iovp;
237 struct msghdr *mesgp;
238 #endif
240 again:
241 /* FIXME -- should xp_addrlen be a size_t? */
242 len = (socklen_t) sizeof(struct sockaddr_in);
243 #ifdef IP_PKTINFO
244 iovp = (struct iovec *) &xprt->xp_pad [0];
245 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
246 if (mesgp->msg_iovlen)
248 iovp->iov_base = rpc_buffer (xprt);
249 iovp->iov_len = su->su_iosz;
250 mesgp->msg_iov = iovp;
251 mesgp->msg_iovlen = 1;
252 mesgp->msg_name = &(xprt->xp_raddr);
253 mesgp->msg_namelen = len;
254 mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
255 + sizeof (struct msghdr)];
256 mesgp->msg_controllen = sizeof(xprt->xp_pad)
257 - sizeof (struct iovec) - sizeof (struct msghdr);
258 rlen = recvmsg (xprt->xp_sock, mesgp, 0);
259 if (rlen >= 0)
260 len = mesgp->msg_namelen;
262 else
263 #endif
264 rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt),
265 (int) su->su_iosz, 0,
266 (struct sockaddr *) &(xprt->xp_raddr), &len);
267 xprt->xp_addrlen = len;
268 if (rlen == -1 && errno == EINTR)
269 goto again;
270 if (rlen < 16) /* < 4 32-bit ints? */
271 return FALSE;
272 xdrs->x_op = XDR_DECODE;
273 XDR_SETPOS (xdrs, 0);
274 if (!xdr_callmsg (xdrs, msg))
275 return FALSE;
276 su->su_xid = msg->rm_xid;
277 if (su->su_cache != NULL)
279 if (cache_get (xprt, msg, &reply, &replylen))
281 #ifdef IP_PKTINFO
282 if (mesgp->msg_iovlen)
284 iovp->iov_base = reply;
285 iovp->iov_len = replylen;
286 (void) sendmsg (xprt->xp_sock, mesgp, 0);
288 else
289 #endif
290 (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
291 (struct sockaddr *) &xprt->xp_raddr, len);
292 return TRUE;
295 return TRUE;
298 static bool_t
299 svcudp_reply (xprt, msg)
300 SVCXPRT *xprt;
301 struct rpc_msg *msg;
303 struct svcudp_data *su = su_data (xprt);
304 XDR *xdrs = &(su->su_xdrs);
305 int slen, sent;
306 bool_t stat = FALSE;
307 #ifdef IP_PKTINFO
308 struct iovec *iovp;
309 struct msghdr *mesgp;
310 #endif
312 xdrs->x_op = XDR_ENCODE;
313 XDR_SETPOS (xdrs, 0);
314 msg->rm_xid = su->su_xid;
315 if (xdr_replymsg (xdrs, msg))
317 slen = (int) XDR_GETPOS (xdrs);
318 #ifdef IP_PKTINFO
319 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
320 if (mesgp->msg_iovlen)
322 iovp = (struct iovec *) &xprt->xp_pad [0];
323 iovp->iov_base = rpc_buffer (xprt);
324 iovp->iov_len = slen;
325 sent = sendmsg (xprt->xp_sock, mesgp, 0);
327 else
328 #endif
329 sent = sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
330 (struct sockaddr *) &(xprt->xp_raddr),
331 xprt->xp_addrlen);
332 if (sent == slen)
334 stat = TRUE;
335 if (su->su_cache && slen >= 0)
337 cache_set (xprt, (u_long) slen);
341 return stat;
344 static bool_t
345 svcudp_getargs (xprt, xdr_args, args_ptr)
346 SVCXPRT *xprt;
347 xdrproc_t xdr_args;
348 caddr_t args_ptr;
351 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
354 static bool_t
355 svcudp_freeargs (xprt, xdr_args, args_ptr)
356 SVCXPRT *xprt;
357 xdrproc_t xdr_args;
358 caddr_t args_ptr;
360 XDR *xdrs = &(su_data (xprt)->su_xdrs);
362 xdrs->x_op = XDR_FREE;
363 return (*xdr_args) (xdrs, args_ptr);
366 static void
367 svcudp_destroy (xprt)
368 SVCXPRT *xprt;
370 struct svcudp_data *su = su_data (xprt);
372 xprt_unregister (xprt);
373 (void) __close (xprt->xp_sock);
374 XDR_DESTROY (&(su->su_xdrs));
375 mem_free (rpc_buffer (xprt), su->su_iosz);
376 mem_free ((caddr_t) su, sizeof (struct svcudp_data));
377 mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
381 /***********this could be a separate file*********************/
384 * Fifo cache for udp server
385 * Copies pointers to reply buffers into fifo cache
386 * Buffers are sent again if retransmissions are detected.
389 #define SPARSENESS 4 /* 75% sparse */
391 #ifdef USE_IN_LIBIO
392 # define CACHE_PERROR(msg) \
393 if (_IO_fwide (stderr, 0) > 0) \
394 (void) __fwprintf(stderr, L"%s\n", msg); \
395 else \
396 (void) fprintf(stderr, "%s\n", msg)
397 #else
398 # define CACHE_PERROR(msg) \
399 (void) fprintf(stderr,"%s\n", msg)
400 #endif
402 #define ALLOC(type, size) \
403 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
405 #define BZERO(addr, type, size) \
406 __bzero((char *) addr, sizeof(type) * (int) (size))
409 * An entry in the cache
411 typedef struct cache_node *cache_ptr;
412 struct cache_node
415 * Index into cache is xid, proc, vers, prog and address
417 u_long cache_xid;
418 u_long cache_proc;
419 u_long cache_vers;
420 u_long cache_prog;
421 struct sockaddr_in cache_addr;
423 * The cached reply and length
425 char *cache_reply;
426 u_long cache_replylen;
428 * Next node on the list, if there is a collision
430 cache_ptr cache_next;
436 * The entire cache
438 struct udp_cache
440 u_long uc_size; /* size of cache */
441 cache_ptr *uc_entries; /* hash table of entries in cache */
442 cache_ptr *uc_fifo; /* fifo list of entries in cache */
443 u_long uc_nextvictim; /* points to next victim in fifo list */
444 u_long uc_prog; /* saved program number */
445 u_long uc_vers; /* saved version number */
446 u_long uc_proc; /* saved procedure number */
447 struct sockaddr_in uc_addr; /* saved caller's address */
452 * the hashing function
454 #define CACHE_LOC(transp, xid) \
455 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
459 * Enable use of the cache.
460 * Note: there is no disable.
463 svcudp_enablecache (SVCXPRT *transp, u_long size)
465 struct svcudp_data *su = su_data (transp);
466 struct udp_cache *uc;
468 if (su->su_cache != NULL)
470 CACHE_PERROR (_("enablecache: cache already enabled"));
471 return 0;
473 uc = ALLOC (struct udp_cache, 1);
474 if (uc == NULL)
476 CACHE_PERROR (_("enablecache: could not allocate cache"));
477 return 0;
479 uc->uc_size = size;
480 uc->uc_nextvictim = 0;
481 uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
482 if (uc->uc_entries == NULL)
484 CACHE_PERROR (_("enablecache: could not allocate cache data"));
485 return 0;
487 BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
488 uc->uc_fifo = ALLOC (cache_ptr, size);
489 if (uc->uc_fifo == NULL)
491 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
492 return 0;
494 BZERO (uc->uc_fifo, cache_ptr, size);
495 su->su_cache = (char *) uc;
496 return 1;
501 * Set an entry in the cache
503 static void
504 cache_set (SVCXPRT *xprt, u_long replylen)
506 cache_ptr victim;
507 cache_ptr *vicp;
508 struct svcudp_data *su = su_data (xprt);
509 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
510 u_int loc;
511 char *newbuf;
514 * Find space for the new entry, either by
515 * reusing an old entry, or by mallocing a new one
517 victim = uc->uc_fifo[uc->uc_nextvictim];
518 if (victim != NULL)
520 loc = CACHE_LOC (xprt, victim->cache_xid);
521 for (vicp = &uc->uc_entries[loc];
522 *vicp != NULL && *vicp != victim;
523 vicp = &(*vicp)->cache_next)
525 if (*vicp == NULL)
527 CACHE_PERROR (_("cache_set: victim not found"));
528 return;
530 *vicp = victim->cache_next; /* remote from cache */
531 newbuf = victim->cache_reply;
533 else
535 victim = ALLOC (struct cache_node, 1);
536 if (victim == NULL)
538 CACHE_PERROR (_("cache_set: victim alloc failed"));
539 return;
541 newbuf = mem_alloc (su->su_iosz);
542 if (newbuf == NULL)
544 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
545 return;
550 * Store it away
552 victim->cache_replylen = replylen;
553 victim->cache_reply = rpc_buffer (xprt);
554 rpc_buffer (xprt) = newbuf;
555 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
556 victim->cache_xid = su->su_xid;
557 victim->cache_proc = uc->uc_proc;
558 victim->cache_vers = uc->uc_vers;
559 victim->cache_prog = uc->uc_prog;
560 victim->cache_addr = uc->uc_addr;
561 loc = CACHE_LOC (xprt, victim->cache_xid);
562 victim->cache_next = uc->uc_entries[loc];
563 uc->uc_entries[loc] = victim;
564 uc->uc_fifo[uc->uc_nextvictim++] = victim;
565 uc->uc_nextvictim %= uc->uc_size;
569 * Try to get an entry from the cache
570 * return 1 if found, 0 if not found
572 static int
573 cache_get (xprt, msg, replyp, replylenp)
574 SVCXPRT *xprt;
575 struct rpc_msg *msg;
576 char **replyp;
577 u_long *replylenp;
579 u_int loc;
580 cache_ptr ent;
581 struct svcudp_data *su = su_data (xprt);
582 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
584 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
586 loc = CACHE_LOC (xprt, su->su_xid);
587 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
589 if (ent->cache_xid == su->su_xid &&
590 ent->cache_proc == uc->uc_proc &&
591 ent->cache_vers == uc->uc_vers &&
592 ent->cache_prog == uc->uc_prog &&
593 EQADDR (ent->cache_addr, uc->uc_addr))
595 *replyp = ent->cache_reply;
596 *replylenp = ent->cache_replylen;
597 return 1;
601 * Failed to find entry
602 * Remember a few things so we can do a set later
604 uc->uc_proc = msg->rm_call.cb_proc;
605 uc->uc_vers = msg->rm_call.cb_vers;
606 uc->uc_prog = msg->rm_call.cb_prog;
607 memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
608 return 0;