fdwalk should return 0 on an empty directory
[kopensolaris-gnu/glibc.git] / sunrpc / 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 !defined(lint) && defined(SCCSIDS)
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 #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>
49
50 #ifdef IP_PKTINFO
51 #include <sys/uio.h>
52 #endif
53
54 #ifdef USE_IN_LIBIO
55 # include <wchar.h>
56 # include <libio/iolibio.h>
57 #endif
58
59 #define rpc_buffer(xprt) ((xprt)->xp_p1)
60 #ifndef MAX
61 #define MAX(a, b)     ((a > b) ? a : b)
62 #endif
63
64 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
65 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
66 static enum xprt_stat svcudp_stat (SVCXPRT *);
67 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
68 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
69 static void svcudp_destroy (SVCXPRT *);
70
71 static const struct xp_ops svcudp_op =
72 {
73   svcudp_recv,
74   svcudp_stat,
75   svcudp_getargs,
76   svcudp_reply,
77   svcudp_freeargs,
78   svcudp_destroy
79 };
80
81 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
82                       u_long *replylenp);
83 static void cache_set (SVCXPRT *xprt, u_long replylen);
84
85 /*
86  * kept in xprt->xp_p2
87  */
88 struct svcudp_data
89   {
90     u_int su_iosz;              /* byte size of send.recv buffer */
91     u_long su_xid;              /* transaction id */
92     XDR su_xdrs;                /* XDR handle */
93     char su_verfbody[MAX_AUTH_BYTES];   /* verifier body */
94     char *su_cache;             /* cached data, NULL if no cache */
95   };
96 #define su_data(xprt)   ((struct svcudp_data *)(xprt->xp_p2))
97
98 /*
99  * Usage:
100  *      xprt = svcudp_create(sock);
101  *
102  * If sock<0 then a socket is created, else sock is used.
103  * If the socket, sock is not bound to a port then svcudp_create
104  * binds it to an arbitrary port.  In any (successful) case,
105  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
106  * associated port number.
107  * Once *xprt is initialized, it is registered as a transporter;
108  * see (svc.h, xprt_register).
109  * The routines returns NULL if a problem occurred.
110  */
111 SVCXPRT *
112 svcudp_bufcreate (sock, sendsz, recvsz)
113      int sock;
114      u_int sendsz, recvsz;
115 {
116   bool_t madesock = FALSE;
117   SVCXPRT *xprt;
118   struct svcudp_data *su;
119   struct sockaddr_in addr;
120   socklen_t len = sizeof (struct sockaddr_in);
121   int pad;
122   void *buf;
123
124   if (sock == RPC_ANYSOCK)
125     {
126       if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
127         {
128           perror (_("svcudp_create: socket creation problem"));
129           return (SVCXPRT *) NULL;
130         }
131       madesock = TRUE;
132     }
133   __bzero ((char *) &addr, sizeof (addr));
134   addr.sin_family = AF_INET;
135   if (bindresvport (sock, &addr))
136     {
137       addr.sin_port = 0;
138       (void) __bind (sock, (struct sockaddr *) &addr, len);
139     }
140   if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
141     {
142       perror (_("svcudp_create - cannot getsockname"));
143       if (madesock)
144         (void) __close (sock);
145       return (SVCXPRT *) NULL;
146     }
147   xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
148   su = (struct svcudp_data *) mem_alloc (sizeof (*su));
149   buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
150   if (xprt == NULL || su == NULL || buf == NULL)
151     {
152       (void) __fxprintf (NULL, "%s", _("svcudp_create: out of memory\n"));
153       mem_free (xprt, sizeof (SVCXPRT));
154       mem_free (su, sizeof (*su));
155       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
156       return NULL;
157     }
158   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
159   rpc_buffer (xprt) = buf;
160   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
161                          XDR_DECODE);
162   su->su_cache = NULL;
163   xprt->xp_p2 = (caddr_t) su;
164   xprt->xp_verf.oa_base = su->su_verfbody;
165   xprt->xp_ops = &svcudp_op;
166   xprt->xp_port = ntohs (addr.sin_port);
167   xprt->xp_sock = sock;
168
169 #ifdef IP_PKTINFO
170   if ((sizeof (struct iovec) + sizeof (struct msghdr)
171        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
172       > sizeof (xprt->xp_pad))
173     {
174       (void) __fxprintf (NULL,"%s", _("\
175 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
176       return NULL;
177     }
178   pad = 1;
179   if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
180                     sizeof (pad)) == 0)
181     /* Set the padding to all 1s. */
182     pad = 0xff;
183   else
184 #endif
185     /* Clear the padding. */
186     pad = 0;
187   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
188
189   xprt_register (xprt);
190   return xprt;
191 }
192 INTDEF (svcudp_bufcreate)
193
194 SVCXPRT *
195 svcudp_create (sock)
196      int sock;
197 {
198   return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
199 }
200 INTDEF (svcudp_create)
201
202 static enum xprt_stat
203 svcudp_stat (xprt)
204      SVCXPRT *xprt;
205 {
206
207   return XPRT_IDLE;
208 }
209
210 static bool_t
211 svcudp_recv (xprt, msg)
212      SVCXPRT *xprt;
213      struct rpc_msg *msg;
214 {
215   struct svcudp_data *su = su_data (xprt);
216   XDR *xdrs = &(su->su_xdrs);
217   int rlen;
218   char *reply;
219   u_long replylen;
220   socklen_t len;
221
222   /* It is very tricky when you have IP aliases. We want to make sure
223      that we are sending the packet from the IP address where the
224      incoming packet is addressed to. H.J. */
225 #ifdef IP_PKTINFO
226   struct iovec *iovp;
227   struct msghdr *mesgp;
228 #endif
229
230 again:
231   /* FIXME -- should xp_addrlen be a size_t?  */
232   len = (socklen_t) sizeof(struct sockaddr_in);
233 #ifdef IP_PKTINFO
234   iovp = (struct iovec *) &xprt->xp_pad [0];
235   mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
236   if (mesgp->msg_iovlen)
237     {
238       iovp->iov_base = rpc_buffer (xprt);
239       iovp->iov_len = su->su_iosz;
240       mesgp->msg_iov = iovp;
241       mesgp->msg_iovlen = 1;
242       mesgp->msg_name = &(xprt->xp_raddr);
243       mesgp->msg_namelen = len;
244       mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
245                                           + sizeof (struct msghdr)];
246       mesgp->msg_controllen = sizeof(xprt->xp_pad)
247                               - sizeof (struct iovec) - sizeof (struct msghdr);
248       rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
249       if (rlen >= 0)
250         {
251           struct cmsghdr *cmsg;
252           len = mesgp->msg_namelen;
253           cmsg = CMSG_FIRSTHDR (mesgp);
254           if (cmsg == NULL
255               || CMSG_NXTHDR (mesgp, cmsg) != NULL
256               || cmsg->cmsg_level != SOL_IP
257               || cmsg->cmsg_type != IP_PKTINFO
258               || cmsg->cmsg_len < (sizeof (struct cmsghdr)
259                                    + sizeof (struct in_pktinfo)))
260             {
261               /* Not a simple IP_PKTINFO, ignore it.  */
262               mesgp->msg_control = NULL;
263               mesgp->msg_controllen = 0;
264             }
265           else
266             {
267               /* It was a simple IP_PKTIFO as we expected, discard the
268                  interface field.  */
269               struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
270               pkti->ipi_ifindex = 0;
271             }
272         }
273     }
274   else
275 #endif
276     rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
277                        (int) su->su_iosz, 0,
278                        (struct sockaddr *) &(xprt->xp_raddr), &len);
279   xprt->xp_addrlen = len;
280   if (rlen == -1 && errno == EINTR)
281     goto again;
282   if (rlen < 16)                /* < 4 32-bit ints? */
283     return FALSE;
284   xdrs->x_op = XDR_DECODE;
285   XDR_SETPOS (xdrs, 0);
286   if (!INTUSE(xdr_callmsg) (xdrs, msg))
287     return FALSE;
288   su->su_xid = msg->rm_xid;
289   if (su->su_cache != NULL)
290     {
291       if (cache_get (xprt, msg, &reply, &replylen))
292         {
293 #ifdef IP_PKTINFO
294           if (mesgp->msg_iovlen)
295             {
296               iovp->iov_base = reply;
297               iovp->iov_len = replylen;
298               (void) __sendmsg (xprt->xp_sock, mesgp, 0);
299             }
300           else
301 #endif
302             (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
303                              (struct sockaddr *) &xprt->xp_raddr, len);
304           return TRUE;
305         }
306     }
307   return TRUE;
308 }
309
310 static bool_t
311 svcudp_reply (xprt, msg)
312      SVCXPRT *xprt;
313      struct rpc_msg *msg;
314 {
315   struct svcudp_data *su = su_data (xprt);
316   XDR *xdrs = &(su->su_xdrs);
317   int slen, sent;
318   bool_t stat = FALSE;
319 #ifdef IP_PKTINFO
320   struct iovec *iovp;
321   struct msghdr *mesgp;
322 #endif
323
324   xdrs->x_op = XDR_ENCODE;
325   XDR_SETPOS (xdrs, 0);
326   msg->rm_xid = su->su_xid;
327   if (INTUSE(xdr_replymsg) (xdrs, msg))
328     {
329       slen = (int) XDR_GETPOS (xdrs);
330 #ifdef IP_PKTINFO
331       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
332       if (mesgp->msg_iovlen)
333         {
334           iovp = (struct iovec *) &xprt->xp_pad [0];
335           iovp->iov_base = rpc_buffer (xprt);
336           iovp->iov_len = slen;
337           sent = __sendmsg (xprt->xp_sock, mesgp, 0);
338         }
339       else
340 #endif
341         sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
342                          (struct sockaddr *) &(xprt->xp_raddr),
343                          xprt->xp_addrlen);
344       if (sent == slen)
345         {
346           stat = TRUE;
347           if (su->su_cache && slen >= 0)
348             {
349               cache_set (xprt, (u_long) slen);
350             }
351         }
352     }
353   return stat;
354 }
355
356 static bool_t
357 svcudp_getargs (xprt, xdr_args, args_ptr)
358      SVCXPRT *xprt;
359      xdrproc_t xdr_args;
360      caddr_t args_ptr;
361 {
362
363   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
364 }
365
366 static bool_t
367 svcudp_freeargs (xprt, xdr_args, args_ptr)
368      SVCXPRT *xprt;
369      xdrproc_t xdr_args;
370      caddr_t args_ptr;
371 {
372   XDR *xdrs = &(su_data (xprt)->su_xdrs);
373
374   xdrs->x_op = XDR_FREE;
375   return (*xdr_args) (xdrs, args_ptr);
376 }
377
378 static void
379 svcudp_destroy (xprt)
380      SVCXPRT *xprt;
381 {
382   struct svcudp_data *su = su_data (xprt);
383
384   xprt_unregister (xprt);
385   (void) __close (xprt->xp_sock);
386   XDR_DESTROY (&(su->su_xdrs));
387   mem_free (rpc_buffer (xprt), su->su_iosz);
388   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
389   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
390 }
391
392
393 /***********this could be a separate file*********************/
394
395 /*
396  * Fifo cache for udp server
397  * Copies pointers to reply buffers into fifo cache
398  * Buffers are sent again if retransmissions are detected.
399  */
400
401 #define SPARSENESS 4            /* 75% sparse */
402
403 #define CACHE_PERROR(msg)       \
404         (void) __fxprintf(NULL, "%s\n", msg)
405
406 #define ALLOC(type, size)       \
407         (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
408
409 #define CALLOC(type, size)      \
410   (type *) calloc (sizeof (type), size)
411
412 /*
413  * An entry in the cache
414  */
415 typedef struct cache_node *cache_ptr;
416 struct cache_node
417   {
418     /*
419      * Index into cache is xid, proc, vers, prog and address
420      */
421     u_long cache_xid;
422     u_long cache_proc;
423     u_long cache_vers;
424     u_long cache_prog;
425     struct sockaddr_in cache_addr;
426     /*
427      * The cached reply and length
428      */
429     char *cache_reply;
430     u_long cache_replylen;
431     /*
432      * Next node on the list, if there is a collision
433      */
434     cache_ptr cache_next;
435   };
436
437
438
439 /*
440  * The entire cache
441  */
442 struct udp_cache
443   {
444     u_long uc_size;             /* size of cache */
445     cache_ptr *uc_entries;      /* hash table of entries in cache */
446     cache_ptr *uc_fifo;         /* fifo list of entries in cache */
447     u_long uc_nextvictim;       /* points to next victim in fifo list */
448     u_long uc_prog;             /* saved program number */
449     u_long uc_vers;             /* saved version number */
450     u_long uc_proc;             /* saved procedure number */
451     struct sockaddr_in uc_addr; /* saved caller's address */
452   };
453
454
455 /*
456  * the hashing function
457  */
458 #define CACHE_LOC(transp, xid)  \
459  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
460
461
462 /*
463  * Enable use of the cache.
464  * Note: there is no disable.
465  */
466 int
467 svcudp_enablecache (SVCXPRT *transp, u_long size)
468 {
469   struct svcudp_data *su = su_data (transp);
470   struct udp_cache *uc;
471
472   if (su->su_cache != NULL)
473     {
474       CACHE_PERROR (_("enablecache: cache already enabled"));
475       return 0;
476     }
477   uc = ALLOC (struct udp_cache, 1);
478   if (uc == NULL)
479     {
480       CACHE_PERROR (_("enablecache: could not allocate cache"));
481       return 0;
482     }
483   uc->uc_size = size;
484   uc->uc_nextvictim = 0;
485   uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
486   if (uc->uc_entries == NULL)
487     {
488       mem_free (uc, sizeof (struct udp_cache));
489       CACHE_PERROR (_("enablecache: could not allocate cache data"));
490       return 0;
491     }
492   uc->uc_fifo = CALLOC (cache_ptr, size);
493   if (uc->uc_fifo == NULL)
494     {
495       mem_free (uc->uc_entries, size * SPARSENESS);
496       mem_free (uc, sizeof (struct udp_cache));
497       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
498       return 0;
499     }
500   su->su_cache = (char *) uc;
501   return 1;
502 }
503
504
505 /*
506  * Set an entry in the cache
507  */
508 static void
509 cache_set (SVCXPRT *xprt, u_long replylen)
510 {
511   cache_ptr victim;
512   cache_ptr *vicp;
513   struct svcudp_data *su = su_data (xprt);
514   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
515   u_int loc;
516   char *newbuf;
517
518   /*
519    * Find space for the new entry, either by
520    * reusing an old entry, or by mallocing a new one
521    */
522   victim = uc->uc_fifo[uc->uc_nextvictim];
523   if (victim != NULL)
524     {
525       loc = CACHE_LOC (xprt, victim->cache_xid);
526       for (vicp = &uc->uc_entries[loc];
527            *vicp != NULL && *vicp != victim;
528            vicp = &(*vicp)->cache_next)
529         ;
530       if (*vicp == NULL)
531         {
532           CACHE_PERROR (_("cache_set: victim not found"));
533           return;
534         }
535       *vicp = victim->cache_next;       /* remote from cache */
536       newbuf = victim->cache_reply;
537     }
538   else
539     {
540       victim = ALLOC (struct cache_node, 1);
541       if (victim == NULL)
542         {
543           CACHE_PERROR (_("cache_set: victim alloc failed"));
544           return;
545         }
546       newbuf = mem_alloc (su->su_iosz);
547       if (newbuf == NULL)
548         {
549           mem_free (victim, sizeof (struct cache_node));
550           CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
551           return;
552         }
553     }
554
555   /*
556    * Store it away
557    */
558   victim->cache_replylen = replylen;
559   victim->cache_reply = rpc_buffer (xprt);
560   rpc_buffer (xprt) = newbuf;
561   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
562                          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;
573 }
574
575 /*
576  * Try to get an entry from the cache
577  * return 1 if found, 0 if not found
578  */
579 static int
580 cache_get (xprt, msg, replyp, replylenp)
581      SVCXPRT *xprt;
582      struct rpc_msg *msg;
583      char **replyp;
584      u_long *replylenp;
585 {
586   u_int loc;
587   cache_ptr ent;
588   struct svcudp_data *su = su_data (xprt);
589   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
590
591 #define EQADDR(a1, a2)  (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
592
593   loc = CACHE_LOC (xprt, su->su_xid);
594   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
595     {
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))
601         {
602           *replyp = ent->cache_reply;
603           *replylenp = ent->cache_replylen;
604           return 1;
605         }
606     }
607   /*
608    * Failed to find entry
609    * Remember a few things so we can do a set later
610    */
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));
615   return 0;
616 }