Back to index

glibc  2.9
svc_udp.c
Go to the documentation of this file.
00001 /* @(#)svc_udp.c     2.2 88/07/29 4.0 RPCSRC */
00002 /*
00003  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
00004  * unrestricted use provided that this legend is included on all tape
00005  * media and as a part of the software program in whole or part.  Users
00006  * may copy or modify Sun RPC without charge, but are not authorized
00007  * to license or distribute it to anyone else except as part of a product or
00008  * program developed by the user.
00009  *
00010  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
00011  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
00012  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
00013  *
00014  * Sun RPC is provided with no support and without any obligation on the
00015  * part of Sun Microsystems, Inc. to assist in its use, correction,
00016  * modification or enhancement.
00017  *
00018  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
00019  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
00020  * OR ANY PART THEREOF.
00021  *
00022  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
00023  * or profits or other special, indirect and consequential damages, even if
00024  * Sun has been advised of the possibility of such damages.
00025  *
00026  * Sun Microsystems, Inc.
00027  * 2550 Garcia Avenue
00028  * Mountain View, California  94043
00029  */
00030 #if !defined(lint) && defined(SCCSIDS)
00031 static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
00032 #endif
00033 
00034 /*
00035  * svc_udp.c,
00036  * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
00037  * achieving execute-at-most-once semantics.)
00038  *
00039  * Copyright (C) 1984, Sun Microsystems, Inc.
00040  */
00041 
00042 #include <stdio.h>
00043 #include <unistd.h>
00044 #include <string.h>
00045 #include <rpc/rpc.h>
00046 #include <sys/socket.h>
00047 #include <errno.h>
00048 #include <libintl.h>
00049 
00050 #ifdef IP_PKTINFO
00051 #include <sys/uio.h>
00052 #endif
00053 
00054 #ifdef USE_IN_LIBIO
00055 # include <wchar.h>
00056 # include <libio/iolibio.h>
00057 #endif
00058 
00059 #define rpc_buffer(xprt) ((xprt)->xp_p1)
00060 #ifndef MAX
00061 #define MAX(a, b)     ((a > b) ? a : b)
00062 #endif
00063 
00064 static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
00065 static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
00066 static enum xprt_stat svcudp_stat (SVCXPRT *);
00067 static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
00068 static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
00069 static void svcudp_destroy (SVCXPRT *);
00070 
00071 static const struct xp_ops svcudp_op =
00072 {
00073   svcudp_recv,
00074   svcudp_stat,
00075   svcudp_getargs,
00076   svcudp_reply,
00077   svcudp_freeargs,
00078   svcudp_destroy
00079 };
00080 
00081 static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
00082                     u_long *replylenp);
00083 static void cache_set (SVCXPRT *xprt, u_long replylen);
00084 
00085 /*
00086  * kept in xprt->xp_p2
00087  */
00088 struct svcudp_data
00089   {
00090     u_int su_iosz;          /* byte size of send.recv buffer */
00091     u_long su_xid;          /* transaction id */
00092     XDR su_xdrs;            /* XDR handle */
00093     char su_verfbody[MAX_AUTH_BYTES];     /* verifier body */
00094     char *su_cache;         /* cached data, NULL if no cache */
00095   };
00096 #define       su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
00097 
00098 /*
00099  * Usage:
00100  *      xprt = svcudp_create(sock);
00101  *
00102  * If sock<0 then a socket is created, else sock is used.
00103  * If the socket, sock is not bound to a port then svcudp_create
00104  * binds it to an arbitrary port.  In any (successful) case,
00105  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
00106  * associated port number.
00107  * Once *xprt is initialized, it is registered as a transporter;
00108  * see (svc.h, xprt_register).
00109  * The routines returns NULL if a problem occurred.
00110  */
00111 SVCXPRT *
00112 svcudp_bufcreate (sock, sendsz, recvsz)
00113      int sock;
00114      u_int sendsz, recvsz;
00115 {
00116   bool_t madesock = FALSE;
00117   SVCXPRT *xprt;
00118   struct svcudp_data *su;
00119   struct sockaddr_in addr;
00120   socklen_t len = sizeof (struct sockaddr_in);
00121   int pad;
00122   void *buf;
00123 
00124   if (sock == RPC_ANYSOCK)
00125     {
00126       if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
00127        {
00128          perror (_("svcudp_create: socket creation problem"));
00129          return (SVCXPRT *) NULL;
00130        }
00131       madesock = TRUE;
00132     }
00133   __bzero ((char *) &addr, sizeof (addr));
00134   addr.sin_family = AF_INET;
00135   if (bindresvport (sock, &addr))
00136     {
00137       addr.sin_port = 0;
00138       (void) __bind (sock, (struct sockaddr *) &addr, len);
00139     }
00140   if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
00141     {
00142       perror (_("svcudp_create - cannot getsockname"));
00143       if (madesock)
00144        (void) __close (sock);
00145       return (SVCXPRT *) NULL;
00146     }
00147   xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
00148   su = (struct svcudp_data *) mem_alloc (sizeof (*su));
00149   buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
00150   if (xprt == NULL || su == NULL || buf == NULL)
00151     {
00152       (void) __fxprintf (NULL, "%s: %s",
00153                       "svcudp_create",  _("out of memory\n"));
00154       mem_free (xprt, sizeof (SVCXPRT));
00155       mem_free (su, sizeof (*su));
00156       mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
00157       return NULL;
00158     }
00159   su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
00160   rpc_buffer (xprt) = buf;
00161   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
00162                       XDR_DECODE);
00163   su->su_cache = NULL;
00164   xprt->xp_p2 = (caddr_t) su;
00165   xprt->xp_verf.oa_base = su->su_verfbody;
00166   xprt->xp_ops = &svcudp_op;
00167   xprt->xp_port = ntohs (addr.sin_port);
00168   xprt->xp_sock = sock;
00169 
00170 #ifdef IP_PKTINFO
00171   if ((sizeof (struct iovec) + sizeof (struct msghdr)
00172        + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
00173       > sizeof (xprt->xp_pad))
00174     {
00175       (void) __fxprintf (NULL,"%s", _("\
00176 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
00177       return NULL;
00178     }
00179   pad = 1;
00180   if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
00181                   sizeof (pad)) == 0)
00182     /* Set the padding to all 1s. */
00183     pad = 0xff;
00184   else
00185 #endif
00186     /* Clear the padding. */
00187     pad = 0;
00188   memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
00189 
00190   xprt_register (xprt);
00191   return xprt;
00192 }
00193 INTDEF (svcudp_bufcreate)
00194 
00195 SVCXPRT *
00196 svcudp_create (sock)
00197      int sock;
00198 {
00199   return INTUSE(svcudp_bufcreate) (sock, UDPMSGSIZE, UDPMSGSIZE);
00200 }
00201 INTDEF (svcudp_create)
00202 
00203 static enum xprt_stat
00204 svcudp_stat (xprt)
00205      SVCXPRT *xprt;
00206 {
00207 
00208   return XPRT_IDLE;
00209 }
00210 
00211 static bool_t
00212 svcudp_recv (xprt, msg)
00213      SVCXPRT *xprt;
00214      struct rpc_msg *msg;
00215 {
00216   struct svcudp_data *su = su_data (xprt);
00217   XDR *xdrs = &(su->su_xdrs);
00218   int rlen;
00219   char *reply;
00220   u_long replylen;
00221   socklen_t len;
00222 
00223   /* It is very tricky when you have IP aliases. We want to make sure
00224      that we are sending the packet from the IP address where the
00225      incoming packet is addressed to. H.J. */
00226 #ifdef IP_PKTINFO
00227   struct iovec *iovp;
00228   struct msghdr *mesgp;
00229 #endif
00230 
00231 again:
00232   /* FIXME -- should xp_addrlen be a size_t?  */
00233   len = (socklen_t) sizeof(struct sockaddr_in);
00234 #ifdef IP_PKTINFO
00235   iovp = (struct iovec *) &xprt->xp_pad [0];
00236   mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
00237   if (mesgp->msg_iovlen)
00238     {
00239       iovp->iov_base = rpc_buffer (xprt);
00240       iovp->iov_len = su->su_iosz;
00241       mesgp->msg_iov = iovp;
00242       mesgp->msg_iovlen = 1;
00243       mesgp->msg_name = &(xprt->xp_raddr);
00244       mesgp->msg_namelen = len;
00245       mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
00246                                      + sizeof (struct msghdr)];
00247       mesgp->msg_controllen = sizeof(xprt->xp_pad)
00248                            - sizeof (struct iovec) - sizeof (struct msghdr);
00249       rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
00250       if (rlen >= 0)
00251        {
00252          struct cmsghdr *cmsg;
00253          len = mesgp->msg_namelen;
00254          cmsg = CMSG_FIRSTHDR (mesgp);
00255          if (cmsg == NULL
00256              || CMSG_NXTHDR (mesgp, cmsg) != NULL
00257              || cmsg->cmsg_level != SOL_IP
00258              || cmsg->cmsg_type != IP_PKTINFO
00259              || cmsg->cmsg_len < (sizeof (struct cmsghdr)
00260                                + sizeof (struct in_pktinfo)))
00261            {
00262              /* Not a simple IP_PKTINFO, ignore it.  */
00263              mesgp->msg_control = NULL;
00264              mesgp->msg_controllen = 0;
00265            }
00266          else
00267            {
00268              /* It was a simple IP_PKTIFO as we expected, discard the
00269                interface field.  */
00270              struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
00271              pkti->ipi_ifindex = 0;
00272            }
00273        }
00274     }
00275   else
00276 #endif
00277     rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
00278                      (int) su->su_iosz, 0,
00279                      (struct sockaddr *) &(xprt->xp_raddr), &len);
00280   xprt->xp_addrlen = len;
00281   if (rlen == -1 && errno == EINTR)
00282     goto again;
00283   if (rlen < 16)            /* < 4 32-bit ints? */
00284     return FALSE;
00285   xdrs->x_op = XDR_DECODE;
00286   XDR_SETPOS (xdrs, 0);
00287   if (!INTUSE(xdr_callmsg) (xdrs, msg))
00288     return FALSE;
00289   su->su_xid = msg->rm_xid;
00290   if (su->su_cache != NULL)
00291     {
00292       if (cache_get (xprt, msg, &reply, &replylen))
00293        {
00294 #ifdef IP_PKTINFO
00295          if (mesgp->msg_iovlen)
00296            {
00297              iovp->iov_base = reply;
00298              iovp->iov_len = replylen;
00299              (void) __sendmsg (xprt->xp_sock, mesgp, 0);
00300            }
00301          else
00302 #endif
00303            (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
00304                           (struct sockaddr *) &xprt->xp_raddr, len);
00305          return TRUE;
00306        }
00307     }
00308   return TRUE;
00309 }
00310 
00311 static bool_t
00312 svcudp_reply (xprt, msg)
00313      SVCXPRT *xprt;
00314      struct rpc_msg *msg;
00315 {
00316   struct svcudp_data *su = su_data (xprt);
00317   XDR *xdrs = &(su->su_xdrs);
00318   int slen, sent;
00319   bool_t stat = FALSE;
00320 #ifdef IP_PKTINFO
00321   struct iovec *iovp;
00322   struct msghdr *mesgp;
00323 #endif
00324 
00325   xdrs->x_op = XDR_ENCODE;
00326   XDR_SETPOS (xdrs, 0);
00327   msg->rm_xid = su->su_xid;
00328   if (INTUSE(xdr_replymsg) (xdrs, msg))
00329     {
00330       slen = (int) XDR_GETPOS (xdrs);
00331 #ifdef IP_PKTINFO
00332       mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
00333       if (mesgp->msg_iovlen)
00334        {
00335          iovp = (struct iovec *) &xprt->xp_pad [0];
00336          iovp->iov_base = rpc_buffer (xprt);
00337          iovp->iov_len = slen;
00338          sent = __sendmsg (xprt->xp_sock, mesgp, 0);
00339        }
00340       else
00341 #endif
00342        sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
00343                       (struct sockaddr *) &(xprt->xp_raddr),
00344                       xprt->xp_addrlen);
00345       if (sent == slen)
00346        {
00347          stat = TRUE;
00348          if (su->su_cache && slen >= 0)
00349            {
00350              cache_set (xprt, (u_long) slen);
00351            }
00352        }
00353     }
00354   return stat;
00355 }
00356 
00357 static bool_t
00358 svcudp_getargs (xprt, xdr_args, args_ptr)
00359      SVCXPRT *xprt;
00360      xdrproc_t xdr_args;
00361      caddr_t args_ptr;
00362 {
00363 
00364   return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
00365 }
00366 
00367 static bool_t
00368 svcudp_freeargs (xprt, xdr_args, args_ptr)
00369      SVCXPRT *xprt;
00370      xdrproc_t xdr_args;
00371      caddr_t args_ptr;
00372 {
00373   XDR *xdrs = &(su_data (xprt)->su_xdrs);
00374 
00375   xdrs->x_op = XDR_FREE;
00376   return (*xdr_args) (xdrs, args_ptr);
00377 }
00378 
00379 static void
00380 svcudp_destroy (xprt)
00381      SVCXPRT *xprt;
00382 {
00383   struct svcudp_data *su = su_data (xprt);
00384 
00385   xprt_unregister (xprt);
00386   (void) __close (xprt->xp_sock);
00387   XDR_DESTROY (&(su->su_xdrs));
00388   mem_free (rpc_buffer (xprt), su->su_iosz);
00389   mem_free ((caddr_t) su, sizeof (struct svcudp_data));
00390   mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
00391 }
00392 
00393 
00394 /***********this could be a separate file*********************/
00395 
00396 /*
00397  * Fifo cache for udp server
00398  * Copies pointers to reply buffers into fifo cache
00399  * Buffers are sent again if retransmissions are detected.
00400  */
00401 
00402 #define SPARSENESS 4        /* 75% sparse */
00403 
00404 #define CACHE_PERROR(msg)   \
00405        (void) __fxprintf(NULL, "%s\n", msg)
00406 
00407 #define ALLOC(type, size)   \
00408        (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
00409 
00410 #define CALLOC(type, size)  \
00411   (type *) calloc (sizeof (type), size)
00412 
00413 /*
00414  * An entry in the cache
00415  */
00416 typedef struct cache_node *cache_ptr;
00417 struct cache_node
00418   {
00419     /*
00420      * Index into cache is xid, proc, vers, prog and address
00421      */
00422     u_long cache_xid;
00423     u_long cache_proc;
00424     u_long cache_vers;
00425     u_long cache_prog;
00426     struct sockaddr_in cache_addr;
00427     /*
00428      * The cached reply and length
00429      */
00430     char *cache_reply;
00431     u_long cache_replylen;
00432     /*
00433      * Next node on the list, if there is a collision
00434      */
00435     cache_ptr cache_next;
00436   };
00437 
00438 
00439 
00440 /*
00441  * The entire cache
00442  */
00443 struct udp_cache
00444   {
00445     u_long uc_size;         /* size of cache */
00446     cache_ptr *uc_entries;  /* hash table of entries in cache */
00447     cache_ptr *uc_fifo;            /* fifo list of entries in cache */
00448     u_long uc_nextvictim;   /* points to next victim in fifo list */
00449     u_long uc_prog;         /* saved program number */
00450     u_long uc_vers;         /* saved version number */
00451     u_long uc_proc;         /* saved procedure number */
00452     struct sockaddr_in uc_addr;    /* saved caller's address */
00453   };
00454 
00455 
00456 /*
00457  * the hashing function
00458  */
00459 #define CACHE_LOC(transp, xid)     \
00460  (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
00461 
00462 
00463 /*
00464  * Enable use of the cache.
00465  * Note: there is no disable.
00466  */
00467 int
00468 svcudp_enablecache (SVCXPRT *transp, u_long size)
00469 {
00470   struct svcudp_data *su = su_data (transp);
00471   struct udp_cache *uc;
00472 
00473   if (su->su_cache != NULL)
00474     {
00475       CACHE_PERROR (_("enablecache: cache already enabled"));
00476       return 0;
00477     }
00478   uc = ALLOC (struct udp_cache, 1);
00479   if (uc == NULL)
00480     {
00481       CACHE_PERROR (_("enablecache: could not allocate cache"));
00482       return 0;
00483     }
00484   uc->uc_size = size;
00485   uc->uc_nextvictim = 0;
00486   uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
00487   if (uc->uc_entries == NULL)
00488     {
00489       mem_free (uc, sizeof (struct udp_cache));
00490       CACHE_PERROR (_("enablecache: could not allocate cache data"));
00491       return 0;
00492     }
00493   uc->uc_fifo = CALLOC (cache_ptr, size);
00494   if (uc->uc_fifo == NULL)
00495     {
00496       mem_free (uc->uc_entries, size * SPARSENESS);
00497       mem_free (uc, sizeof (struct udp_cache));
00498       CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
00499       return 0;
00500     }
00501   su->su_cache = (char *) uc;
00502   return 1;
00503 }
00504 
00505 
00506 /*
00507  * Set an entry in the cache
00508  */
00509 static void
00510 cache_set (SVCXPRT *xprt, u_long replylen)
00511 {
00512   cache_ptr victim;
00513   cache_ptr *vicp;
00514   struct svcudp_data *su = su_data (xprt);
00515   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
00516   u_int loc;
00517   char *newbuf;
00518 
00519   /*
00520    * Find space for the new entry, either by
00521    * reusing an old entry, or by mallocing a new one
00522    */
00523   victim = uc->uc_fifo[uc->uc_nextvictim];
00524   if (victim != NULL)
00525     {
00526       loc = CACHE_LOC (xprt, victim->cache_xid);
00527       for (vicp = &uc->uc_entries[loc];
00528           *vicp != NULL && *vicp != victim;
00529           vicp = &(*vicp)->cache_next)
00530        ;
00531       if (*vicp == NULL)
00532        {
00533          CACHE_PERROR (_("cache_set: victim not found"));
00534          return;
00535        }
00536       *vicp = victim->cache_next;  /* remote from cache */
00537       newbuf = victim->cache_reply;
00538     }
00539   else
00540     {
00541       victim = ALLOC (struct cache_node, 1);
00542       if (victim == NULL)
00543        {
00544          CACHE_PERROR (_("cache_set: victim alloc failed"));
00545          return;
00546        }
00547       newbuf = mem_alloc (su->su_iosz);
00548       if (newbuf == NULL)
00549        {
00550          mem_free (victim, sizeof (struct cache_node));
00551          CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
00552          return;
00553        }
00554     }
00555 
00556   /*
00557    * Store it away
00558    */
00559   victim->cache_replylen = replylen;
00560   victim->cache_reply = rpc_buffer (xprt);
00561   rpc_buffer (xprt) = newbuf;
00562   INTUSE(xdrmem_create) (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz,
00563                       XDR_ENCODE);
00564   victim->cache_xid = su->su_xid;
00565   victim->cache_proc = uc->uc_proc;
00566   victim->cache_vers = uc->uc_vers;
00567   victim->cache_prog = uc->uc_prog;
00568   victim->cache_addr = uc->uc_addr;
00569   loc = CACHE_LOC (xprt, victim->cache_xid);
00570   victim->cache_next = uc->uc_entries[loc];
00571   uc->uc_entries[loc] = victim;
00572   uc->uc_fifo[uc->uc_nextvictim++] = victim;
00573   uc->uc_nextvictim %= uc->uc_size;
00574 }
00575 
00576 /*
00577  * Try to get an entry from the cache
00578  * return 1 if found, 0 if not found
00579  */
00580 static int
00581 cache_get (xprt, msg, replyp, replylenp)
00582      SVCXPRT *xprt;
00583      struct rpc_msg *msg;
00584      char **replyp;
00585      u_long *replylenp;
00586 {
00587   u_int loc;
00588   cache_ptr ent;
00589   struct svcudp_data *su = su_data (xprt);
00590   struct udp_cache *uc = (struct udp_cache *) su->su_cache;
00591 
00592 #define EQADDR(a1, a2)      (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
00593 
00594   loc = CACHE_LOC (xprt, su->su_xid);
00595   for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
00596     {
00597       if (ent->cache_xid == su->su_xid &&
00598          ent->cache_proc == uc->uc_proc &&
00599          ent->cache_vers == uc->uc_vers &&
00600          ent->cache_prog == uc->uc_prog &&
00601          EQADDR (ent->cache_addr, uc->uc_addr))
00602        {
00603          *replyp = ent->cache_reply;
00604          *replylenp = ent->cache_replylen;
00605          return 1;
00606        }
00607     }
00608   /*
00609    * Failed to find entry
00610    * Remember a few things so we can do a set later
00611    */
00612   uc->uc_proc = msg->rm_call.cb_proc;
00613   uc->uc_vers = msg->rm_call.cb_vers;
00614   uc->uc_prog = msg->rm_call.cb_prog;
00615   memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
00616   return 0;
00617 }