1 /* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ 2 /* 3 * Copyright (c) 2010, Oracle America, Inc. 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * * Neither the name of the "Oracle America, Inc." nor the names of 19 * its contributors may be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 23 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 28 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 #if !defined(lint) && defined(SCCSIDS) 35 static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; 36 #endif 37 38 /* 39 * clnt_tcp.c, Implements a TCP/IP based, client side RPC. 40 * 41 * TCP based RPC supports 'batched calls'. 42 * A sequence of calls may be batched-up in a send buffer. The rpc call 43 * return immediately to the client even though the call was not necessarily 44 * sent. The batching occurs if the results' xdr routine is NULL (0) AND 45 * the rpc timeout value is zero (see clnt.h, rpc). 46 * 47 * Clients should NOT casually batch calls that in fact return results; that is, 48 * the server side should be aware that a call is batched and not produce any 49 * return message. Batched calls that produce many result messages can 50 * deadlock (netlock) the client and the server.... 51 * 52 * Now go hang yourself. 53 */ 54 55 #include <stdio.h> 56 #include <unistd.h> 57 #include <gssrpc/rpc.h> 58 #include <sys/socket.h> 59 #include <netdb.h> 60 #include <errno.h> 61 #include <string.h> 62 #include <gssrpc/pmap_clnt.h> 63 /* FD_ZERO may need memset declaration (e.g., Solaris 9) */ 64 #include <string.h> 65 #include <port-sockets.h> 66 67 #define MCALL_MSG_SIZE 24 68 69 #ifndef GETSOCKNAME_ARG3_TYPE 70 #define GETSOCKNAME_ARG3_TYPE int 71 #endif 72 73 static enum clnt_stat clnttcp_call(CLIENT *, rpcproc_t, xdrproc_t, void *, 74 xdrproc_t, void *, struct timeval); 75 static void clnttcp_abort(CLIENT *); 76 static void clnttcp_geterr(CLIENT *, struct rpc_err *); 77 static bool_t clnttcp_freeres(CLIENT *, xdrproc_t, void *); 78 static bool_t clnttcp_control(CLIENT *, int, void *); 79 static void clnttcp_destroy(CLIENT *); 80 81 static struct clnt_ops tcp_ops = { 82 clnttcp_call, 83 clnttcp_abort, 84 clnttcp_geterr, 85 clnttcp_freeres, 86 clnttcp_destroy, 87 clnttcp_control 88 }; 89 90 struct ct_data { 91 int ct_sock; 92 bool_t ct_closeit; 93 struct timeval ct_wait; 94 bool_t ct_waitset; /* wait set by clnt_control? */ 95 struct sockaddr_in ct_addr; 96 struct rpc_err ct_error; 97 union { 98 char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ 99 uint32_t ct_mcalli; 100 } ct_u; 101 u_int ct_mpos; /* pos after marshal */ 102 XDR ct_xdrs; 103 }; 104 105 static int readtcp(char *, caddr_t, int); 106 static int writetcp(char *, caddr_t, int); 107 108 109 /* 110 * Create a client handle for a tcp/ip connection. 111 * If *sockp<0, *sockp is set to a newly created TCP socket and it is 112 * connected to raddr. If *sockp non-negative then 113 * raddr is ignored. The rpc/tcp package does buffering 114 * similar to stdio, so the client must pick send and receive buffer sizes,]; 115 * 0 => use the default. 116 * If raddr->sin_port is 0, then a binder on the remote machine is 117 * consulted for the right port number. 118 * NB: *sockp is copied into a private area. 119 * NB: It is the clients responsibility to close *sockp. 120 * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this 121 * something more useful. 122 */ 123 CLIENT * 124 clnttcp_create( 125 struct sockaddr_in *raddr, 126 rpcprog_t prog, 127 rpcvers_t vers, 128 SOCKET *sockp, 129 u_int sendsz, 130 u_int recvsz) 131 { 132 CLIENT *h; 133 struct ct_data *ct = 0; 134 struct timeval now; 135 struct rpc_msg call_msg; 136 137 h = (CLIENT *)mem_alloc(sizeof(*h)); 138 if (h == NULL) { 139 (void)fprintf(stderr, "clnttcp_create: out of memory\n"); 140 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 141 rpc_createerr.cf_error.re_errno = errno; 142 goto fooy; 143 } 144 ct = (struct ct_data *)mem_alloc(sizeof(*ct)); 145 if (ct == NULL) { 146 (void)fprintf(stderr, "clnttcp_create: out of memory\n"); 147 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 148 rpc_createerr.cf_error.re_errno = errno; 149 goto fooy; 150 } 151 152 /* 153 * If no port number given ask the pmap for one 154 */ 155 if (raddr != NULL && raddr->sin_port == 0) { 156 u_short port; 157 if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) { 158 mem_free((caddr_t)ct, sizeof(struct ct_data)); 159 mem_free((caddr_t)h, sizeof(CLIENT)); 160 return ((CLIENT *)NULL); 161 } 162 raddr->sin_port = htons(port); 163 } 164 165 /* 166 * If no socket given, open one 167 */ 168 if (*sockp < 0) { 169 *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 170 (void)bindresvport_sa(*sockp, NULL); 171 if (*sockp < 0 || raddr == NULL || 172 connect(*sockp, (struct sockaddr *)raddr, 173 sizeof(*raddr)) < 0) { 174 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 175 rpc_createerr.cf_error.re_errno = errno; 176 (void)closesocket(*sockp); 177 goto fooy; 178 } 179 ct->ct_closeit = TRUE; 180 } else { 181 ct->ct_closeit = FALSE; 182 } 183 184 /* 185 * Set up private data struct 186 */ 187 ct->ct_sock = *sockp; 188 ct->ct_wait.tv_usec = 0; 189 ct->ct_waitset = FALSE; 190 if (raddr == NULL) { 191 /* Get the remote address from the socket, if it's IPv4. */ 192 struct sockaddr_in sin; 193 socklen_t len = sizeof(sin); 194 int ret = getpeername(ct->ct_sock, (struct sockaddr *)&sin, &len); 195 if (ret == 0 && len == sizeof(sin) && sin.sin_family == AF_INET) 196 ct->ct_addr = sin; 197 else 198 memset(&ct->ct_addr, 0, sizeof(ct->ct_addr)); 199 } else 200 ct->ct_addr = *raddr; 201 202 /* 203 * Initialize call message 204 */ 205 (void)gettimeofday(&now, (struct timezone *)0); 206 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; 207 call_msg.rm_direction = CALL; 208 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 209 call_msg.rm_call.cb_prog = prog; 210 call_msg.rm_call.cb_vers = vers; 211 212 /* 213 * pre-serialize the staic part of the call msg and stash it away 214 */ 215 xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcall, MCALL_MSG_SIZE, 216 XDR_ENCODE); 217 if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { 218 if (ct->ct_closeit) 219 (void)closesocket(*sockp); 220 goto fooy; 221 } 222 ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); 223 XDR_DESTROY(&(ct->ct_xdrs)); 224 225 /* 226 * Create a client handle which uses xdrrec for serialization 227 * and authnone for authentication. 228 */ 229 xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, 230 (caddr_t)ct, readtcp, writetcp); 231 h->cl_ops = &tcp_ops; 232 h->cl_private = (caddr_t) ct; 233 h->cl_auth = authnone_create(); 234 return (h); 235 236 fooy: 237 /* 238 * Something goofed, free stuff and barf 239 */ 240 mem_free((caddr_t)ct, sizeof(struct ct_data)); 241 mem_free((caddr_t)h, sizeof(CLIENT)); 242 return ((CLIENT *)NULL); 243 } 244 245 static enum clnt_stat 246 clnttcp_call( 247 CLIENT *h, 248 rpcproc_t proc, 249 xdrproc_t xdr_args, 250 void * args_ptr, 251 xdrproc_t xdr_results, 252 void * results_ptr, 253 struct timeval timeout) 254 { 255 struct ct_data *ct = h->cl_private; 256 XDR *xdrs = &ct->ct_xdrs; 257 struct rpc_msg reply_msg; 258 uint32_t x_id; 259 uint32_t *msg_x_id = &ct->ct_u.ct_mcalli; /* yuk */ 260 bool_t shipnow; 261 int refreshes = 2; 262 long procl = proc; 263 264 if (!ct->ct_waitset) { 265 ct->ct_wait = timeout; 266 } 267 268 shipnow = 269 (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 270 && timeout.tv_usec == 0) ? FALSE : TRUE; 271 272 call_again: 273 xdrs->x_op = XDR_ENCODE; 274 ct->ct_error.re_status = RPC_SUCCESS; 275 x_id = ntohl(--(*msg_x_id)); 276 if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcall, ct->ct_mpos)) || 277 (! XDR_PUTLONG(xdrs, &procl)) || 278 (! AUTH_MARSHALL(h->cl_auth, xdrs)) || 279 (! AUTH_WRAP(h->cl_auth, xdrs, xdr_args, args_ptr))) { 280 if (ct->ct_error.re_status == RPC_SUCCESS) 281 ct->ct_error.re_status = RPC_CANTENCODEARGS; 282 (void)xdrrec_endofrecord(xdrs, TRUE); 283 return (ct->ct_error.re_status); 284 } 285 if (! xdrrec_endofrecord(xdrs, shipnow)) 286 return (ct->ct_error.re_status = RPC_CANTSEND); 287 if (! shipnow) 288 return (RPC_SUCCESS); 289 /* 290 * Hack to provide rpc-based message passing 291 */ 292 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 293 return(ct->ct_error.re_status = RPC_TIMEDOUT); 294 } 295 296 297 /* 298 * Keep receiving until we get a valid transaction id 299 */ 300 xdrs->x_op = XDR_DECODE; 301 while (TRUE) { 302 reply_msg.acpted_rply.ar_verf = gssrpc__null_auth; 303 reply_msg.acpted_rply.ar_results.where = NULL; 304 reply_msg.acpted_rply.ar_results.proc = xdr_void; 305 if (! xdrrec_skiprecord(xdrs)) 306 return (ct->ct_error.re_status); 307 /* now decode and validate the response header */ 308 if (! xdr_replymsg(xdrs, &reply_msg)) { 309 /* 310 * Free some stuff allocated by xdr_replymsg() 311 * to avoid leaks, since it may allocate 312 * memory from partially successful decodes. 313 */ 314 enum xdr_op op = xdrs->x_op; 315 xdrs->x_op = XDR_FREE; 316 xdr_replymsg(xdrs, &reply_msg); 317 xdrs->x_op = op; 318 if (ct->ct_error.re_status == RPC_SUCCESS) 319 continue; 320 return (ct->ct_error.re_status); 321 } 322 if (reply_msg.rm_xid == x_id) 323 break; 324 } 325 326 /* 327 * process header 328 */ 329 gssrpc__seterr_reply(&reply_msg, &(ct->ct_error)); 330 if (ct->ct_error.re_status == RPC_SUCCESS) { 331 if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) { 332 ct->ct_error.re_status = RPC_AUTHERROR; 333 ct->ct_error.re_why = AUTH_INVALIDRESP; 334 } else if (! AUTH_UNWRAP(h->cl_auth, xdrs, 335 xdr_results, results_ptr)) { 336 if (ct->ct_error.re_status == RPC_SUCCESS) 337 ct->ct_error.re_status = RPC_CANTDECODERES; 338 } 339 } /* end successful completion */ 340 else { 341 /* maybe our credentials need to be refreshed ... */ 342 if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg)) 343 goto call_again; 344 } /* end of unsuccessful completion */ 345 /* free verifier ... */ 346 if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && 347 (reply_msg.acpted_rply.ar_verf.oa_base != NULL)) { 348 xdrs->x_op = XDR_FREE; 349 (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf)); 350 } 351 return (ct->ct_error.re_status); 352 } 353 354 static void 355 clnttcp_geterr( 356 CLIENT *h, 357 struct rpc_err *errp) 358 { 359 struct ct_data *ct = h->cl_private; 360 361 *errp = ct->ct_error; 362 } 363 364 static bool_t 365 clnttcp_freeres( 366 CLIENT *cl, 367 xdrproc_t xdr_res, 368 void * res_ptr) 369 { 370 struct ct_data *ct = cl->cl_private; 371 XDR *xdrs = &ct->ct_xdrs; 372 373 xdrs->x_op = XDR_FREE; 374 return ((*xdr_res)(xdrs, res_ptr)); 375 } 376 377 /*ARGSUSED*/ 378 static void 379 clnttcp_abort(CLIENT *cl) 380 { 381 } 382 383 static bool_t 384 clnttcp_control( 385 CLIENT *cl, 386 int request, 387 void *info) 388 { 389 struct ct_data *ct = cl->cl_private; 390 GETSOCKNAME_ARG3_TYPE len; 391 392 switch (request) { 393 case CLSET_TIMEOUT: 394 ct->ct_wait = *(struct timeval *)info; 395 ct->ct_waitset = TRUE; 396 break; 397 case CLGET_TIMEOUT: 398 *(struct timeval *)info = ct->ct_wait; 399 break; 400 case CLGET_SERVER_ADDR: 401 *(struct sockaddr_in *)info = ct->ct_addr; 402 break; 403 case CLGET_LOCAL_ADDR: 404 len = sizeof(struct sockaddr); 405 if (getsockname(ct->ct_sock, (struct sockaddr*)info, &len) < 0) 406 return FALSE; 407 else 408 return TRUE; 409 default: 410 return (FALSE); 411 } 412 return (TRUE); 413 } 414 415 416 static void 417 clnttcp_destroy(CLIENT *h) 418 { 419 struct ct_data *ct = h->cl_private; 420 421 if (ct->ct_closeit) 422 (void)closesocket(ct->ct_sock); 423 XDR_DESTROY(&(ct->ct_xdrs)); 424 mem_free((caddr_t)ct, sizeof(struct ct_data)); 425 mem_free((caddr_t)h, sizeof(CLIENT)); 426 } 427 428 /* 429 * Interface between xdr serializer and tcp connection. 430 * Behaves like the system calls, read & write, but keeps some error state 431 * around for the rpc level. 432 */ 433 static int 434 readtcp( 435 char *ctptr, 436 caddr_t buf, 437 int len) 438 { 439 struct ct_data *ct = (void *)ctptr; 440 struct timeval tout; 441 #ifdef FD_SETSIZE 442 fd_set mask; 443 fd_set readfds; 444 445 if (len == 0) 446 return (0); 447 FD_ZERO(&mask); 448 FD_SET(ct->ct_sock, &mask); 449 #else 450 int mask = 1 << (ct->ct_sock); 451 int readfds; 452 453 if (len == 0) 454 return (0); 455 456 #endif /* def FD_SETSIZE */ 457 while (TRUE) { 458 readfds = mask; 459 tout = ct->ct_wait; 460 switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set*)NULL, (fd_set*)NULL, 461 &tout)) { 462 case 0: 463 ct->ct_error.re_status = RPC_TIMEDOUT; 464 return (-1); 465 466 case -1: 467 if (errno == EINTR) 468 continue; 469 ct->ct_error.re_status = RPC_CANTRECV; 470 ct->ct_error.re_errno = errno; 471 return (-1); 472 } 473 break; 474 } 475 switch (len = read(ct->ct_sock, buf, (size_t) len)) { 476 477 case 0: 478 /* premature eof */ 479 ct->ct_error.re_errno = ECONNRESET; 480 ct->ct_error.re_status = RPC_CANTRECV; 481 len = -1; /* it's really an error */ 482 break; 483 484 case -1: 485 ct->ct_error.re_errno = errno; 486 ct->ct_error.re_status = RPC_CANTRECV; 487 break; 488 } 489 return (len); 490 } 491 492 static int 493 writetcp( 494 char *ctptr, 495 caddr_t buf, 496 int len) 497 { 498 struct ct_data *ct = (struct ct_data *)(void *)ctptr; 499 int i, cnt; 500 501 for (cnt = len; cnt > 0; cnt -= i, buf += i) { 502 if ((i = write(ct->ct_sock, buf, (size_t) cnt)) == -1) { 503 ct->ct_error.re_errno = errno; 504 ct->ct_error.re_status = RPC_CANTSEND; 505 return (-1); 506 } 507 } 508 return (len); 509 } 510