fix up errno references
[project/librpc-uclibc.git] / svc_udp.c
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.
9 *
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.
13 *
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.
17 *
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.
21 *
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.
25 *
26 * Sun Microsystems, Inc.
27 * 2550 Garcia Avenue
28 * Mountain View, California 94043
29 */
30 #if 0
31 static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
32 #endif
33
34 /*
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.)
38 *
39 * Copyright (C) 1984, Sun Microsystems, Inc.
40 */
41
42 #define __FORCE_GLIBC
43 #include <features.h>
44
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <string.h>
48 #include <rpc/rpc.h>
49 #include <sys/socket.h>
50 #include <errno.h>
51
52 #ifdef IP_PKTINFO
53 #include <sys/uio.h>
54 #endif
55
56 #ifdef USE_IN_LIBIO
57 # include <wchar.h>
58 # include <libio/iolibio.h>
59 # define fputs(s, f) _IO_fputs (s, f)
60 #endif
61
62
63 #define rpc_buffer(xprt) ((xprt)->xp_p1)
64 #ifndef MAX
65 #define MAX(a, b) ((a > b) ? a : b)
66 #endif
67
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 *);
74
75 static const struct xp_ops svcudp_op =
76 {
77 svcudp_recv,
78 svcudp_stat,
79 svcudp_getargs,
80 svcudp_reply,
81 svcudp_freeargs,
82 svcudp_destroy
83 };
84
85 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
86 u_long *replylenp);
87 static void cache_set (SVCXPRT *xprt, u_long replylen);
88
89 /*
90 * kept in xprt->xp_p2
91 */
92 struct svcudp_data
93 {
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 */
99 };
100 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
101
102 /*
103 * Usage:
104 * xprt = svcudp_create(sock);
105 *
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.
114 */
115 SVCXPRT *
116 svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
117 {
118 bool_t madesock = FALSE;
119 SVCXPRT *xprt;
120 struct svcudp_data *su;
121 struct sockaddr_in addr;
122 socklen_t len = sizeof (struct sockaddr_in);
123 int pad;
124 void *buf;
125
126 if (sock == RPC_ANYSOCK)
127 {
128 if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
129 {
130 perror (_("svcudp_create: socket creation problem"));
131 return (SVCXPRT *) NULL;
132 }
133 madesock = TRUE;
134 }
135 memset ((char *) &addr, 0, sizeof (addr));
136 addr.sin_family = AF_INET;
137 if (bindresvport (sock, &addr))
138 {
139 addr.sin_port = 0;
140 (void) bind (sock, (struct sockaddr *) &addr, len);
141 }
142 if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
143 {
144 perror (_("svcudp_create - cannot getsockname"));
145 if (madesock)
146 (void) close (sock);
147 return (SVCXPRT *) NULL;
148 }
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)
153 {
154 #ifdef USE_IN_LIBIO
155 if (_IO_fwide (stderr, 0) > 0)
156 (void) fwprintf (stderr, L"%s", _("svcudp_create: out of memory\n"));
157 else
158 #endif
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);
163 return NULL;
164 }
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);
168 su->su_cache = NULL;
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;
174
175 #ifdef IP_PKTINFO
176 if ((sizeof (struct iovec) + sizeof (struct msghdr)
177 + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
178 > sizeof (xprt->xp_pad))
179 {
180 # ifdef USE_IN_LIBIO
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"));
184 else
185 # endif
186 (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
187 stderr);
188 return NULL;
189 }
190 pad = 1;
191 if (setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
192 sizeof (pad)) == 0)
193 /* Set the padding to all 1s. */
194 pad = 0xff;
195 else
196 #endif
197 /* Clear the padding. */
198 pad = 0;
199 memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
200
201 xprt_register (xprt);
202 return xprt;
203 }
204 libc_hidden_def(svcudp_bufcreate)
205
206 SVCXPRT *
207 svcudp_create (int sock)
208 {
209
210 return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
211 }
212 libc_hidden_def(svcudp_create)
213
214 static enum xprt_stat
215 svcudp_stat (SVCXPRT *xprt attribute_unused)
216 {
217
218 return XPRT_IDLE;
219 }
220
221 static bool_t
222 svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
223 {
224 struct svcudp_data *su = su_data (xprt);
225 XDR *xdrs = &(su->su_xdrs);
226 int rlen;
227 char *reply;
228 u_long replylen;
229 socklen_t len;
230
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. */
234 #ifdef IP_PKTINFO
235 struct iovec *iovp;
236 struct msghdr *mesgp;
237 #endif
238
239 again:
240 /* FIXME -- should xp_addrlen be a size_t? */
241 len = (socklen_t) sizeof(struct sockaddr_in);
242 #ifdef IP_PKTINFO
243 iovp = (struct iovec *) &xprt->xp_pad [0];
244 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
245 if (mesgp->msg_iovlen)
246 {
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);
258 if (rlen >= 0)
259 len = mesgp->msg_namelen;
260 }
261 else
262 #endif
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)
268 goto again;
269 if (rlen < 16) /* < 4 32-bit ints? */
270 return FALSE;
271 xdrs->x_op = XDR_DECODE;
272 XDR_SETPOS (xdrs, 0);
273 if (!xdr_callmsg (xdrs, msg))
274 return FALSE;
275 su->su_xid = msg->rm_xid;
276 if (su->su_cache != NULL)
277 {
278 if (cache_get (xprt, msg, &reply, &replylen))
279 {
280 #ifdef IP_PKTINFO
281 if (mesgp->msg_iovlen)
282 {
283 iovp->iov_base = reply;
284 iovp->iov_len = replylen;
285 (void) sendmsg (xprt->xp_sock, mesgp, 0);
286 }
287 else
288 #endif
289 (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
290 (struct sockaddr *) &xprt->xp_raddr, len);
291 return TRUE;
292 }
293 }
294 return TRUE;
295 }
296
297 static bool_t
298 svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
299 {
300 struct svcudp_data *su = su_data (xprt);
301 XDR *xdrs = &(su->su_xdrs);
302 int slen, sent;
303 bool_t stat = FALSE;
304 #ifdef IP_PKTINFO
305 struct iovec *iovp;
306 struct msghdr *mesgp;
307 #endif
308
309 xdrs->x_op = XDR_ENCODE;
310 XDR_SETPOS (xdrs, 0);
311 msg->rm_xid = su->su_xid;
312 if (xdr_replymsg (xdrs, msg))
313 {
314 slen = (int) XDR_GETPOS (xdrs);
315 #ifdef IP_PKTINFO
316 mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
317 if (mesgp->msg_iovlen)
318 {
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);
323 }
324 else
325 #endif
326 sent = sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
327 (struct sockaddr *) &(xprt->xp_raddr),
328 xprt->xp_addrlen);
329 if (sent == slen)
330 {
331 stat = TRUE;
332 if (su->su_cache && slen >= 0)
333 {
334 cache_set (xprt, (u_long) slen);
335 }
336 }
337 }
338 return stat;
339 }
340
341 static bool_t
342 svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
343 {
344
345 return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
346 }
347
348 static bool_t
349 svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
350 {
351 XDR *xdrs = &(su_data (xprt)->su_xdrs);
352
353 xdrs->x_op = XDR_FREE;
354 return (*xdr_args) (xdrs, args_ptr);
355 }
356
357 static void
358 svcudp_destroy (SVCXPRT *xprt)
359 {
360 struct svcudp_data *su = su_data (xprt);
361
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));
368 }
369
370
371 /***********this could be a separate file*********************/
372
373 /*
374 * Fifo cache for udp server
375 * Copies pointers to reply buffers into fifo cache
376 * Buffers are sent again if retransmissions are detected.
377 */
378
379 #define SPARSENESS 4 /* 75% sparse */
380
381 #ifdef USE_IN_LIBIO
382 # define CACHE_PERROR(msg) \
383 if (_IO_fwide (stderr, 0) > 0) \
384 (void) __fwprintf(stderr, L"%s\n", msg); \
385 else \
386 (void) fprintf(stderr, "%s\n", msg)
387 #else
388 # define CACHE_PERROR(msg) \
389 (void) fprintf(stderr,"%s\n", msg)
390 #endif
391
392 #define ALLOC(type, size) \
393 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
394
395 #define BZERO(addr, type, size) \
396 memset((char *) addr, 0, sizeof(type) * (int) (size))
397
398 /*
399 * An entry in the cache
400 */
401 typedef struct cache_node *cache_ptr;
402 struct cache_node
403 {
404 /*
405 * Index into cache is xid, proc, vers, prog and address
406 */
407 u_long cache_xid;
408 u_long cache_proc;
409 u_long cache_vers;
410 u_long cache_prog;
411 struct sockaddr_in cache_addr;
412 /*
413 * The cached reply and length
414 */
415 char *cache_reply;
416 u_long cache_replylen;
417 /*
418 * Next node on the list, if there is a collision
419 */
420 cache_ptr cache_next;
421 };
422
423
424
425 /*
426 * The entire cache
427 */
428 struct udp_cache
429 {
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 */
438 };
439
440
441 /*
442 * the hashing function
443 */
444 #define CACHE_LOC(transp, xid) \
445 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
446
447
448 /*
449 * Enable use of the cache.
450 * Note: there is no disable.
451 */
452 int svcudp_enablecache (SVCXPRT *transp, u_long size);
453 int
454 svcudp_enablecache (SVCXPRT *transp, u_long size)
455 {
456 struct svcudp_data *su = su_data (transp);
457 struct udp_cache *uc;
458
459 if (su->su_cache != NULL)
460 {
461 CACHE_PERROR (_("enablecache: cache already enabled"));
462 return 0;
463 }
464 uc = ALLOC (struct udp_cache, 1);
465 if (uc == NULL)
466 {
467 CACHE_PERROR (_("enablecache: could not allocate cache"));
468 return 0;
469 }
470 uc->uc_size = size;
471 uc->uc_nextvictim = 0;
472 uc->uc_entries = ALLOC (cache_ptr, size * SPARSENESS);
473 if (uc->uc_entries == NULL)
474 {
475 CACHE_PERROR (_("enablecache: could not allocate cache data"));
476 return 0;
477 }
478 BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
479 uc->uc_fifo = ALLOC (cache_ptr, size);
480 if (uc->uc_fifo == NULL)
481 {
482 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
483 return 0;
484 }
485 BZERO (uc->uc_fifo, cache_ptr, size);
486 su->su_cache = (char *) uc;
487 return 1;
488 }
489
490
491 /*
492 * Set an entry in the cache
493 */
494 static void
495 cache_set (SVCXPRT *xprt, u_long replylen)
496 {
497 cache_ptr victim;
498 cache_ptr *vicp;
499 struct svcudp_data *su = su_data (xprt);
500 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
501 u_int loc;
502 char *newbuf;
503
504 /*
505 * Find space for the new entry, either by
506 * reusing an old entry, or by mallocing a new one
507 */
508 victim = uc->uc_fifo[uc->uc_nextvictim];
509 if (victim != NULL)
510 {
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)
515 ;
516 if (*vicp == NULL)
517 {
518 CACHE_PERROR (_("cache_set: victim not found"));
519 return;
520 }
521 *vicp = victim->cache_next; /* remote from cache */
522 newbuf = victim->cache_reply;
523 }
524 else
525 {
526 victim = ALLOC (struct cache_node, 1);
527 if (victim == NULL)
528 {
529 CACHE_PERROR (_("cache_set: victim alloc failed"));
530 return;
531 }
532 newbuf = mem_alloc (su->su_iosz);
533 if (newbuf == NULL)
534 {
535 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
536 return;
537 }
538 }
539
540 /*
541 * Store it away
542 */
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;
557 }
558
559 /*
560 * Try to get an entry from the cache
561 * return 1 if found, 0 if not found
562 */
563 static int
564 cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp)
565 {
566 u_int loc;
567 cache_ptr ent;
568 struct svcudp_data *su = su_data (xprt);
569 struct udp_cache *uc = (struct udp_cache *) su->su_cache;
570
571 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
572
573 loc = CACHE_LOC (xprt, su->su_xid);
574 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
575 {
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))
581 {
582 *replyp = ent->cache_reply;
583 *replylenp = ent->cache_replylen;
584 return 1;
585 }
586 }
587 /*
588 * Failed to find entry
589 * Remember a few things so we can do a set later
590 */
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));
595 return 0;
596 }