OSDN Git Service

- trim any trailing whitespace
[uclinux-h8/uClibc.git] / libc / inet / rpc / clnt_tcp.c
1 /* @(#)clnt_tcp.c       2.2 88/08/01 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[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
32 #endif
33
34 /*
35  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
36  *
37  * Copyright (C) 1984, Sun Microsystems, Inc.
38  *
39  * TCP based RPC supports 'batched calls'.
40  * A sequence of calls may be batched-up in a send buffer.  The rpc call
41  * return immediately to the client even though the call was not necessarily
42  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
43  * the rpc timeout value is zero (see clnt.h, rpc).
44  *
45  * Clients should NOT casually batch calls that in fact return results; that is,
46  * the server side should be aware that a call is batched and not produce any
47  * return message.  Batched calls that produce many result messages can
48  * deadlock (netlock) the client and the server....
49  *
50  * Now go hang yourself.
51  */
52
53 #define __FORCE_GLIBC
54 #include <features.h>
55
56 #include <netdb.h>
57 #include <errno.h>
58 #include <stdio.h>
59 #include <unistd.h>
60 #include <rpc/rpc.h>
61 #include <sys/poll.h>
62 #include <sys/socket.h>
63 #include <rpc/pmap_clnt.h>
64 #ifdef USE_IN_LIBIO
65 # include <wchar.h>
66 #endif
67
68 libc_hidden_proto(socket)
69 libc_hidden_proto(read)
70 libc_hidden_proto(write)
71 libc_hidden_proto(close)
72 libc_hidden_proto(authnone_create)
73 libc_hidden_proto(xdrrec_create)
74 libc_hidden_proto(xdrrec_endofrecord)
75 libc_hidden_proto(xdrrec_skiprecord)
76 libc_hidden_proto(xdr_callhdr)
77 libc_hidden_proto(xdr_replymsg)
78 libc_hidden_proto(xdr_opaque_auth)
79 libc_hidden_proto(xdrmem_create)
80 libc_hidden_proto(xdr_void)
81 libc_hidden_proto(pmap_getport)
82 libc_hidden_proto(_seterr_reply)
83 libc_hidden_proto(connect)
84 libc_hidden_proto(bindresvport)
85 libc_hidden_proto(poll)
86 libc_hidden_proto(fputs)
87 libc_hidden_proto(__rpc_thread_createerr)
88 #ifdef USE_IN_LIBIO
89 libc_hidden_proto(fwprintf)
90 #endif
91
92 extern u_long _create_xid (void) attribute_hidden;
93
94 #define MCALL_MSG_SIZE 24
95
96 struct ct_data
97   {
98     int ct_sock;
99     bool_t ct_closeit;
100     struct timeval ct_wait;
101     bool_t ct_waitset;          /* wait set by clnt_control? */
102     struct sockaddr_in ct_addr;
103     struct rpc_err ct_error;
104     char ct_mcall[MCALL_MSG_SIZE];      /* marshalled callmsg */
105     u_int ct_mpos;              /* pos after marshal */
106     XDR ct_xdrs;
107   };
108
109 static int readtcp (char *, char *, int);
110 static int writetcp (char *, char *, int);
111
112 static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
113                                     xdrproc_t, caddr_t, struct timeval);
114 static void clnttcp_abort (void);
115 static void clnttcp_geterr (CLIENT *, struct rpc_err *);
116 static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t);
117 static bool_t clnttcp_control (CLIENT *, int, char *);
118 static void clnttcp_destroy (CLIENT *);
119
120 static struct clnt_ops tcp_ops =
121 {
122   clnttcp_call,
123   clnttcp_abort,
124   clnttcp_geterr,
125   clnttcp_freeres,
126   clnttcp_destroy,
127   clnttcp_control
128 };
129
130 /*
131  * Create a client handle for a tcp/ip connection.
132  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
133  * connected to raddr.  If *sockp non-negative then
134  * raddr is ignored.  The rpc/tcp package does buffering
135  * similar to stdio, so the client must pick send and receive buffer sizes,];
136  * 0 => use the default.
137  * If raddr->sin_port is 0, then a binder on the remote machine is
138  * consulted for the right port number.
139  * NB: *sockp is copied into a private area.
140  * NB: It is the clients responsibility to close *sockp.
141  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
142  * something more useful.
143  */
144 libc_hidden_proto(clnttcp_create)
145 CLIENT *
146 clnttcp_create (struct sockaddr_in *raddr, u_long prog, u_long vers,
147                 int *sockp, u_int sendsz, u_int recvsz)
148 {
149   CLIENT *h;
150   struct ct_data *ct;
151   struct rpc_msg call_msg;
152
153   h = (CLIENT *) mem_alloc (sizeof (*h));
154   ct = (struct ct_data *) mem_alloc (sizeof (*ct));
155   if (h == NULL || ct == NULL)
156     {
157       struct rpc_createerr *ce = &get_rpc_createerr ();
158 #ifdef USE_IN_LIBIO
159       if (_IO_fwide (stderr, 0) > 0)
160         (void) fwprintf (stderr, L"%s",
161                            _("clnttcp_create: out of memory\n"));
162       else
163 #endif
164         (void) fputs (_("clnttcp_create: out of memory\n"), stderr);
165       ce->cf_stat = RPC_SYSTEMERROR;
166       ce->cf_error.re_errno = ENOMEM;
167       goto fooy;
168     }
169
170   /*
171    * If no port number given ask the pmap for one
172    */
173   if (raddr->sin_port == 0)
174     {
175       u_short port;
176       if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0)
177         {
178           mem_free ((caddr_t) ct, sizeof (struct ct_data));
179           mem_free ((caddr_t) h, sizeof (CLIENT));
180           return ((CLIENT *) NULL);
181         }
182       raddr->sin_port = htons (port);
183     }
184
185   /*
186    * If no socket given, open one
187    */
188   if (*sockp < 0)
189     {
190       *sockp = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
191       (void) bindresvport (*sockp, (struct sockaddr_in *) 0);
192       if ((*sockp < 0)
193           || (connect (*sockp, (struct sockaddr *) raddr,
194                          sizeof (*raddr)) < 0))
195         {
196           struct rpc_createerr *ce = &get_rpc_createerr ();
197           ce->cf_stat = RPC_SYSTEMERROR;
198           ce->cf_error.re_errno = errno;
199           if (*sockp >= 0)
200             (void) close (*sockp);
201           goto fooy;
202         }
203       ct->ct_closeit = TRUE;
204     }
205   else
206     {
207       ct->ct_closeit = FALSE;
208     }
209
210   /*
211    * Set up private data struct
212    */
213   ct->ct_sock = *sockp;
214   ct->ct_wait.tv_usec = 0;
215   ct->ct_waitset = FALSE;
216   ct->ct_addr = *raddr;
217
218   /*
219    * Initialize call message
220    */
221   call_msg.rm_xid = _create_xid ();
222   call_msg.rm_direction = CALL;
223   call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
224   call_msg.rm_call.cb_prog = prog;
225   call_msg.rm_call.cb_vers = vers;
226
227   /*
228    * pre-serialize the static part of the call msg and stash it away
229    */
230   xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
231                  XDR_ENCODE);
232   if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg))
233     {
234       if (ct->ct_closeit)
235         {
236           (void) close (*sockp);
237         }
238       goto fooy;
239     }
240   ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
241   XDR_DESTROY (&(ct->ct_xdrs));
242
243   /*
244    * Create a client handle which uses xdrrec for serialization
245    * and authnone for authentication.
246    */
247   xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz,
248                  (caddr_t) ct, readtcp, writetcp);
249   h->cl_ops = &tcp_ops;
250   h->cl_private = (caddr_t) ct;
251   h->cl_auth = authnone_create ();
252   return h;
253
254 fooy:
255   /*
256    * Something goofed, free stuff and barf
257    */
258   mem_free ((caddr_t) ct, sizeof (struct ct_data));
259   mem_free ((caddr_t) h, sizeof (CLIENT));
260   return ((CLIENT *) NULL);
261 }
262 libc_hidden_def(clnttcp_create)
263
264 static enum clnt_stat
265 clnttcp_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
266      CLIENT *h;
267      u_long proc;
268      xdrproc_t xdr_args;
269      caddr_t args_ptr;
270      xdrproc_t xdr_results;
271      caddr_t results_ptr;
272      struct timeval timeout;
273 {
274   struct ct_data *ct = (struct ct_data *) h->cl_private;
275   XDR *xdrs = &(ct->ct_xdrs);
276   struct rpc_msg reply_msg;
277   u_long x_id;
278   u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall);   /* yuk */
279   bool_t shipnow;
280   int refreshes = 2;
281
282   if (!ct->ct_waitset)
283     {
284       ct->ct_wait = timeout;
285     }
286
287   shipnow =
288     (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
289      && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
290
291 call_again:
292   xdrs->x_op = XDR_ENCODE;
293   ct->ct_error.re_status = RPC_SUCCESS;
294   x_id = ntohl (--(*msg_x_id));
295   if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
296       (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
297       (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
298       (!(*xdr_args) (xdrs, args_ptr)))
299     {
300       if (ct->ct_error.re_status == RPC_SUCCESS)
301         ct->ct_error.re_status = RPC_CANTENCODEARGS;
302       (void) xdrrec_endofrecord (xdrs, TRUE);
303       return (ct->ct_error.re_status);
304     }
305   if (!xdrrec_endofrecord (xdrs, shipnow))
306     return ct->ct_error.re_status = RPC_CANTSEND;
307   if (!shipnow)
308     return RPC_SUCCESS;
309   /*
310    * Hack to provide rpc-based message passing
311    */
312   if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
313     {
314       return ct->ct_error.re_status = RPC_TIMEDOUT;
315     }
316
317
318   /*
319    * Keep receiving until we get a valid transaction id
320    */
321   xdrs->x_op = XDR_DECODE;
322   while (TRUE)
323     {
324       reply_msg.acpted_rply.ar_verf = _null_auth;
325       reply_msg.acpted_rply.ar_results.where = NULL;
326       reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
327       if (!xdrrec_skiprecord (xdrs))
328         return (ct->ct_error.re_status);
329       /* now decode and validate the response header */
330       if (!xdr_replymsg (xdrs, &reply_msg))
331         {
332           if (ct->ct_error.re_status == RPC_SUCCESS)
333             continue;
334           return ct->ct_error.re_status;
335         }
336       if ((u_int32_t) reply_msg.rm_xid == (u_int32_t) x_id)
337         break;
338     }
339
340   /*
341    * process header
342    */
343   _seterr_reply (&reply_msg, &(ct->ct_error));
344   if (ct->ct_error.re_status == RPC_SUCCESS)
345     {
346       if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
347         {
348           ct->ct_error.re_status = RPC_AUTHERROR;
349           ct->ct_error.re_why = AUTH_INVALIDRESP;
350         }
351       else if (!(*xdr_results) (xdrs, results_ptr))
352         {
353           if (ct->ct_error.re_status == RPC_SUCCESS)
354             ct->ct_error.re_status = RPC_CANTDECODERES;
355         }
356       /* free verifier ... */
357       if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
358         {
359           xdrs->x_op = XDR_FREE;
360           (void) xdr_opaque_auth (xdrs, &(reply_msg.acpted_rply.ar_verf));
361         }
362     }                           /* end successful completion */
363   else
364     {
365       /* maybe our credentials need to be refreshed ... */
366       if (refreshes-- && AUTH_REFRESH (h->cl_auth))
367         goto call_again;
368     }                           /* end of unsuccessful completion */
369   return ct->ct_error.re_status;
370 }
371
372 static void
373 clnttcp_geterr (h, errp)
374      CLIENT *h;
375      struct rpc_err *errp;
376 {
377   struct ct_data *ct =
378   (struct ct_data *) h->cl_private;
379
380   *errp = ct->ct_error;
381 }
382
383 static bool_t
384 clnttcp_freeres (cl, xdr_res, res_ptr)
385      CLIENT *cl;
386      xdrproc_t xdr_res;
387      caddr_t res_ptr;
388 {
389   struct ct_data *ct = (struct ct_data *) cl->cl_private;
390   XDR *xdrs = &(ct->ct_xdrs);
391
392   xdrs->x_op = XDR_FREE;
393   return (*xdr_res) (xdrs, res_ptr);
394 }
395
396 static void
397 clnttcp_abort ()
398 {
399 }
400
401 static bool_t
402 clnttcp_control (CLIENT *cl, int request, char *info)
403 {
404   struct ct_data *ct = (struct ct_data *) cl->cl_private;
405
406
407   switch (request)
408     {
409     case CLSET_FD_CLOSE:
410       ct->ct_closeit = TRUE;
411       break;
412     case CLSET_FD_NCLOSE:
413       ct->ct_closeit = FALSE;
414       break;
415     case CLSET_TIMEOUT:
416       ct->ct_wait = *(struct timeval *) info;
417       ct->ct_waitset = TRUE;
418       break;
419     case CLGET_TIMEOUT:
420       *(struct timeval *) info = ct->ct_wait;
421       break;
422     case CLGET_SERVER_ADDR:
423       *(struct sockaddr_in *) info = ct->ct_addr;
424       break;
425     case CLGET_FD:
426       *(int *)info = ct->ct_sock;
427       break;
428     case CLGET_XID:
429       /*
430        * use the knowledge that xid is the
431        * first element in the call structure *.
432        * This will get the xid of the PREVIOUS call
433        */
434       *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall);
435       break;
436     case CLSET_XID:
437       /* This will set the xid of the NEXT call */
438       *(u_long *)ct->ct_mcall =  htonl (*(u_long *)info - 1);
439       /* decrement by 1 as clnttcp_call() increments once */
440     case CLGET_VERS:
441       /*
442        * This RELIES on the information that, in the call body,
443        * the version number field is the fifth field from the
444        * begining of the RPC header. MUST be changed if the
445        * call_struct is changed
446        */
447       *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall +
448                                            4 * BYTES_PER_XDR_UNIT));
449       break;
450     case CLSET_VERS:
451       *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
452         = htonl (*(u_long *)info);
453       break;
454     case CLGET_PROG:
455       /*
456        * This RELIES on the information that, in the call body,
457        * the program number field is the  field from the
458        * begining of the RPC header. MUST be changed if the
459        * call_struct is changed
460        */
461       *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
462                                           3 * BYTES_PER_XDR_UNIT));
463       break;
464     case CLSET_PROG:
465       *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
466         = htonl(*(u_long *)info);
467       break;
468     /* The following are only possible with TI-RPC */
469     case CLGET_RETRY_TIMEOUT:
470     case CLSET_RETRY_TIMEOUT:
471     case CLGET_SVC_ADDR:
472     case CLSET_SVC_ADDR:
473     case CLSET_PUSH_TIMOD:
474     case CLSET_POP_TIMOD:
475     default:
476       return FALSE;
477     }
478   return TRUE;
479 }
480
481
482 static void
483 clnttcp_destroy (CLIENT *h)
484 {
485   struct ct_data *ct =
486   (struct ct_data *) h->cl_private;
487
488   if (ct->ct_closeit)
489     {
490       (void) close (ct->ct_sock);
491     }
492   XDR_DESTROY (&(ct->ct_xdrs));
493   mem_free ((caddr_t) ct, sizeof (struct ct_data));
494   mem_free ((caddr_t) h, sizeof (CLIENT));
495 }
496
497 /*
498  * Interface between xdr serializer and tcp connection.
499  * Behaves like the system calls, read & write, but keeps some error state
500  * around for the rpc level.
501  */
502 static int
503 readtcp (char *ctptr, char *buf, int len)
504 {
505   struct ct_data *ct = (struct ct_data *)ctptr;
506   struct pollfd fd;
507   int milliseconds = (ct->ct_wait.tv_sec * 1000) +
508     (ct->ct_wait.tv_usec / 1000);
509
510   if (len == 0)
511     return 0;
512
513   fd.fd = ct->ct_sock;
514   fd.events = POLLIN;
515   while (TRUE)
516     {
517       switch (poll(&fd, 1, milliseconds))
518         {
519         case 0:
520           ct->ct_error.re_status = RPC_TIMEDOUT;
521           return -1;
522
523         case -1:
524           if (errno == EINTR)
525             continue;
526           ct->ct_error.re_status = RPC_CANTRECV;
527           ct->ct_error.re_errno = errno;
528           return -1;
529         }
530       break;
531     }
532   switch (len = read (ct->ct_sock, buf, len))
533     {
534
535     case 0:
536       /* premature eof */
537       ct->ct_error.re_errno = ECONNRESET;
538       ct->ct_error.re_status = RPC_CANTRECV;
539       len = -1;                 /* it's really an error */
540       break;
541
542     case -1:
543       ct->ct_error.re_errno = errno;
544       ct->ct_error.re_status = RPC_CANTRECV;
545       break;
546     }
547   return len;
548 }
549
550 static int
551 writetcp (char *ctptr, char *buf, int len)
552 {
553   int i, cnt;
554   struct ct_data *ct = (struct ct_data*)ctptr;
555
556   for (cnt = len; cnt > 0; cnt -= i, buf += i)
557     {
558       if ((i = write (ct->ct_sock, buf, cnt)) == -1)
559         {
560           ct->ct_error.re_errno = errno;
561           ct->ct_error.re_status = RPC_CANTSEND;
562           return -1;
563         }
564     }
565   return len;
566 }