do not rely on libc providing TEMP_FAILURE_RETRY
[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 }