Back to index

glibc  2.9
clnt_tcp.c
Go to the documentation of this file.
00001 /* @(#)clnt_tcp.c    2.2 88/08/01 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[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
00032 #endif
00033 
00034 /*
00035  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
00036  *
00037  * Copyright (C) 1984, Sun Microsystems, Inc.
00038  *
00039  * TCP based RPC supports 'batched calls'.
00040  * A sequence of calls may be batched-up in a send buffer.  The rpc call
00041  * return immediately to the client even though the call was not necessarily
00042  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
00043  * the rpc timeout value is zero (see clnt.h, rpc).
00044  *
00045  * Clients should NOT casually batch calls that in fact return results; that is,
00046  * the server side should be aware that a call is batched and not produce any
00047  * return message.  Batched calls that produce many result messages can
00048  * deadlock (netlock) the client and the server....
00049  *
00050  * Now go hang yourself.
00051  */
00052 
00053 #include <netdb.h>
00054 #include <errno.h>
00055 #include <stdio.h>
00056 #include <unistd.h>
00057 #include <libintl.h>
00058 #include <rpc/rpc.h>
00059 #include <sys/poll.h>
00060 #include <sys/socket.h>
00061 #include <rpc/pmap_clnt.h>
00062 #ifdef USE_IN_LIBIO
00063 # include <wchar.h>
00064 #endif
00065 
00066 extern u_long _create_xid (void);
00067 
00068 #define MCALL_MSG_SIZE 24
00069 
00070 struct ct_data
00071   {
00072     int ct_sock;
00073     bool_t ct_closeit;
00074     struct timeval ct_wait;
00075     bool_t ct_waitset;             /* wait set by clnt_control? */
00076     struct sockaddr_in ct_addr;
00077     struct rpc_err ct_error;
00078     char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
00079     u_int ct_mpos;          /* pos after marshal */
00080     XDR ct_xdrs;
00081   };
00082 
00083 static int readtcp (char *, char *, int);
00084 static int writetcp (char *, char *, int);
00085 
00086 static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
00087                                 xdrproc_t, caddr_t, struct timeval);
00088 static void clnttcp_abort (void);
00089 static void clnttcp_geterr (CLIENT *, struct rpc_err *);
00090 static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t);
00091 static bool_t clnttcp_control (CLIENT *, int, char *);
00092 static void clnttcp_destroy (CLIENT *);
00093 
00094 static const struct clnt_ops tcp_ops =
00095 {
00096   clnttcp_call,
00097   clnttcp_abort,
00098   clnttcp_geterr,
00099   clnttcp_freeres,
00100   clnttcp_destroy,
00101   clnttcp_control
00102 };
00103 
00104 /*
00105  * Create a client handle for a tcp/ip connection.
00106  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
00107  * connected to raddr.  If *sockp non-negative then
00108  * raddr is ignored.  The rpc/tcp package does buffering
00109  * similar to stdio, so the client must pick send and receive buffer sizes,];
00110  * 0 => use the default.
00111  * If raddr->sin_port is 0, then a binder on the remote machine is
00112  * consulted for the right port number.
00113  * NB: *sockp is copied into a private area.
00114  * NB: It is the clients responsibility to close *sockp.
00115  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
00116  * something more useful.
00117  */
00118 CLIENT *
00119 clnttcp_create (struct sockaddr_in *raddr, u_long prog, u_long vers,
00120               int *sockp, u_int sendsz, u_int recvsz)
00121 {
00122   CLIENT *h;
00123   struct ct_data *ct;
00124   struct rpc_msg call_msg;
00125 
00126   h = (CLIENT *) mem_alloc (sizeof (*h));
00127   ct = (struct ct_data *) mem_alloc (sizeof (*ct));
00128   if (h == NULL || ct == NULL)
00129     {
00130       struct rpc_createerr *ce = &get_rpc_createerr ();
00131       (void) __fxprintf (NULL, "%s: %s", __func__, _("out of memory\n"));
00132       ce->cf_stat = RPC_SYSTEMERROR;
00133       ce->cf_error.re_errno = ENOMEM;
00134       goto fooy;
00135     }
00136 
00137   /*
00138    * If no port number given ask the pmap for one
00139    */
00140   if (raddr->sin_port == 0)
00141     {
00142       u_short port;
00143       if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0)
00144        {
00145          mem_free ((caddr_t) ct, sizeof (struct ct_data));
00146          mem_free ((caddr_t) h, sizeof (CLIENT));
00147          return ((CLIENT *) NULL);
00148        }
00149       raddr->sin_port = htons (port);
00150     }
00151 
00152   /*
00153    * If no socket given, open one
00154    */
00155   if (*sockp < 0)
00156     {
00157       *sockp = __socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
00158       (void) bindresvport (*sockp, (struct sockaddr_in *) 0);
00159       if ((*sockp < 0)
00160          || (__connect (*sockp, (struct sockaddr *) raddr,
00161                       sizeof (*raddr)) < 0))
00162        {
00163          struct rpc_createerr *ce = &get_rpc_createerr ();
00164          ce->cf_stat = RPC_SYSTEMERROR;
00165          ce->cf_error.re_errno = errno;
00166          if (*sockp >= 0)
00167            (void) __close (*sockp);
00168          goto fooy;
00169        }
00170       ct->ct_closeit = TRUE;
00171     }
00172   else
00173     {
00174       ct->ct_closeit = FALSE;
00175     }
00176 
00177   /*
00178    * Set up private data struct
00179    */
00180   ct->ct_sock = *sockp;
00181   ct->ct_wait.tv_usec = 0;
00182   ct->ct_waitset = FALSE;
00183   ct->ct_addr = *raddr;
00184 
00185   /*
00186    * Initialize call message
00187    */
00188   call_msg.rm_xid = _create_xid ();
00189   call_msg.rm_direction = CALL;
00190   call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
00191   call_msg.rm_call.cb_prog = prog;
00192   call_msg.rm_call.cb_vers = vers;
00193 
00194   /*
00195    * pre-serialize the static part of the call msg and stash it away
00196    */
00197   INTUSE(xdrmem_create) (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
00198                       XDR_ENCODE);
00199   if (!INTUSE(xdr_callhdr) (&(ct->ct_xdrs), &call_msg))
00200     {
00201       if (ct->ct_closeit)
00202        {
00203          (void) __close (*sockp);
00204        }
00205       goto fooy;
00206     }
00207   ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
00208   XDR_DESTROY (&(ct->ct_xdrs));
00209 
00210   /*
00211    * Create a client handle which uses xdrrec for serialization
00212    * and authnone for authentication.
00213    */
00214   INTUSE(xdrrec_create) (&(ct->ct_xdrs), sendsz, recvsz,
00215                       (caddr_t) ct, readtcp, writetcp);
00216   h->cl_ops = (struct clnt_ops *) &tcp_ops;
00217   h->cl_private = (caddr_t) ct;
00218   h->cl_auth = INTUSE(authnone_create) ();
00219   return h;
00220 
00221 fooy:
00222   /*
00223    * Something goofed, free stuff and barf
00224    */
00225   mem_free ((caddr_t) ct, sizeof (struct ct_data));
00226   mem_free ((caddr_t) h, sizeof (CLIENT));
00227   return ((CLIENT *) NULL);
00228 }
00229 INTDEF (clnttcp_create)
00230 
00231 static enum clnt_stat
00232 clnttcp_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
00233      CLIENT *h;
00234      u_long proc;
00235      xdrproc_t xdr_args;
00236      caddr_t args_ptr;
00237      xdrproc_t xdr_results;
00238      caddr_t results_ptr;
00239      struct timeval timeout;
00240 {
00241   struct ct_data *ct = (struct ct_data *) h->cl_private;
00242   XDR *xdrs = &(ct->ct_xdrs);
00243   struct rpc_msg reply_msg;
00244   u_long x_id;
00245   u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall);   /* yuk */
00246   bool_t shipnow;
00247   int refreshes = 2;
00248 
00249   if (!ct->ct_waitset)
00250     {
00251       ct->ct_wait = timeout;
00252     }
00253 
00254   shipnow =
00255     (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
00256      && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
00257 
00258 call_again:
00259   xdrs->x_op = XDR_ENCODE;
00260   ct->ct_error.re_status = RPC_SUCCESS;
00261   x_id = ntohl (--(*msg_x_id));
00262   if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
00263       (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
00264       (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
00265       (!(*xdr_args) (xdrs, args_ptr)))
00266     {
00267       if (ct->ct_error.re_status == RPC_SUCCESS)
00268        ct->ct_error.re_status = RPC_CANTENCODEARGS;
00269       (void) INTUSE(xdrrec_endofrecord) (xdrs, TRUE);
00270       return (ct->ct_error.re_status);
00271     }
00272   if (!INTUSE(xdrrec_endofrecord) (xdrs, shipnow))
00273     return ct->ct_error.re_status = RPC_CANTSEND;
00274   if (!shipnow)
00275     return RPC_SUCCESS;
00276   /*
00277    * Hack to provide rpc-based message passing
00278    */
00279   if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
00280     {
00281       return ct->ct_error.re_status = RPC_TIMEDOUT;
00282     }
00283 
00284 
00285   /*
00286    * Keep receiving until we get a valid transaction id
00287    */
00288   xdrs->x_op = XDR_DECODE;
00289   while (TRUE)
00290     {
00291       reply_msg.acpted_rply.ar_verf = _null_auth;
00292       reply_msg.acpted_rply.ar_results.where = NULL;
00293       reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)INTUSE(xdr_void);
00294       if (!INTUSE(xdrrec_skiprecord) (xdrs))
00295        return (ct->ct_error.re_status);
00296       /* now decode and validate the response header */
00297       if (!INTUSE(xdr_replymsg) (xdrs, &reply_msg))
00298        {
00299          if (ct->ct_error.re_status == RPC_SUCCESS)
00300            continue;
00301          return ct->ct_error.re_status;
00302        }
00303       if ((u_int32_t) reply_msg.rm_xid == (u_int32_t) x_id)
00304        break;
00305     }
00306 
00307   /*
00308    * process header
00309    */
00310   _seterr_reply (&reply_msg, &(ct->ct_error));
00311   if (ct->ct_error.re_status == RPC_SUCCESS)
00312     {
00313       if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
00314        {
00315          ct->ct_error.re_status = RPC_AUTHERROR;
00316          ct->ct_error.re_why = AUTH_INVALIDRESP;
00317        }
00318       else if (!(*xdr_results) (xdrs, results_ptr))
00319        {
00320          if (ct->ct_error.re_status == RPC_SUCCESS)
00321            ct->ct_error.re_status = RPC_CANTDECODERES;
00322        }
00323       /* free verifier ... */
00324       if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
00325        {
00326          xdrs->x_op = XDR_FREE;
00327          (void) INTUSE(xdr_opaque_auth) (xdrs,
00328                                      &(reply_msg.acpted_rply.ar_verf));
00329        }
00330     }                       /* end successful completion */
00331   else
00332     {
00333       /* maybe our credentials need to be refreshed ... */
00334       if (refreshes-- && AUTH_REFRESH (h->cl_auth))
00335        goto call_again;
00336     }                       /* end of unsuccessful completion */
00337   return ct->ct_error.re_status;
00338 }
00339 
00340 static void
00341 clnttcp_geterr (h, errp)
00342      CLIENT *h;
00343      struct rpc_err *errp;
00344 {
00345   struct ct_data *ct =
00346   (struct ct_data *) h->cl_private;
00347 
00348   *errp = ct->ct_error;
00349 }
00350 
00351 static bool_t
00352 clnttcp_freeres (cl, xdr_res, res_ptr)
00353      CLIENT *cl;
00354      xdrproc_t xdr_res;
00355      caddr_t res_ptr;
00356 {
00357   struct ct_data *ct = (struct ct_data *) cl->cl_private;
00358   XDR *xdrs = &(ct->ct_xdrs);
00359 
00360   xdrs->x_op = XDR_FREE;
00361   return (*xdr_res) (xdrs, res_ptr);
00362 }
00363 
00364 static void
00365 clnttcp_abort ()
00366 {
00367 }
00368 
00369 static bool_t
00370 clnttcp_control (CLIENT *cl, int request, char *info)
00371 {
00372   struct ct_data *ct = (struct ct_data *) cl->cl_private;
00373 
00374 
00375   switch (request)
00376     {
00377     case CLSET_FD_CLOSE:
00378       ct->ct_closeit = TRUE;
00379       break;
00380     case CLSET_FD_NCLOSE:
00381       ct->ct_closeit = FALSE;
00382       break;
00383     case CLSET_TIMEOUT:
00384       ct->ct_wait = *(struct timeval *) info;
00385       ct->ct_waitset = TRUE;
00386       break;
00387     case CLGET_TIMEOUT:
00388       *(struct timeval *) info = ct->ct_wait;
00389       break;
00390     case CLGET_SERVER_ADDR:
00391       *(struct sockaddr_in *) info = ct->ct_addr;
00392       break;
00393     case CLGET_FD:
00394       *(int *)info = ct->ct_sock;
00395       break;
00396     case CLGET_XID:
00397       /*
00398        * use the knowledge that xid is the
00399        * first element in the call structure *.
00400        * This will get the xid of the PREVIOUS call
00401        */
00402       *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall);
00403       break;
00404     case CLSET_XID:
00405       /* This will set the xid of the NEXT call */
00406       *(u_long *)ct->ct_mcall =  htonl (*(u_long *)info - 1);
00407       /* decrement by 1 as clnttcp_call() increments once */
00408     case CLGET_VERS:
00409       /*
00410        * This RELIES on the information that, in the call body,
00411        * the version number field is the fifth field from the
00412        * begining of the RPC header. MUST be changed if the
00413        * call_struct is changed
00414        */
00415       *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall +
00416                                       4 * BYTES_PER_XDR_UNIT));
00417       break;
00418     case CLSET_VERS:
00419       *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
00420        = htonl (*(u_long *)info);
00421       break;
00422     case CLGET_PROG:
00423       /*
00424        * This RELIES on the information that, in the call body,
00425        * the program number field is the  field from the
00426        * begining of the RPC header. MUST be changed if the
00427        * call_struct is changed
00428        */
00429       *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
00430                                      3 * BYTES_PER_XDR_UNIT));
00431       break;
00432     case CLSET_PROG:
00433       *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
00434        = htonl(*(u_long *)info);
00435       break;
00436     /* The following are only possible with TI-RPC */
00437     case CLGET_RETRY_TIMEOUT:
00438     case CLSET_RETRY_TIMEOUT:
00439     case CLGET_SVC_ADDR:
00440     case CLSET_SVC_ADDR:
00441     case CLSET_PUSH_TIMOD:
00442     case CLSET_POP_TIMOD:
00443     default:
00444       return FALSE;
00445     }
00446   return TRUE;
00447 }
00448 
00449 
00450 static void
00451 clnttcp_destroy (CLIENT *h)
00452 {
00453   struct ct_data *ct =
00454   (struct ct_data *) h->cl_private;
00455 
00456   if (ct->ct_closeit)
00457     {
00458       (void) __close (ct->ct_sock);
00459     }
00460   XDR_DESTROY (&(ct->ct_xdrs));
00461   mem_free ((caddr_t) ct, sizeof (struct ct_data));
00462   mem_free ((caddr_t) h, sizeof (CLIENT));
00463 }
00464 
00465 /*
00466  * Interface between xdr serializer and tcp connection.
00467  * Behaves like the system calls, read & write, but keeps some error state
00468  * around for the rpc level.
00469  */
00470 static int
00471 readtcp (char *ctptr, char *buf, int len)
00472 {
00473   struct ct_data *ct = (struct ct_data *)ctptr;
00474   struct pollfd fd;
00475   int milliseconds = (ct->ct_wait.tv_sec * 1000) +
00476     (ct->ct_wait.tv_usec / 1000);
00477 
00478   if (len == 0)
00479     return 0;
00480 
00481   fd.fd = ct->ct_sock;
00482   fd.events = POLLIN;
00483   while (TRUE)
00484     {
00485       switch (__poll(&fd, 1, milliseconds))
00486        {
00487        case 0:
00488          ct->ct_error.re_status = RPC_TIMEDOUT;
00489          return -1;
00490 
00491        case -1:
00492          if (errno == EINTR)
00493            continue;
00494          ct->ct_error.re_status = RPC_CANTRECV;
00495          ct->ct_error.re_errno = errno;
00496          return -1;
00497        }
00498       break;
00499     }
00500   switch (len = __read (ct->ct_sock, buf, len))
00501     {
00502 
00503     case 0:
00504       /* premature eof */
00505       ct->ct_error.re_errno = ECONNRESET;
00506       ct->ct_error.re_status = RPC_CANTRECV;
00507       len = -1;                    /* it's really an error */
00508       break;
00509 
00510     case -1:
00511       ct->ct_error.re_errno = errno;
00512       ct->ct_error.re_status = RPC_CANTRECV;
00513       break;
00514     }
00515   return len;
00516 }
00517 
00518 static int
00519 writetcp (char *ctptr, char *buf, int len)
00520 {
00521   int i, cnt;
00522   struct ct_data *ct = (struct ct_data*)ctptr;
00523 
00524   for (cnt = len; cnt > 0; cnt -= i, buf += i)
00525     {
00526       if ((i = __write (ct->ct_sock, buf, cnt)) == -1)
00527        {
00528          ct->ct_error.re_errno = errno;
00529          ct->ct_error.re_status = RPC_CANTSEND;
00530          return -1;
00531        }
00532     }
00533   return len;
00534 }