(root)/
glibc-2.38/
sunrpc/
clnt_udp.c
       1  /*
       2   * clnt_udp.c, Implements a UDP/IP based, client side RPC.
       3   *
       4   * Copyright (c) 2010, Oracle America, Inc.
       5   *
       6   * Redistribution and use in source and binary forms, with or without
       7   * modification, are permitted provided that the following conditions are
       8   * met:
       9   *
      10   *     * Redistributions of source code must retain the above copyright
      11   *       notice, this list of conditions and the following disclaimer.
      12   *     * Redistributions in binary form must reproduce the above
      13   *       copyright notice, this list of conditions and the following
      14   *       disclaimer in the documentation and/or other materials
      15   *       provided with the distribution.
      16   *     * Neither the name of the "Oracle America, Inc." nor the names of its
      17   *       contributors may be used to endorse or promote products derived
      18   *       from this software without specific prior written permission.
      19   *
      20   *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      21   *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      22   *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      23   *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      24   *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
      25   *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      26   *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
      27   *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      28   *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
      29   *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      30   *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      31   *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      32   */
      33  
      34  #include <stdio.h>
      35  #include <unistd.h>
      36  #include <libintl.h>
      37  #include <rpc/rpc.h>
      38  #include <rpc/xdr.h>
      39  #include <rpc/clnt.h>
      40  #include <sys/poll.h>
      41  #include <sys/socket.h>
      42  #include <sys/ioctl.h>
      43  #include <netdb.h>
      44  #include <errno.h>
      45  #include <stdint.h>
      46  #include <rpc/pmap_clnt.h>
      47  #include <net/if.h>
      48  #include <ifaddrs.h>
      49  #include <wchar.h>
      50  #include <fcntl.h>
      51  
      52  #ifdef IP_RECVERR
      53  #include <errqueue.h>
      54  #include <sys/uio.h>
      55  #endif
      56  
      57  #include <kernel-features.h>
      58  #include <inet/net-internal.h>
      59  #include <shlib-compat.h>
      60  #include <libc-diag.h>
      61  
      62  extern u_long _create_xid (void);
      63  
      64  /*
      65   * UDP bases client side rpc operations
      66   */
      67  static enum clnt_stat clntudp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
      68  				    xdrproc_t, caddr_t, struct timeval);
      69  static void clntudp_abort (void);
      70  static void clntudp_geterr (CLIENT *, struct rpc_err *);
      71  static bool_t clntudp_freeres (CLIENT *, xdrproc_t, caddr_t);
      72  static bool_t clntudp_control (CLIENT *, int, char *);
      73  static void clntudp_destroy (CLIENT *);
      74  
      75  static const struct clnt_ops udp_ops =
      76  {
      77    clntudp_call,
      78    clntudp_abort,
      79    clntudp_geterr,
      80    clntudp_freeres,
      81    clntudp_destroy,
      82    clntudp_control
      83  };
      84  
      85  /*
      86   * Private data kept per client handle.  This private struct is
      87   * unfortunately part of the ABI; ypbind contains a copy of it and
      88   * accesses it through CLIENT::cl_private field.
      89   */
      90  struct cu_data
      91    {
      92      int cu_sock;
      93      bool_t cu_closeit;
      94      struct sockaddr_in cu_raddr;
      95      int cu_rlen;
      96      struct timeval cu_wait;
      97      struct timeval cu_total;
      98      struct rpc_err cu_error;
      99      XDR cu_outxdrs;
     100      u_int cu_xdrpos;
     101      u_int cu_sendsz;
     102      char *cu_outbuf;
     103      u_int cu_recvsz;
     104      char cu_inbuf[1];
     105    };
     106  
     107  /*
     108   * Create a UDP based client handle.
     109   * If *sockp<0, *sockp is set to a newly created UPD socket.
     110   * If raddr->sin_port is 0 a binder on the remote machine
     111   * is consulted for the correct port number.
     112   * NB: It is the clients responsibility to close *sockp.
     113   * NB: The rpch->cl_auth is initialized to null authentication.
     114   *     Caller may wish to set this something more useful.
     115   *
     116   * wait is the amount of time used between retransmitting a call if
     117   * no response has been heard; retransmission occurs until the actual
     118   * rpc call times out.
     119   *
     120   * sendsz and recvsz are the maximum allowable packet sizes that can be
     121   * sent and received.
     122   */
     123  CLIENT *
     124  __libc_clntudp_bufcreate (struct sockaddr_in *raddr, u_long program,
     125  			  u_long version, struct timeval wait, int *sockp,
     126  			  u_int sendsz, u_int recvsz, int flags)
     127  {
     128    CLIENT *cl;
     129    struct cu_data *cu = NULL;
     130    struct rpc_msg call_msg;
     131  
     132    cl = (CLIENT *) mem_alloc (sizeof (CLIENT));
     133    sendsz = ((sendsz + 3) / 4) * 4;
     134    recvsz = ((recvsz + 3) / 4) * 4;
     135    cu = (struct cu_data *) mem_alloc (sizeof (*cu) + sendsz + recvsz);
     136    if (cl == NULL || cu == NULL)
     137      {
     138        struct rpc_createerr *ce = &get_rpc_createerr ();
     139        (void) __fxprintf (NULL, "%s: %s",
     140  			 "clntudp_create", _("out of memory\n"));
     141        ce->cf_stat = RPC_SYSTEMERROR;
     142        ce->cf_error.re_errno = ENOMEM;
     143        goto fooy;
     144      }
     145    cu->cu_outbuf = &cu->cu_inbuf[recvsz];
     146  
     147    if (raddr->sin_port == 0)
     148      {
     149        u_short port;
     150        if ((port =
     151  	   pmap_getport (raddr, program, version, IPPROTO_UDP)) == 0)
     152  	{
     153  	  goto fooy;
     154  	}
     155        raddr->sin_port = htons (port);
     156      }
     157    cl->cl_ops = (struct clnt_ops *) &udp_ops;
     158    cl->cl_private = (caddr_t) cu;
     159    cu->cu_raddr = *raddr;
     160    cu->cu_rlen = sizeof (cu->cu_raddr);
     161    cu->cu_wait = wait;
     162    cu->cu_total.tv_sec = -1;
     163    cu->cu_total.tv_usec = -1;
     164    cu->cu_sendsz = sendsz;
     165    cu->cu_recvsz = recvsz;
     166    call_msg.rm_xid = _create_xid ();
     167    call_msg.rm_direction = CALL;
     168    call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
     169    call_msg.rm_call.cb_prog = program;
     170    call_msg.rm_call.cb_vers = version;
     171    xdrmem_create (&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
     172    if (!xdr_callhdr (&(cu->cu_outxdrs), &call_msg))
     173      {
     174        goto fooy;
     175      }
     176    cu->cu_xdrpos = XDR_GETPOS (&(cu->cu_outxdrs));
     177    if (*sockp < 0)
     178      {
     179        *sockp = __socket (AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|flags, IPPROTO_UDP);
     180        if (__glibc_unlikely (*sockp < 0))
     181  	{
     182  	  struct rpc_createerr *ce = &get_rpc_createerr ();
     183  	  ce->cf_stat = RPC_SYSTEMERROR;
     184  	  ce->cf_error.re_errno = errno;
     185  	  goto fooy;
     186  	}
     187        /* attempt to bind to prov port */
     188        (void) bindresvport (*sockp, (struct sockaddr_in *) 0);
     189  #ifdef IP_RECVERR
     190        {
     191  	int on = 1;
     192  	__setsockopt (*sockp, SOL_IP, IP_RECVERR, &on, sizeof(on));
     193        }
     194  #endif
     195        cu->cu_closeit = TRUE;
     196      }
     197    else
     198      {
     199        cu->cu_closeit = FALSE;
     200      }
     201    cu->cu_sock = *sockp;
     202    cl->cl_auth = authnone_create ();
     203    return cl;
     204  fooy:
     205    if (cu)
     206      mem_free ((caddr_t) cu, sizeof (*cu) + sendsz + recvsz);
     207    if (cl)
     208      mem_free ((caddr_t) cl, sizeof (CLIENT));
     209    return (CLIENT *) NULL;
     210  }
     211  #ifdef EXPORT_RPC_SYMBOLS
     212  libc_hidden_def (__libc_clntudp_bufcreate)
     213  #else
     214  libc_hidden_nolink_sunrpc (__libc_clntudp_bufcreate, GLIBC_PRIVATE)
     215  #endif
     216  
     217  CLIENT *
     218  clntudp_bufcreate (struct sockaddr_in *raddr, u_long program, u_long version,
     219  		   struct timeval wait, int *sockp, u_int sendsz,
     220  		   u_int recvsz)
     221  {
     222    return __libc_clntudp_bufcreate (raddr, program, version, wait,
     223  				   sockp, sendsz, recvsz, 0);
     224  }
     225  libc_hidden_nolink_sunrpc (clntudp_bufcreate, GLIBC_2_0)
     226  
     227  CLIENT *
     228  clntudp_create (struct sockaddr_in *raddr, u_long program, u_long version,
     229  		struct timeval wait, int *sockp)
     230  {
     231    return __libc_clntudp_bufcreate (raddr, program, version, wait,
     232  				   sockp, UDPMSGSIZE, UDPMSGSIZE, 0);
     233  }
     234  #ifdef EXPORT_RPC_SYMBOLS
     235  libc_hidden_def (clntudp_create)
     236  #else
     237  libc_hidden_nolink_sunrpc (clntudp_create, GLIBC_2_0)
     238  #endif
     239  
     240  static int
     241  is_network_up (int sock)
     242  {
     243    struct ifaddrs *ifa;
     244  
     245    if (getifaddrs (&ifa) != 0)
     246      return 0;
     247  
     248    struct ifaddrs *run = ifa;
     249    while (run != NULL)
     250      {
     251        if ((run->ifa_flags & IFF_UP) != 0
     252  	  && run->ifa_addr != NULL
     253  	  && run->ifa_addr->sa_family == AF_INET)
     254  	break;
     255  
     256        run = run->ifa_next;
     257      }
     258  
     259    freeifaddrs (ifa);
     260  
     261    return run != NULL;
     262  }
     263  
     264  static enum clnt_stat
     265  clntudp_call (/* client handle */
     266  	      CLIENT *cl,
     267  	      /* procedure number */
     268  	      u_long proc,
     269  	      /* xdr routine for args */
     270  	      xdrproc_t xargs,
     271  	      /* pointer to args */
     272  	      caddr_t argsp,
     273  	      /* xdr routine for results */
     274  	      xdrproc_t xresults,
     275  	      /* pointer to results */
     276  	      caddr_t resultsp,
     277  	      /* seconds to wait before giving up */
     278  	      struct timeval utimeout)
     279  {
     280    struct cu_data *cu = (struct cu_data *) cl->cl_private;
     281    XDR *xdrs;
     282    int outlen = 0;
     283    int inlen;
     284    socklen_t fromlen;
     285    struct pollfd fd;
     286    struct sockaddr_in from;
     287    struct rpc_msg reply_msg;
     288    XDR reply_xdrs;
     289    bool_t ok;
     290    int nrefreshes = 2;		/* number of times to refresh cred */
     291    int anyup;			/* any network interface up */
     292  
     293    struct deadline_current_time current_time = __deadline_current_time ();
     294    /* GCC 10 for MIPS reports total_deadline as possibly used
     295       uninitialized; see
     296       <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91691>.  In fact it
     297       is initialized conditionally and only ever used under the same
     298       condition.  The same warning is also disabled in
     299       inet/net-internal.h because in some other configurations GCC
     300       gives the warning in an inline function.  */
     301    DIAG_PUSH_NEEDS_COMMENT;
     302    DIAG_IGNORE_NEEDS_COMMENT (10, "-Wmaybe-uninitialized");
     303    struct deadline total_deadline; /* Determined once by overall timeout.  */
     304    DIAG_POP_NEEDS_COMMENT;
     305    struct deadline response_deadline; /* Determined anew for each query.  */
     306  
     307    /* Choose the timeout value.  For non-sending usage (xargs == NULL),
     308       the total deadline does not matter, only cu->cu_wait is used
     309       below.  */
     310    if (xargs != NULL)
     311      {
     312        struct timeval tv;
     313        if (cu->cu_total.tv_usec == -1)
     314  	/* Use supplied timeout.  */
     315  	tv = utimeout;
     316        else
     317  	/* Use default timeout.  */
     318  	tv = cu->cu_total;
     319        if (!__is_timeval_valid_timeout (tv))
     320  	return (cu->cu_error.re_status = RPC_TIMEDOUT);
     321        total_deadline = __deadline_from_timeval (current_time, tv);
     322      }
     323  
     324    /* Guard against bad timeout specification.  */
     325    if (!__is_timeval_valid_timeout (cu->cu_wait))
     326      return (cu->cu_error.re_status = RPC_TIMEDOUT);
     327  
     328  call_again:
     329    xdrs = &(cu->cu_outxdrs);
     330    if (xargs == NULL)
     331      goto get_reply;
     332    xdrs->x_op = XDR_ENCODE;
     333    XDR_SETPOS (xdrs, cu->cu_xdrpos);
     334    /*
     335     * the transaction is the first thing in the out buffer
     336     */
     337    (*(uint32_t *) (cu->cu_outbuf))++;
     338    if ((!XDR_PUTLONG (xdrs, (long *) &proc)) ||
     339        (!AUTH_MARSHALL (cl->cl_auth, xdrs)) ||
     340        (!(*xargs) (xdrs, argsp)))
     341      return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
     342    outlen = (int) XDR_GETPOS (xdrs);
     343  
     344  send_again:
     345    if (__sendto (cu->cu_sock, cu->cu_outbuf, outlen, 0,
     346  		(struct sockaddr *) &(cu->cu_raddr), cu->cu_rlen)
     347        != outlen)
     348      {
     349        cu->cu_error.re_errno = errno;
     350        return (cu->cu_error.re_status = RPC_CANTSEND);
     351      }
     352  
     353    /* sendto may have blocked, so recompute the current time.  */
     354    current_time = __deadline_current_time ();
     355   get_reply:
     356    response_deadline = __deadline_from_timeval (current_time, cu->cu_wait);
     357  
     358    reply_msg.acpted_rply.ar_verf = _null_auth;
     359    reply_msg.acpted_rply.ar_results.where = resultsp;
     360    reply_msg.acpted_rply.ar_results.proc = xresults;
     361    fd.fd = cu->cu_sock;
     362    fd.events = POLLIN;
     363    anyup = 0;
     364  
     365    /* Per-response retry loop.  current_time must be up-to-date at the
     366       top of the loop.  */
     367    for (;;)
     368      {
     369        int milliseconds;
     370        if (xargs != NULL)
     371  	{
     372  	  if (__deadline_elapsed (current_time, total_deadline))
     373  	    /* Overall timeout expired.  */
     374  	    return (cu->cu_error.re_status = RPC_TIMEDOUT);
     375  	  milliseconds = __deadline_to_ms
     376  	    (current_time, __deadline_first (total_deadline,
     377  					     response_deadline));
     378  	  if (milliseconds == 0)
     379  	    /* Per-query timeout expired.  */
     380  	    goto send_again;
     381  	}
     382        else
     383  	{
     384  	  /* xatgs == NULL.  Collect a response without sending a
     385  	     query.  In this mode, we need to ignore the total
     386  	     deadline.  */
     387  	  milliseconds = __deadline_to_ms (current_time, response_deadline);
     388  	  if (milliseconds == 0)
     389  	    /* Cannot send again, so bail out.  */
     390  	    return (cu->cu_error.re_status = RPC_CANTSEND);
     391  	}
     392  
     393        switch (__poll (&fd, 1, milliseconds))
     394  	{
     395  
     396  	case 0:
     397  	  if (anyup == 0)
     398  	    {
     399  	      anyup = is_network_up (cu->cu_sock);
     400  	      if (!anyup)
     401  		return (cu->cu_error.re_status = RPC_CANTRECV);
     402  	    }
     403  	  goto next_response;
     404  	case -1:
     405  	  if (errno == EINTR)
     406  	    goto next_response;
     407  	  cu->cu_error.re_errno = errno;
     408  	  return (cu->cu_error.re_status = RPC_CANTRECV);
     409  	}
     410  #ifdef IP_RECVERR
     411        if (fd.revents & POLLERR)
     412  	{
     413  	  struct msghdr msg;
     414  	  struct cmsghdr *cmsg;
     415  	  struct sock_extended_err *e;
     416  	  struct sockaddr_in err_addr;
     417  	  struct iovec iov;
     418  	  char *cbuf = malloc (outlen + 256);
     419  	  int ret;
     420  
     421  	  if (cbuf == NULL)
     422  	    {
     423  	      cu->cu_error.re_errno = errno;
     424  	      return (cu->cu_error.re_status = RPC_CANTRECV);
     425  	    }
     426  
     427  	  iov.iov_base = cbuf + 256;
     428  	  iov.iov_len = outlen;
     429  	  msg.msg_name = (void *) &err_addr;
     430  	  msg.msg_namelen = sizeof (err_addr);
     431  	  msg.msg_iov = &iov;
     432  	  msg.msg_iovlen = 1;
     433  	  msg.msg_flags = 0;
     434  	  msg.msg_control = cbuf;
     435  	  msg.msg_controllen = 256;
     436  	  ret = __recvmsg (cu->cu_sock, &msg, MSG_ERRQUEUE);
     437  	  if (ret >= 0
     438  	      && memcmp (cbuf + 256, cu->cu_outbuf, ret) == 0
     439  	      && (msg.msg_flags & MSG_ERRQUEUE)
     440  	      && ((msg.msg_namelen == 0
     441  		   && ret >= 12)
     442  		  || (msg.msg_namelen == sizeof (err_addr)
     443  		      && err_addr.sin_family == AF_INET
     444  		      && memcmp (&err_addr.sin_addr, &cu->cu_raddr.sin_addr,
     445  				 sizeof (err_addr.sin_addr)) == 0
     446  		      && err_addr.sin_port == cu->cu_raddr.sin_port)))
     447  	    for (cmsg = CMSG_FIRSTHDR (&msg); cmsg;
     448  		 cmsg = CMSG_NXTHDR (&msg, cmsg))
     449  	      if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
     450  		{
     451  		  e = (struct sock_extended_err *) CMSG_DATA(cmsg);
     452  		  cu->cu_error.re_errno = e->ee_errno;
     453  		  free (cbuf);
     454  		  return (cu->cu_error.re_status = RPC_CANTRECV);
     455  		}
     456  	  free (cbuf);
     457  	}
     458  #endif
     459        do
     460  	{
     461  	  fromlen = sizeof (struct sockaddr);
     462  	  inlen = __recvfrom (cu->cu_sock, cu->cu_inbuf,
     463  			      (int) cu->cu_recvsz, MSG_DONTWAIT,
     464  			      (struct sockaddr *) &from, &fromlen);
     465  	}
     466        while (inlen < 0 && errno == EINTR);
     467        if (inlen < 0)
     468  	{
     469  	  if (errno == EWOULDBLOCK)
     470  	    goto next_response;
     471  	  cu->cu_error.re_errno = errno;
     472  	  return (cu->cu_error.re_status = RPC_CANTRECV);
     473  	}
     474        /* Accept the response if the packet is sufficiently long and
     475  	 the transaction ID matches the query (if available).  */
     476        if (inlen >= 4
     477  	  && (xargs == NULL
     478  	      || memcmp (cu->cu_inbuf, cu->cu_outbuf,
     479  			 sizeof (uint32_t)) == 0))
     480  	break;
     481  
     482      next_response:
     483        /* Update the current time because poll and recvmsg waited for
     484  	 an unknown time.  */
     485        current_time = __deadline_current_time ();
     486      }
     487  
     488    /*
     489     * now decode and validate the response
     490     */
     491    xdrmem_create (&reply_xdrs, cu->cu_inbuf, (u_int) inlen, XDR_DECODE);
     492    ok = xdr_replymsg (&reply_xdrs, &reply_msg);
     493    /* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
     494    if (ok)
     495      {
     496        _seterr_reply (&reply_msg, &(cu->cu_error));
     497        if (cu->cu_error.re_status == RPC_SUCCESS)
     498  	{
     499  	  if (!AUTH_VALIDATE (cl->cl_auth,
     500  			      &reply_msg.acpted_rply.ar_verf))
     501  	    {
     502  	      cu->cu_error.re_status = RPC_AUTHERROR;
     503  	      cu->cu_error.re_why = AUTH_INVALIDRESP;
     504  	    }
     505  	  if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
     506  	    {
     507  	      xdrs->x_op = XDR_FREE;
     508  	      (void) xdr_opaque_auth (xdrs, &(reply_msg.acpted_rply.ar_verf));
     509  	    }
     510  	}			/* end successful completion */
     511        else
     512  	{
     513  	  /* maybe our credentials need to be refreshed ... */
     514  	  if (nrefreshes > 0 && AUTH_REFRESH (cl->cl_auth))
     515  	    {
     516  	      nrefreshes--;
     517  	      goto call_again;
     518  	    }
     519  	}			/* end of unsuccessful completion */
     520      }				/* end of valid reply message */
     521    else
     522      {
     523        cu->cu_error.re_status = RPC_CANTDECODERES;
     524      }
     525    return cu->cu_error.re_status;
     526  }
     527  
     528  static void
     529  clntudp_geterr (CLIENT *cl, struct rpc_err *errp)
     530  {
     531    struct cu_data *cu = (struct cu_data *) cl->cl_private;
     532  
     533    *errp = cu->cu_error;
     534  }
     535  
     536  
     537  static bool_t
     538  clntudp_freeres (CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
     539  {
     540    struct cu_data *cu = (struct cu_data *) cl->cl_private;
     541    XDR *xdrs = &(cu->cu_outxdrs);
     542  
     543    xdrs->x_op = XDR_FREE;
     544    return (*xdr_res) (xdrs, res_ptr);
     545  }
     546  
     547  static void
     548  clntudp_abort (void)
     549  {
     550  }
     551  
     552  static bool_t
     553  clntudp_control (CLIENT *cl, int request, char *info)
     554  {
     555    struct cu_data *cu = (struct cu_data *) cl->cl_private;
     556    u_long ul;
     557    uint32_t ui32;
     558  
     559    switch (request)
     560      {
     561      case CLSET_FD_CLOSE:
     562        cu->cu_closeit = TRUE;
     563        break;
     564      case CLSET_FD_NCLOSE:
     565        cu->cu_closeit = FALSE;
     566        break;
     567      case CLSET_TIMEOUT:
     568        cu->cu_total = *(struct timeval *) info;
     569        break;
     570      case CLGET_TIMEOUT:
     571        *(struct timeval *) info = cu->cu_total;
     572        break;
     573      case CLSET_RETRY_TIMEOUT:
     574        cu->cu_wait = *(struct timeval *) info;
     575        break;
     576      case CLGET_RETRY_TIMEOUT:
     577        *(struct timeval *) info = cu->cu_wait;
     578        break;
     579      case CLGET_SERVER_ADDR:
     580        *(struct sockaddr_in *) info = cu->cu_raddr;
     581        break;
     582      case CLGET_FD:
     583        *(int *)info = cu->cu_sock;
     584        break;
     585      case CLGET_XID:
     586        /*
     587         * use the knowledge that xid is the
     588         * first element in the call structure *.
     589         * This will get the xid of the PREVIOUS call
     590         */
     591        memcpy (&ui32, cu->cu_outbuf, sizeof (ui32));
     592        ul = ntohl (ui32);
     593        memcpy (info, &ul, sizeof (ul));
     594        break;
     595      case CLSET_XID:
     596        /* This will set the xid of the NEXT call */
     597        memcpy (&ul, info, sizeof (ul));
     598        ui32 = htonl (ul - 1);
     599        memcpy (cu->cu_outbuf, &ui32, sizeof (ui32));
     600        /* decrement by 1 as clntudp_call() increments once */
     601        break;
     602      case CLGET_VERS:
     603        /*
     604         * This RELIES on the information that, in the call body,
     605         * the version number field is the fifth field from the
     606         * beginning of the RPC header. MUST be changed if the
     607         * call_struct is changed
     608         */
     609        memcpy (&ui32, cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT, sizeof (ui32));
     610        ul = ntohl (ui32);
     611        memcpy (info, &ul, sizeof (ul));
     612        break;
     613      case CLSET_VERS:
     614        memcpy (&ul, info, sizeof (ul));
     615        ui32 = htonl (ul);
     616        memcpy (cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT, &ui32, sizeof (ui32));
     617        break;
     618      case CLGET_PROG:
     619        /*
     620         * This RELIES on the information that, in the call body,
     621         * the program number field is the  field from the
     622         * beginning of the RPC header. MUST be changed if the
     623         * call_struct is changed
     624         */
     625        memcpy (&ui32, cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT, sizeof (ui32));
     626        ul = ntohl (ui32);
     627        memcpy (info, &ul, sizeof (ul));
     628        break;
     629      case CLSET_PROG:
     630        memcpy (&ul, info, sizeof (ul));
     631        ui32 = htonl (ul);
     632        memcpy (cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT, &ui32, sizeof (ui32));
     633        break;
     634      /* The following are only possible with TI-RPC */
     635      case CLGET_SVC_ADDR:
     636      case CLSET_SVC_ADDR:
     637      case CLSET_PUSH_TIMOD:
     638      case CLSET_POP_TIMOD:
     639      default:
     640        return FALSE;
     641      }
     642    return TRUE;
     643  }
     644  
     645  static void
     646  clntudp_destroy (CLIENT *cl)
     647  {
     648    struct cu_data *cu = (struct cu_data *) cl->cl_private;
     649  
     650    if (cu->cu_closeit)
     651      {
     652        (void) __close (cu->cu_sock);
     653      }
     654    XDR_DESTROY (&(cu->cu_outxdrs));
     655    mem_free ((caddr_t) cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
     656    mem_free ((caddr_t) cl, sizeof (CLIENT));
     657  }