]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/libc/inet/rpc/svc_udp.c
update
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / libc / inet / rpc / 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 #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>
48
49 #ifdef IP_PKTINFO
50 #include <sys/uio.h>
51 #endif
52
53 #define rpc_buffer(xprt) ((xprt)->xp_p1)
54 #ifndef MAX
55 #define MAX(a, b)     ((a > b) ? a : b)
56 #endif
57
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 *);
64
65 static const struct xp_ops svcudp_op =
66 {
67   svcudp_recv,
68   svcudp_stat,
69   svcudp_getargs,
70   svcudp_reply,
71   svcudp_freeargs,
72   svcudp_destroy
73 };
74
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);
78
79 /*
80  * kept in xprt->xp_p2
81  */
82 struct svcudp_data
83   {
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 */
89   };
90 #define su_data(xprt)   ((struct svcudp_data *)(xprt->xp_p2))
91
92 /*
93  * Usage:
94  *      xprt = svcudp_create(sock);
95  *
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.
104  */
105 SVCXPRT *
106 svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
107 {
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;
115
116   if (sock == RPC_ANYSOCK)
117     {
118       if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
119         {
120           perror (_("svcudp_create: socket creation problem"));
121           return (SVCXPRT *) NULL;
122         }
123       madesock = TRUE;
124     }
125   memset ((char *) &addr, 0, sizeof (addr));
126   addr.sin_family = AF_INET;
127   if (bindresvport (sock, &addr))
128     {
129       addr.sin_port = 0;
130       (void) bind (sock, (struct sockaddr *) &addr, len);
131     }
132   if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
133     {
134       perror (_("svcudp_create - cannot getsockname"));
135       if (madesock)
136         (void) close (sock);
137       return (SVCXPRT *) NULL;
138     }
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)
143     {
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;
149     }
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;
159
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))
164     {
165       (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
166                       stderr);
167       return NULL;
168     }
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));
179
180   xprt_register (xprt);
181   return xprt;
182 }
183 libc_hidden_def(svcudp_bufcreate)
184
185 SVCXPRT *
186 svcudp_create (int sock)
187 {
188
189   return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
190 }
191 libc_hidden_def(svcudp_create)
192
193 static enum xprt_stat
194 svcudp_stat (SVCXPRT *xprt attribute_unused)
195 {
196
197   return XPRT_IDLE;
198 }
199
200 static bool_t
201 svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
202 {
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;
209
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
217
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)
225     {
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;
239     }
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)
256     {
257       if (cache_get (xprt, msg, &reply, &replylen))
258         {
259 #ifdef IP_PKTINFO
260           if (mesgp->msg_iovlen)
261             {
262               iovp->iov_base = reply;
263               iovp->iov_len = replylen;
264               (void) sendmsg (xprt->xp_sock, mesgp, 0);
265             }
266           else
267 #endif
268             (void) sendto (xprt->xp_sock, reply, (int) replylen, 0,
269                            (struct sockaddr *) &xprt->xp_raddr, len);
270           return TRUE;
271         }
272     }
273   return TRUE;
274 }
275
276 static bool_t
277 svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
278 {
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
287
288   xdrs->x_op = XDR_ENCODE;
289   XDR_SETPOS (xdrs, 0);
290   msg->rm_xid = su->su_xid;
291   if (xdr_replymsg (xdrs, msg))
292     {
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)
297         {
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);
302         }
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)
309         {
310           stat = TRUE;
311           if (su->su_cache && slen >= 0)
312             {
313               cache_set (xprt, (u_long) slen);
314             }
315         }
316     }
317   return stat;
318 }
319
320 static bool_t
321 svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
322 {
323
324   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
325 }
326
327 static bool_t
328 svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
329 {
330   XDR *xdrs = &(su_data (xprt)->su_xdrs);
331
332   xdrs->x_op = XDR_FREE;
333   return (*xdr_args) (xdrs, args_ptr);
334 }
335
336 static void
337 svcudp_destroy (SVCXPRT *xprt)
338 {
339   struct svcudp_data *su = su_data (xprt);
340
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));
347 }
348
349
350 /***********this could be a separate file*********************/
351
352 /*
353  * Fifo cache for udp server
354  * Copies pointers to reply buffers into fifo cache
355  * Buffers are sent again if retransmissions are detected.
356  */
357
358 #define SPARSENESS 4            /* 75% sparse */
359
360 #define CACHE_PERROR(msg)       \
361         (void) fprintf(stderr,"%s\n", msg)
362
363 #define ALLOC(type, size)       \
364         (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
365
366 #define BZERO(addr, type, size)  \
367         memset((char *) addr, 0, sizeof(type) * (int) (size))
368
369 /*
370  * An entry in the cache
371  */
372 typedef struct cache_node *cache_ptr;
373 struct cache_node
374   {
375     /*
376      * Index into cache is xid, proc, vers, prog and address
377      */
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;
383     /*
384      * The cached reply and length
385      */
386     char *cache_reply;
387     u_long cache_replylen;
388     /*
389      * Next node on the list, if there is a collision
390      */
391     cache_ptr cache_next;
392   };
393
394
395
396 /*
397  * The entire cache
398  */
399 struct udp_cache
400   {
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 */
409   };
410
411
412 /*
413  * the hashing function
414  */
415 #define CACHE_LOC(transp, xid)  \
416  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
417
418
419 /*
420  * Enable use of the cache.
421  * Note: there is no disable.
422  */
423 int svcudp_enablecache (SVCXPRT *transp, u_long size);
424 int
425 svcudp_enablecache (SVCXPRT *transp, u_long size)
426 {
427   struct svcudp_data *su = su_data (transp);
428   struct udp_cache *uc;
429
430   if (su->su_cache != NULL)
431     {
432       CACHE_PERROR (_("enablecache: cache already enabled"));
433       return 0;
434     }
435   uc = ALLOC (struct udp_cache, 1);
436   if (uc == NULL)
437     {
438       CACHE_PERROR (_("enablecache: could not allocate cache"));
439       return 0;
440     }
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)
445     {
446       CACHE_PERROR (_("enablecache: could not allocate cache data"));
447       return 0;
448     }
449   BZERO (uc->uc_entries, cache_ptr, size * SPARSENESS);
450   uc->uc_fifo = ALLOC (cache_ptr, size);
451   if (uc->uc_fifo == NULL)
452     {
453       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
454       return 0;
455     }
456   BZERO (uc->uc_fifo, cache_ptr, size);
457   su->su_cache = (char *) uc;
458   return 1;
459 }
460
461
462 /*
463  * Set an entry in the cache
464  */
465 static void
466 cache_set (SVCXPRT *xprt, u_long replylen)
467 {
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;
474
475   /*
476    * Find space for the new entry, either by
477    * reusing an old entry, or by mallocing a new one
478    */
479   victim = uc->uc_fifo[uc->uc_nextvictim];
480   if (victim != NULL)
481     {
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)
486         ;
487       if (*vicp == NULL)
488         {
489           CACHE_PERROR (_("cache_set: victim not found"));
490           return;
491         }
492       *vicp = victim->cache_next;       /* remote from cache */
493       newbuf = victim->cache_reply;
494     }
495   else
496     {
497       victim = ALLOC (struct cache_node, 1);
498       if (victim == NULL)
499         {
500           CACHE_PERROR (_("cache_set: victim alloc failed"));
501           return;
502         }
503       newbuf = mem_alloc (su->su_iosz);
504       if (newbuf == NULL)
505         {
506           CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
507           return;
508         }
509     }
510
511   /*
512    * Store it away
513    */
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;
528 }
529
530 /*
531  * Try to get an entry from the cache
532  * return 1 if found, 0 if not found
533  */
534 static int
535 cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp, u_long *replylenp)
536 {
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;
541
542 #define EQADDR(a1, a2)  (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
543
544   loc = CACHE_LOC (xprt, su->su_xid);
545   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
546     {
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))
552         {
553           *replyp = ent->cache_reply;
554           *replylenp = ent->cache_replylen;
555           return 1;
556         }
557     }
558   /*
559    * Failed to find entry
560    * Remember a few things so we can do a set later
561    */
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;
567 }