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