sync header with GNU libc / kernel
[uclibc-ng.git] / libc / inet / rpc / svc_udp.c
blob5b8a8d297761887eaf03a9a3863b8d2b65331697
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 0
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_private.h"
46 #include <sys/socket.h>
47 #include <errno.h>
49 #ifdef IP_PKTINFO
50 #include <sys/uio.h>
51 #endif
53 #define rpc_buffer(xprt) ((xprt)->xp_p1)
54 #ifndef MAX
55 #define MAX(a, b) ((a > b) ? a : b)
56 #endif
58 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
59 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
60 static enum xprt_stat svcudp_stat (SVCXPRT *);
61 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
62 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
63 static void svcudp_destroy (SVCXPRT *);
65 static const struct xp_ops svcudp_op =
67 svcudp_recv,
68 svcudp_stat,
69 svcudp_getargs,
70 svcudp_reply,
71 svcudp_freeargs,
72 svcudp_destroy
75 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
76 u_long *replylenp);
77 static void cache_set (SVCXPRT *xprt, u_long replylen);
80 * kept in xprt->xp_p2
82 struct svcudp_data
84 u_int su_iosz; /* byte size of send.recv buffer */
85 u_long su_xid; /* transaction id */
86 XDR su_xdrs; /* XDR handle */
87 char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
88 char *su_cache; /* cached data, NULL if no cache */
90 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
93 * Usage:
94 * xprt = svcudp_create(sock);
96 * If sock<0 then a socket is created, else sock is used.
97 * If the socket, sock is not bound to a port then svcudp_create
98 * binds it to an arbitrary port. In any (successful) case,
99 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
100 * associated port number.
101 * Once *xprt is initialized, it is registered as a transporter;
102 * see (svc.h, xprt_register).
103 * The routines returns NULL if a problem occurred.
105 SVCXPRT *
106 svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
108 bool_t madesock = FALSE;
109 SVCXPRT *xprt;
110 struct svcudp_data *su;
111 struct sockaddr_in addr;
112 socklen_t len = sizeof (struct sockaddr_in);
113 int pad;
114 void *buf;
116 if (sock == RPC_ANYSOCK)
118 if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
120 perror ("svcudp_create: socket creation problem");
121 return (SVCXPRT *) NULL;
123 madesock = TRUE;
125 memset ((char *) &addr, 0, sizeof (addr));
126 addr.sin_family = AF_INET;
127 if (bindresvport (sock, &addr))
129 addr.sin_port = 0;
130 (void) bind (sock, (struct sockaddr *) &addr, len);
132 if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
134 perror ("svcudp_create - cannot getsockname");
135 if (madesock)
136 (void) close (sock);
137 return (SVCXPRT *) NULL;
139 xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
140 su = (struct svcudp_data *) mem_alloc (sizeof (*su));
141 buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
142 if (xprt == NULL || su == NULL || buf == NULL)
144 (void) fputs ("svcudp_create: out of memory\n", stderr);
145 mem_free (xprt, sizeof (SVCXPRT));
146 mem_free (su, sizeof (*su));
147 mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
148 return NULL;
150 su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
151 rpc_buffer (xprt) = buf;
152 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE);
153 su->su_cache = NULL;
154 xprt->xp_p2 = (caddr_t) su;
155 xprt->xp_verf.oa_base = su->su_verfbody;
156 xprt->xp_ops = &svcudp_op;
157 xprt->xp_port = ntohs (addr.sin_port);
158 xprt->xp_sock = sock;
160 #ifdef IP_PKTINFO
161 if ((sizeof (struct iovec) + sizeof (struct msghdr)
162 + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
163 > sizeof (xprt->xp_pad))
165 (void) fputs ("svcudp_create: xp_pad is too small for IP_PKTINFO\n",
166 stderr);
167 return NULL;
169 pad = 1;
170 if (setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
171 sizeof (pad)) == 0)
172 /* Set the padding to all 1s. */
173 pad = 0xff;
174 else
175 #endif
176 /* Clear the padding. */
177 pad = 0;
178 memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
180 xprt_register (xprt);
181 return xprt;
183 libc_hidden_def(svcudp_bufcreate)
185 SVCXPRT *
186 svcudp_create (int sock)
189 return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
191 libc_hidden_def(svcudp_create)
193 static enum xprt_stat
194 svcudp_stat (SVCXPRT *xprt attribute_unused)
197 return XPRT_IDLE;
200 static bool_t
201 svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
203 struct svcudp_data *su = su_data (xprt);
204 XDR *xdrs = &(su->su_xdrs);
205 int rlen;
206 char *reply;
207 u_long replylen;
208 socklen_t len;
210 /* It is very tricky when you have IP aliases. We want to make sure
211 that we are sending the packet from the IP address where the
212 incoming packet is addressed to. H.J. */
213 #ifdef IP_PKTINFO
214 struct iovec *iovp;
215 struct msghdr *mesgp;
216 #endif
218 again:
219 /* FIXME -- should xp_addrlen be a size_t? */
220 len = (socklen_t) sizeof(struct sockaddr_in);
221 #ifdef IP_PKTINFO
222 iovp = (struct iovec *) &xprt->xp_pad [0];
223 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
224 if (mesgp->msg_iovlen)
226 iovp->iov_base = rpc_buffer (xprt);
227 iovp->iov_len = su->su_iosz;
228 mesgp->msg_iov = iovp;
229 mesgp->msg_iovlen = 1;
230 mesgp->msg_name = &(xprt->xp_raddr);
231 mesgp->msg_namelen = len;
232 mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
233 + sizeof (struct msghdr)];
234 mesgp->msg_controllen = sizeof(xprt->xp_pad)
235 - sizeof (struct iovec) - sizeof (struct msghdr);
236 rlen = recvmsg (xprt->xp_sock, mesgp, 0);
237 if (rlen >= 0)
238 len = mesgp->msg_namelen;
240 else
241 #endif
242 rlen = recvfrom (xprt->xp_sock, rpc_buffer (xprt),
243 (int) su->su_iosz, 0,
244 (struct sockaddr *) &(xprt->xp_raddr), &len);
245 xprt->xp_addrlen = len;
246 if (rlen == -1 && errno == EINTR)
247 goto again;
248 if (rlen < 16) /* < 4 32-bit ints? */
249 return FALSE;
250 xdrs->x_op = XDR_DECODE;
251 XDR_SETPOS (xdrs, 0);
252 if (!xdr_callmsg (xdrs, msg))
253 return FALSE;
254 su->su_xid = msg->rm_xid;
255 if (su->su_cache != NULL)
257 if (cache_get (xprt, msg, &reply, &replylen))
259 #ifdef IP_PKTINFO
260 if (mesgp->msg_iovlen)
262 iovp->iov_base = reply;
263 iovp->iov_len = replylen;
264 (void) sendmsg (xprt->xp_sock, mesgp, 0);
266 else
267 #endif
268 (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
269 (struct sockaddr *) &xprt->xp_raddr, len);
270 return TRUE;
273 return TRUE;
276 static bool_t
277 svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
279 struct svcudp_data *su = su_data (xprt);
280 XDR *xdrs = &(su->su_xdrs);
281 int slen, sent;
282 bool_t stat = FALSE;
283 #ifdef IP_PKTINFO
284 struct iovec *iovp;
285 struct msghdr *mesgp;
286 #endif
288 xdrs->x_op = XDR_ENCODE;
289 XDR_SETPOS (xdrs, 0);
290 msg->rm_xid = su->su_xid;
291 if (xdr_replymsg (xdrs, msg))
293 slen = (int) XDR_GETPOS (xdrs);
294 #ifdef IP_PKTINFO
295 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
296 if (mesgp->msg_iovlen)
298 iovp = (struct iovec *) &xprt->xp_pad [0];
299 iovp->iov_base = rpc_buffer (xprt);
300 iovp->iov_len = slen;
301 sent = sendmsg (xprt->xp_sock, mesgp, 0);
303 else
304 #endif
305 sent = sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
306 (struct sockaddr *) &(xprt->xp_raddr),
307 xprt->xp_addrlen);
308 if (sent == slen)
310 stat = TRUE;
311 if (su->su_cache && slen >= 0)
313 cache_set (xprt, (u_long) slen);
317 return stat;
320 static bool_t
321 svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
324 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
327 static bool_t
328 svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
330 XDR *xdrs = &(su_data (xprt)->su_xdrs);
332 xdrs->x_op = XDR_FREE;
333 return (*xdr_args) (xdrs, args_ptr);
336 static void
337 svcudp_destroy (SVCXPRT *xprt)
339 struct svcudp_data *su = su_data (xprt);
341 xprt_unregister (xprt);
342 (void) close (xprt->xp_sock);
343 XDR_DESTROY (&(su->su_xdrs));
344 mem_free (rpc_buffer (xprt), su->su_iosz);
345 mem_free ((caddr_t) su, sizeof (struct svcudp_data));
346 mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
350 /***********this could be a separate file*********************/
353 * Fifo cache for udp server
354 * Copies pointers to reply buffers into fifo cache
355 * Buffers are sent again if retransmissions are detected.
358 #define SPARSENESS 4 /* 75% sparse */
360 #define CACHE_PERROR(msg) \
361 (void) fprintf(stderr,"%s\n", msg)
363 #define ALLOC(type, size) \
364 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
366 #define BZERO(addr, type, size) \
367 memset((char *) addr, 0, sizeof(type) * (int) (size))
370 * An entry in the cache
372 typedef struct cache_node *cache_ptr;
373 struct cache_node
376 * Index into cache is xid, proc, vers, prog and address
378 u_long cache_xid;
379 u_long cache_proc;
380 u_long cache_vers;
381 u_long cache_prog;
382 struct sockaddr_in cache_addr;
384 * The cached reply and length
386 char *cache_reply;
387 u_long cache_replylen;
389 * Next node on the list, if there is a collision
391 cache_ptr cache_next;
397 * The entire cache
399 struct udp_cache
401 u_long uc_size; /* size of cache */
402 cache_ptr *uc_entries; /* hash table of entries in cache */
403 cache_ptr *uc_fifo; /* fifo list of entries in cache */
404 u_long uc_nextvictim; /* points to next victim in fifo list */
405 u_long uc_prog; /* saved program number */
406 u_long uc_vers; /* saved version number */
407 u_long uc_proc; /* saved procedure number */
408 struct sockaddr_in uc_addr; /* saved caller's address */
413 * the hashing function
415 #define CACHE_LOC(transp, xid) \
416 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
420 * Enable use of the cache.
421 * Note: there is no disable.
423 int svcudp_enablecache (SVCXPRT *transp, u_long size);
425 svcudp_enablecache (SVCXPRT *transp, u_long size)
427 struct svcudp_data *su = su_data (transp);
428 struct udp_cache *uc;
430 if (su->su_cache != NULL)
432 CACHE_PERROR ("enablecache: cache already enabled");
433 return 0;
435 uc = ALLOC (struct udp_cache, 1);
436 if (uc == NULL)
438 CACHE_PERROR ("enablecache: could not allocate cache");
439 return 0;
441 uc->uc_size = size;
442 uc->uc_nextvictim = 0;
443 uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
444 if (uc->uc_entries == NULL)
446 CACHE_PERROR ("enablecache: could not allocate cache data");
447 return 0;
449 BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
450 uc->uc_fifo = ALLOC (cache_ptr, size);
451 if (uc->uc_fifo == NULL)
453 CACHE_PERROR ("enablecache: could not allocate cache fifo");
454 return 0;
456 BZERO (uc->uc_fifo, cache_ptr, size);
457 su->su_cache = (char *) uc;
458 return 1;
463 * Set an entry in the cache
465 static void
466 cache_set (SVCXPRT *xprt, u_long replylen)
468 cache_ptr victim;
469 cache_ptr *vicp;
470 struct svcudp_data *su = su_data (xprt);
471 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
472 u_int loc;
473 char *newbuf;
476 * Find space for the new entry, either by
477 * reusing an old entry, or by mallocing a new one
479 victim = uc->uc_fifo[uc->uc_nextvictim];
480 if (victim != NULL)
482 loc = CACHE_LOC (xprt, victim->cache_xid);
483 for (vicp = &uc->uc_entries[loc];
484 *vicp != NULL && *vicp != victim;
485 vicp = &(*vicp)->cache_next)
487 if (*vicp == NULL)
489 CACHE_PERROR ("cache_set: victim not found");
490 return;
492 *vicp = victim->cache_next; /* remote from cache */
493 newbuf = victim->cache_reply;
495 else
497 victim = ALLOC (struct cache_node, 1);
498 if (victim == NULL)
500 CACHE_PERROR ("cache_set: victim alloc failed");
501 return;
503 newbuf = mem_alloc (su->su_iosz);
504 if (newbuf == NULL)
506 CACHE_PERROR ("cache_set: could not allocate new rpc_buffer");
507 return;
512 * Store it away
514 victim->cache_replylen = replylen;
515 victim->cache_reply = rpc_buffer (xprt);
516 rpc_buffer (xprt) = newbuf;
517 xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
518 victim->cache_xid = su->su_xid;
519 victim->cache_proc = uc->uc_proc;
520 victim->cache_vers = uc->uc_vers;
521 victim->cache_prog = uc->uc_prog;
522 victim->cache_addr = uc->uc_addr;
523 loc = CACHE_LOC (xprt, victim->cache_xid);
524 victim->cache_next = uc->uc_entries[loc];
525 uc->uc_entries[loc] = victim;
526 uc->uc_fifo[uc->uc_nextvictim++] = victim;
527 uc->uc_nextvictim %= uc->uc_size;
531 * Try to get an entry from the cache
532 * return 1 if found, 0 if not found
534 static int
535 cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp)
537 u_int loc;
538 cache_ptr ent;
539 struct svcudp_data *su = su_data (xprt);
540 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
542 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
544 loc = CACHE_LOC (xprt, su->su_xid);
545 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
547 if (ent->cache_xid == su->su_xid &&
548 ent->cache_proc == uc->uc_proc &&
549 ent->cache_vers == uc->uc_vers &&
550 ent->cache_prog == uc->uc_prog &&
551 EQADDR (ent->cache_addr, uc->uc_addr))
553 *replyp = ent->cache_reply;
554 *replylenp = ent->cache_replylen;
555 return 1;
559 * Failed to find entry
560 * Remember a few things so we can do a set later
562 uc->uc_proc = msg->rm_call.cb_proc;
563 uc->uc_vers = msg->rm_call.cb_vers;
564 uc->uc_prog = msg->rm_call.cb_prog;
565 memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
566 return 0;