a68a6570a7bfc097eb2d59e8cb598b11fc0860c3
[shibboleth/cpp-sp.git] / oncrpc / clnt_tcp.c
1 /*********************************************************************
2  * RPC for the Windows NT Operating System
3  * 1993 by Martin F. Gergeleit
4  * Users may use, copy or modify Sun RPC for the Windows NT Operating 
5  * System according to the Sun copyright below.
6  *
7  * RPC for the Windows NT Operating System COMES WITH ABSOLUTELY NO 
8  * WARRANTY, NOR WILL I BE LIABLE FOR ANY DAMAGES INCURRED FROM THE 
9  * USE OF. USE ENTIRELY AT YOUR OWN RISK!!!
10  *********************************************************************/
11
12 /* @(#)clnt_tcp.c       2.2 88/08/01 4.0 RPCSRC */
13 /*
14  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
15  * unrestricted use provided that this legend is included on all tape
16  * media and as a part of the software program in whole or part.  Users
17  * may copy or modify Sun RPC without charge, but are not authorized
18  * to license or distribute it to anyone else except as part of a product or
19  * program developed by the user.
20  *
21  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
22  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
24  *
25  * Sun RPC is provided with no support and without any obligation on the
26  * part of Sun Microsystems, Inc. to assist in its use, correction,
27  * modification or enhancement.
28  *
29  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
30  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
31  * OR ANY PART THEREOF.
32  *
33  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
34  * or profits or other special, indirect and consequential damages, even if
35  * Sun has been advised of the possibility of such damages.
36  *
37  * Sun Microsystems, Inc.
38  * 2550 Garcia Avenue
39  * Mountain View, California  94043
40  */
41 #if !defined(lint) && defined(SCCSIDS)
42 static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
43 #endif
44
45 /*
46  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
47  *
48  * Copyright (C) 1984, Sun Microsystems, Inc.
49  *
50  * TCP based RPC supports 'batched calls'.
51  * A sequence of calls may be batched-up in a send buffer.  The rpc call
52  * return immediately to the client even though the call was not necessarily
53  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
54  * the rpc timeout value is zero (see clnt.h, rpc).
55  *
56  * Clients should NOT casually batch calls that in fact return results; that is,
57  * the server side should be aware that a call is batched and not produce any
58  * return message.  Batched calls that produce many result messages can
59  * deadlock (netlock) the client and the server....
60  *
61  * Now go hang yourself.
62  */
63
64 #include <stdio.h>
65 #include <rpc/rpc.h>
66 #ifdef WIN32
67 #include <errno.h>
68 #include <rpc/pmap_clnt.h>
69 #else
70 #include <sys/socket.h>
71 #include <netdb.h>
72 #include <errno.h>
73 #include <rpc/pmap_clnt.h>
74 #endif
75
76 #define MCALL_MSG_SIZE 24
77
78 #ifndef WIN32
79 extern int errno;
80 #endif
81
82 static int      readtcp();
83 static int      writetcp();
84
85 static enum clnt_stat   clnttcp_call();
86 static void             clnttcp_abort();
87 static void             clnttcp_geterr();
88 static bool_t           clnttcp_freeres();
89 static bool_t           clnttcp_control();
90 static void             clnttcp_destroy();
91
92 static struct clnt_ops tcp_ops = {
93         clnttcp_call,
94         clnttcp_abort,
95         clnttcp_geterr,
96         clnttcp_freeres,
97         clnttcp_destroy,
98         clnttcp_control
99 };
100
101 struct ct_data {
102         int             ct_sock;
103         bool_t          ct_closeit;
104         struct timeval  ct_wait;
105         bool_t          ct_waitset;       /* wait set by clnt_control? */
106         struct sockaddr_in ct_addr;
107         struct rpc_err  ct_error;
108         char            ct_mcall[MCALL_MSG_SIZE];       /* marshalled callmsg */
109         u_int           ct_mpos;                        /* pos after marshal */
110         XDR             ct_xdrs;
111 };
112
113 /*
114  * Create a client handle for a tcp/ip connection.
115  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
116  * connected to raddr.  If *sockp non-negative then
117  * raddr is ignored.  The rpc/tcp package does buffering
118  * similar to stdio, so the client must pick send and receive buffer sizes,];
119  * 0 => use the default.
120  * If raddr->sin_port is 0, then a binder on the remote machine is
121  * consulted for the right port number.
122  * NB: *sockp is copied into a private area.
123  * NB: It is the clients responsibility to close *sockp.
124  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
125  * something more useful.
126  */
127 CLIENT *
128 clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
129         struct sockaddr_in *raddr;
130         u_long prog;
131         u_long vers;
132         register int *sockp;
133         u_int sendsz;
134         u_int recvsz;
135 {
136         CLIENT *h;
137         register struct ct_data *ct;
138         struct timeval now;
139         struct rpc_msg call_msg;
140
141         h  = (CLIENT *)mem_alloc(sizeof(*h));
142         if (h == NULL) {
143 #ifdef WIN32
144                 nt_rpc_report("clnttcp_create: out of memory\n");
145                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
146                 rpc_createerr.cf_error.re_errno = ENOMEM;
147 #else
148                 (void)fprintf(stderr, "clnttcp_create: out of memory\n");
149                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
150                 rpc_createerr.cf_error.re_errno = errno;
151 #endif
152                 goto fooy;
153         }
154         ct = (struct ct_data *)mem_alloc(sizeof(*ct));
155         if (ct == NULL) {
156 #ifdef WIN32
157                 nt_rpc_report("clnttcp_create: out of memory\n");
158                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
159                 rpc_createerr.cf_error.re_errno = ENOMEM;
160 #else
161                 (void)fprintf(stderr, "clnttcp_create: out of memory\n");
162                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
163                 rpc_createerr.cf_error.re_errno = errno;
164 #endif
165                 goto fooy;
166         }
167
168         /*
169          * If no port number given ask the pmap for one
170          */
171         if (raddr->sin_port == 0) {
172                 u_short port;
173                 if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
174                         mem_free((caddr_t)ct, sizeof(struct ct_data));
175                         mem_free((caddr_t)h, sizeof(CLIENT));
176                         return ((CLIENT *)NULL);
177                 }
178                 raddr->sin_port = htons(port);
179         }
180
181         /*
182          * If no socket given, open one
183          */
184 #ifdef WIN32
185         if (*sockp == INVALID_SOCKET) {
186                 struct linger slinger;
187                 
188                 *sockp = socket(AF_INET, SOCK_STREAM, 0);
189                 bindresvport(*sockp, (struct sockaddr_in *)0);
190
191                 slinger.l_onoff = 1;
192                 slinger.l_linger = 0;
193                 setsockopt(*sockp, SOL_SOCKET, SO_LINGER, &slinger, sizeof(struct linger));
194
195                 if ((*sockp == INVALID_SOCKET)
196                     || (connect(*sockp, (struct sockaddr *)raddr,
197                     sizeof(*raddr)) < 0)) {
198                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
199                         rpc_createerr.cf_error.re_errno = WSAerrno;
200                         (void)closesocket(*sockp);
201 #else
202         if (*sockp < 0) {
203                 *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
204                 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
205                 if ((*sockp < 0)
206                     || (connect(*sockp, (struct sockaddr *)raddr,
207                     sizeof(*raddr)) < 0)) {
208                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
209                         rpc_createerr.cf_error.re_errno = errno;
210                         (void)close(*sockp);
211 #endif
212                         goto fooy;
213                 }
214                 ct->ct_closeit = TRUE;
215         } else {
216                 ct->ct_closeit = FALSE;
217         }
218
219         /*
220          * Set up private data struct
221          */
222         ct->ct_sock = *sockp;
223         ct->ct_wait.tv_usec = 0;
224         ct->ct_waitset = FALSE;
225         ct->ct_addr = *raddr;
226
227         /*
228          * Initialize call message
229          */
230         (void)gettimeofday(&now, (struct timezone *)0);
231         call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
232         call_msg.rm_direction = CALL;
233         call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
234         call_msg.rm_call.cb_prog = prog;
235         call_msg.rm_call.cb_vers = vers;
236
237         /*
238          * pre-serialize the staic part of the call msg and stash it away
239          */
240         xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
241             XDR_ENCODE);
242         if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
243                 if (ct->ct_closeit) {
244 #ifdef WIN32
245                         (void)closesocket(*sockp);
246 #else
247                         (void)close(*sockp);
248 #endif
249                 }
250                 goto fooy;
251         }
252         ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
253         XDR_DESTROY(&(ct->ct_xdrs));
254
255         /*
256          * Create a client handle which uses xdrrec for serialization
257          * and authnone for authentication.
258          */
259         xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
260             (caddr_t)ct, readtcp, writetcp);
261         h->cl_ops = &tcp_ops;
262         h->cl_private = (caddr_t) ct;
263         h->cl_auth = authnone_create();
264         return (h);
265
266 fooy:
267         /*
268          * Something goofed, free stuff and barf
269          */
270         mem_free((caddr_t)ct, sizeof(struct ct_data));
271         mem_free((caddr_t)h, sizeof(CLIENT));
272         return ((CLIENT *)NULL);
273 }
274
275 static enum clnt_stat
276 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
277         register CLIENT *h;
278         u_long proc;
279         xdrproc_t xdr_args;
280         caddr_t args_ptr;
281         xdrproc_t xdr_results;
282         caddr_t results_ptr;
283         struct timeval timeout;
284 {
285         register struct ct_data *ct = (struct ct_data *) h->cl_private;
286         register XDR *xdrs = &(ct->ct_xdrs);
287         struct rpc_msg reply_msg;
288         u_long x_id;
289         u_long *msg_x_id = (u_long *)(ct->ct_mcall);    /* yuk */
290         register bool_t shipnow;
291         int refreshes = 2;
292
293         if (!ct->ct_waitset) {
294                 ct->ct_wait = timeout;
295         }
296
297         shipnow =
298             (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
299             && timeout.tv_usec == 0) ? FALSE : TRUE;
300
301 call_again:
302         xdrs->x_op = XDR_ENCODE;
303         ct->ct_error.re_status = RPC_SUCCESS;
304         x_id = ntohl(--(*msg_x_id));
305         if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
306             (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
307             (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
308             (! (*xdr_args)(xdrs, args_ptr))) {
309                 if (ct->ct_error.re_status == RPC_SUCCESS)
310                         ct->ct_error.re_status = RPC_CANTENCODEARGS;
311                 (void)xdrrec_endofrecord(xdrs, TRUE);
312                 return (ct->ct_error.re_status);
313         }
314         if (! xdrrec_endofrecord(xdrs, shipnow))
315                 return (ct->ct_error.re_status = RPC_CANTSEND);
316         if (! shipnow)
317                 return (RPC_SUCCESS);
318         /*
319          * Hack to provide rpc-based message passing
320          */
321         if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
322                 return(ct->ct_error.re_status = RPC_TIMEDOUT);
323         }
324
325
326         /*
327          * Keep receiving until we get a valid transaction id
328          */
329         xdrs->x_op = XDR_DECODE;
330         while (TRUE) {
331                 reply_msg.acpted_rply.ar_verf = _null_auth;
332                 reply_msg.acpted_rply.ar_results.where = NULL;
333                 reply_msg.acpted_rply.ar_results.proc = xdr_void;
334                 if (! xdrrec_skiprecord(xdrs))
335                         return (ct->ct_error.re_status);
336                 /* now decode and validate the response header */
337                 if (! xdr_replymsg(xdrs, &reply_msg)) {
338                         if (ct->ct_error.re_status == RPC_SUCCESS)
339                                 continue;
340                         return (ct->ct_error.re_status);
341                 }
342                 if (reply_msg.rm_xid == x_id)
343                         break;
344         }
345
346         /*
347          * process header
348          */
349         _seterr_reply(&reply_msg, &(ct->ct_error));
350         if (ct->ct_error.re_status == RPC_SUCCESS) {
351                 if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
352                         ct->ct_error.re_status = RPC_AUTHERROR;
353                         ct->ct_error.re_why = AUTH_INVALIDRESP;
354                 } else if (! (*xdr_results)(xdrs, results_ptr)) {
355                         if (ct->ct_error.re_status == RPC_SUCCESS)
356                                 ct->ct_error.re_status = RPC_CANTDECODERES;
357                 }
358                 /* free verifier ... */
359                 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
360                         xdrs->x_op = XDR_FREE;
361                         (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
362                 }
363         }  /* end successful completion */
364         else {
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         register 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         register struct ct_data *ct = (struct ct_data *)cl->cl_private;
390         register 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(cl, request, info)
403         CLIENT *cl;
404         int request;
405         char *info;
406 {
407         register struct ct_data *ct = (struct ct_data *)cl->cl_private;
408
409         switch (request) {
410         case CLSET_TIMEOUT:
411                 ct->ct_wait = *(struct timeval *)info;
412                 ct->ct_waitset = TRUE;
413                 break;
414         case CLGET_TIMEOUT:
415                 *(struct timeval *)info = ct->ct_wait;
416                 break;
417         case CLGET_SERVER_ADDR:
418                 *(struct sockaddr_in *)info = ct->ct_addr;
419                 break;
420         default:
421                 return (FALSE);
422         }
423         return (TRUE);
424 }
425
426
427 static void
428 clnttcp_destroy(h)
429         CLIENT *h;
430 {
431         register struct ct_data *ct =
432             (struct ct_data *) h->cl_private;
433
434         if (ct->ct_closeit) {
435 #ifdef WIN32
436                 (void)closesocket(ct->ct_sock);
437 #else
438                 (void)close(ct->ct_sock);
439 #endif
440         }
441         XDR_DESTROY(&(ct->ct_xdrs));
442         mem_free((caddr_t)ct, sizeof(struct ct_data));
443         mem_free((caddr_t)h, sizeof(CLIENT));
444 }
445
446 /*
447  * Interface between xdr serializer and tcp connection.
448  * Behaves like the system calls, read & write, but keeps some error state
449  * around for the rpc level.
450  */
451 static int
452 readtcp(ct, buf, len)
453         register struct ct_data *ct;
454         caddr_t buf;
455         register int len;
456 {
457 #ifdef FD_SETSIZE
458         fd_set mask;
459         fd_set readfds;
460
461         if (len == 0)
462                 return (0);
463         FD_ZERO(&mask);
464         FD_SET(ct->ct_sock, &mask);
465 #else
466         register int mask = 1 << (ct->ct_sock);
467         int readfds;
468
469         if (len == 0)
470                 return (0);
471
472 #endif /* def FD_SETSIZE */
473         while (TRUE) {
474                 readfds = mask;
475 #ifdef WIN32
476                 switch (select(0 /* unused in winsock */, &readfds, (int*)NULL, (int*)NULL,
477                                &(ct->ct_wait))) {
478                 case 0:
479                         ct->ct_error.re_status = RPC_TIMEDOUT;
480                         return (-1);
481
482                 case -1:
483                         if (WSAerrno == EINTR)
484                                 continue;
485                         ct->ct_error.re_status = RPC_CANTRECV;
486                         ct->ct_error.re_errno = WSAerrno;
487                         return (-1);
488                 }
489                 break;
490         }
491         switch (len = recv(ct->ct_sock, buf, len, 0)) {
492
493         case 0:
494                 /* premature eof */
495                 ct->ct_error.re_errno = WSAECONNRESET;
496                 ct->ct_error.re_status = RPC_CANTRECV;
497                 len = -1;  /* it's really an error */
498                 break;
499
500         case -1:
501                 ct->ct_error.re_errno = WSAerrno;
502                 ct->ct_error.re_status = RPC_CANTRECV;
503                 break;
504         }
505         return (len);
506 #else
507                 switch (select(FD_SETSIZE, &readfds, NULL, NULL,
508                                &(ct->ct_wait))) {
509                 case 0:
510                         ct->ct_error.re_status = RPC_TIMEDOUT;
511                         return (-1);
512
513                 case -1:
514                         if (errno == EINTR)
515                                 continue;
516                         ct->ct_error.re_status = RPC_CANTRECV;
517                         ct->ct_error.re_errno = errno;
518                         return (-1);
519                 }
520                 break;
521         }
522         switch (len = read(ct->ct_sock, buf, len)) {
523
524         case 0:
525                 /* premature eof */
526                 ct->ct_error.re_errno = ECONNRESET;
527                 ct->ct_error.re_status = RPC_CANTRECV;
528                 len = -1;  /* it's really an error */
529                 break;
530
531         case -1:
532                 ct->ct_error.re_errno = errno;
533                 ct->ct_error.re_status = RPC_CANTRECV;
534                 break;
535         }
536         return (len);
537 #endif
538 }
539
540 static int
541 writetcp(ct, buf, len)
542         struct ct_data *ct;
543         caddr_t buf;
544         int len;
545 {
546         register int i, cnt;
547
548         for (cnt = len; cnt > 0; cnt -= i, buf += i) {
549 #ifdef WIN32
550                 if ((i = send(ct->ct_sock, buf, cnt, 0)) == -1) {
551                         ct->ct_error.re_errno = WSAerrno;
552 #else
553                 if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
554                         ct->ct_error.re_errno = errno;
555 #endif
556                         ct->ct_error.re_status = RPC_CANTSEND;
557                         return (-1);
558                 }
559         }
560         return (len);
561 }