1/* 2 * Copyright (c) 1999-2007 Apple 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_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";*/ 55/*static char *sccsid = "from: @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";*/ 56static char *rcsid = "$Id: clnt_udp.c,v 1.4 2002/03/15 22:07:49 majka Exp $"; 57#endif 58 59/* 60 * clnt_udp.c, Implements a UDP/IP based, client side RPC. 61 * 62 * Copyright (C) 1984, Sun Microsystems, Inc. 63 */ 64 65#include <stdio.h> 66#include <stdlib.h> 67#include <string.h> 68#include <unistd.h> 69#include <rpc/rpc.h> 70#include <sys/socket.h> 71#include <sys/fcntl.h> 72#include <sys/ioctl.h> 73#include <netdb.h> 74#include <errno.h> 75#include <rpc/pmap_clnt.h> 76 77extern int bindresvport(); 78extern bool_t xdr_opaque_auth(); 79 80extern u_short pmap_getport_timeout(struct sockaddr_in *address, uint32_t program, uint32_t version, uint32_t protocol, struct timeval *timeout, struct timeval *totaltimeout); 81 82extern int errno; 83 84/* 85 * UDP bases client side rpc operations 86 */ 87static enum clnt_stat clntudp_call(); 88static void clntudp_abort(); 89static void clntudp_geterr(); 90static bool_t clntudp_freeres(); 91static bool_t clntudp_control(); 92static void clntudp_destroy(); 93 94static struct clnt_ops udp_ops = { 95 clntudp_call, 96 clntudp_abort, 97 clntudp_geterr, 98 clntudp_freeres, 99 clntudp_destroy, 100 clntudp_control 101}; 102 103/* 104 * Private data kept per client handle 105 */ 106struct cu_data { 107 int cu_sock; 108 bool_t cu_closeit; 109 struct sockaddr_in cu_raddr; 110 int cu_rlen; 111 struct timeval cu_retry_timeout; 112 struct timeval cu_total_timeout; 113 struct rpc_err cu_error; 114 XDR cu_outxdrs; 115 u_int cu_xdrpos; 116 u_int cu_sendsz; 117 char *cu_outbuf; 118 u_int cu_recvsz; 119 char cu_inbuf[1]; 120}; 121 122/* 123 * Create a UDP based client handle. 124 * If *sockp<0, *sockp is set to a newly created UPD socket. 125 * If raddr->sin_port is 0 a binder on the remote machine 126 * is consulted for the correct port number. 127 * NB: It is the clients responsibility to close *sockp. 128 * NB: The rpch->cl_auth is initialized to null authentication. 129 * Caller may wish to set this something more useful. 130 * 131 * wait is the amount of time used between retransmitting a call if 132 * no response has been heard; retransmition occurs until the actual 133 * rpc call times out. 134 * 135 * sendsz and recvsz are the maximum allowable packet sizes that can be 136 * sent and received. 137 */ 138CLIENT * 139clntudp_bufcreate_timeout(struct sockaddr_in *raddr, uint32_t program, uint32_t version, int *sockp, uint32_t sendsz, uint32_t recvsz, struct timeval *retry_timeout, struct timeval *total_timeout) 140{ 141 CLIENT *cl; 142 struct cu_data *cu = NULL; 143 struct timeval now; 144 struct rpc_msg call_msg; 145 int rfd; 146 u_short port; 147 socklen_t len; 148 unsigned int rsize; 149 150 cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); 151 if (cl == NULL) 152 { 153 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 154 rpc_createerr.cf_error.re_errno = errno; 155 goto fooy; 156 } 157 158 sendsz = ((sendsz + 3) / 4) * 4; 159 recvsz = ((recvsz + 3) / 4) * 4; 160 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); 161 if (cu == NULL) 162 { 163 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 164 rpc_createerr.cf_error.re_errno = errno; 165 goto fooy; 166 } 167 168 cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 169 170 if (raddr->sin_port == 0) 171 { 172 port = pmap_getport_timeout(raddr, program, version, IPPROTO_UDP, retry_timeout, total_timeout); 173 if (port == 0) goto fooy; 174 175 raddr->sin_port = htons(port); 176 } 177 178 cl->cl_ops = &udp_ops; 179 cl->cl_private = (caddr_t)cu; 180 cu->cu_raddr = *raddr; 181 cu->cu_rlen = sizeof (cu->cu_raddr); 182 cu->cu_sendsz = sendsz; 183 cu->cu_recvsz = recvsz; 184 185 cu->cu_retry_timeout.tv_sec = 1; 186 cu->cu_retry_timeout.tv_usec = 0; 187 if (retry_timeout != NULL) cu->cu_retry_timeout = *retry_timeout; 188 189 cu->cu_total_timeout.tv_sec = -1; 190 cu->cu_total_timeout.tv_usec = -1; 191 if (total_timeout != NULL) cu->cu_total_timeout = *total_timeout; 192 193 rfd = open("/dev/random", O_RDONLY, 0); 194 if ((rfd < 0) || (read(rfd, &call_msg.rm_xid, sizeof(call_msg.rm_xid)) != sizeof(call_msg.rm_xid))) 195 { 196 gettimeofday(&now, (struct timezone *)0); 197 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; 198 } 199 200 if (rfd > 0) close(rfd); 201 202 call_msg.rm_direction = CALL; 203 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 204 call_msg.rm_call.cb_prog = program; 205 call_msg.rm_call.cb_vers = version; 206 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE); 207 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) goto fooy; 208 209 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 210 if (*sockp < 0) 211 { 212 int dontblock = 1; 213 214 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 215 if (*sockp < 0) 216 { 217 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 218 rpc_createerr.cf_error.re_errno = errno; 219 goto fooy; 220 } 221 222 /* attempt to bind to prov port */ 223 (void)bindresvport(*sockp, (struct sockaddr_in *)0); 224 225 /* the socket's rpc controls are non-blocking */ 226 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); 227 cu->cu_closeit = TRUE; 228 229 /* set receive size */ 230 rsize = 0; 231 len = sizeof(rsize); 232 if (getsockopt(*sockp, SOL_SOCKET, SO_RCVBUF, (char *)&rsize, &len) != 0) 233 { 234 close(*sockp); 235 *sockp = -1; 236 goto fooy; 237 } 238 239 len = sizeof(recvsz); 240 if ((recvsz > rsize) && (setsockopt(*sockp, SOL_SOCKET, SO_RCVBUF, (char *)&recvsz, len))) 241 { 242 close(*sockp); 243 *sockp = -1; 244 goto fooy; 245 } 246 } 247 else 248 { 249 cu->cu_closeit = FALSE; 250 } 251 252 cu->cu_sock = *sockp; 253 cl->cl_auth = authnone_create(); 254 return (cl); 255 256fooy: 257 if (cu) mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); 258 if (cl) mem_free((caddr_t)cl, sizeof(CLIENT)); 259 return NULL; 260} 261 262CLIENT * 263clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz) 264#ifdef __LP64__ 265 struct sockaddr_in *raddr; 266 uint32_t program; 267 uint32_t version; 268 struct timeval wait; 269 int *sockp; 270 uint32_t sendsz; 271 uint32_t recvsz; 272#else 273 struct sockaddr_in *raddr; 274 u_long program; 275 u_long version; 276 struct timeval wait; 277 register int *sockp; 278 u_int sendsz; 279 u_int recvsz; 280#endif 281{ 282 return clntudp_bufcreate_timeout(raddr, (uint32_t)program, (uint32_t)version, sockp, (uint32_t)sendsz, (uint32_t)recvsz, &wait, NULL); 283} 284 285CLIENT * 286clntudp_create(raddr, program, version, wait, sockp) 287#ifdef __LP64__ 288 struct sockaddr_in *raddr; 289 uint32_t program; 290 uint32_t version; 291 struct timeval wait; 292 int *sockp; 293#else 294 struct sockaddr_in *raddr; 295 u_long program; 296 u_long version; 297 struct timeval wait; 298 register int *sockp; 299#endif 300{ 301 302 return clntudp_bufcreate(raddr, program, version, wait, sockp, UDPMSGSIZE, UDPMSGSIZE); 303} 304 305static enum clnt_stat 306clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 307 register CLIENT *cl; /* client handle */ 308#ifdef __LP64__ 309 uint32_t proc; /* procedure number */ 310#else 311 u_long proc; /* procedure number */ 312#endif 313 xdrproc_t xargs; /* xdr routine for args */ 314 caddr_t argsp; /* pointer to args */ 315 xdrproc_t xresults; /* xdr routine for results */ 316 caddr_t resultsp; /* pointer to results */ 317 struct timeval utimeout; /* seconds to wait before giving up */ 318{ 319 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 320 register XDR *xdrs; 321 register int outlen; 322 register int inlen; 323 u_int32_t fromlen; 324 fd_set readfds; 325 fd_set mask; 326 struct sockaddr_in from; 327 struct rpc_msg reply_msg; 328 XDR reply_xdrs; 329 struct timeval time_waited; 330 bool_t ok; 331 int nrefreshes = 2; /* number of times to refresh cred */ 332 struct timeval timeout; 333 334 if (cu->cu_total_timeout.tv_sec == -1) 335 { 336 /* use supplied total timeout */ 337 timeout = utimeout; 338 } 339 else 340 { 341 /* use client's total timeout */ 342 timeout = cu->cu_total_timeout; 343 } 344 345 time_waited.tv_sec = 0; 346 time_waited.tv_usec = 0; 347 348call_again: 349 xdrs = &(cu->cu_outxdrs); 350 xdrs->x_op = XDR_ENCODE; 351 XDR_SETPOS(xdrs, cu->cu_xdrpos); 352 353 /* 354 * the transaction is the first thing in the out buffer 355 */ 356 (*(u_short *)(cu->cu_outbuf))++; 357#ifdef __LP64__ 358 if ((! XDR_PUTLONG(xdrs, (int *)&proc)) || 359 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 360 (! (*xargs)(xdrs, argsp, 0))) 361 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 362#else 363 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) || 364 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 365 (! (*xargs)(xdrs, argsp, 0))) 366 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 367#endif 368 outlen = (int)XDR_GETPOS(xdrs); 369 370send_again: 371 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) 372 { 373 cu->cu_error.re_errno = errno; 374 return (cu->cu_error.re_status = RPC_CANTSEND); 375 } 376 377 /* 378 * Hack to provide rpc-based message passing 379 */ 380 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) 381 { 382 return (cu->cu_error.re_status = RPC_TIMEDOUT); 383 } 384 385 /* 386 * sub-optimal code appears here because we have 387 * some clock time to spare while the packets are in flight. 388 * (We assume that this is actually only executed once.) 389 */ 390 reply_msg.acpted_rply.ar_verf = _null_auth; 391 reply_msg.acpted_rply.ar_results.where = resultsp; 392 reply_msg.acpted_rply.ar_results.proc = xresults; 393 FD_ZERO(&mask); 394 FD_SET(cu->cu_sock, &mask); 395 for (;;) 396 { 397 readfds = mask; 398 switch (select(cu->cu_sock+1, &readfds, NULL, NULL, &(cu->cu_retry_timeout))) { 399 case 0: 400 time_waited.tv_sec += cu->cu_retry_timeout.tv_sec; 401 time_waited.tv_usec += cu->cu_retry_timeout.tv_usec; 402 while (time_waited.tv_usec >= 1000000) { 403 time_waited.tv_sec++; 404 time_waited.tv_usec -= 1000000; 405 } 406 if ((time_waited.tv_sec < timeout.tv_sec) || 407 ((time_waited.tv_sec == timeout.tv_sec) && 408 (time_waited.tv_usec < timeout.tv_usec))) 409 goto send_again; 410 return (cu->cu_error.re_status = RPC_TIMEDOUT); 411 412 /* 413 * buggy in other cases because time_waited is not being 414 * updated. 415 */ 416 case -1: 417 if (errno == EINTR) 418 continue; 419 cu->cu_error.re_errno = errno; 420 return (cu->cu_error.re_status = RPC_CANTRECV); 421 } 422 do { 423 fromlen = sizeof(struct sockaddr); 424 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 425 (int) cu->cu_recvsz, 0, 426 (struct sockaddr *)&from, &fromlen); 427 } while (inlen < 0 && errno == EINTR); 428 if (inlen < 0) { 429 if (errno == EWOULDBLOCK) 430 continue; 431 cu->cu_error.re_errno = errno; 432 return (cu->cu_error.re_status = RPC_CANTRECV); 433 } 434#ifdef __LP64__ 435 if (inlen < sizeof(uint32_t)) 436 continue; 437 /* see if reply transaction id matches sent id */ 438 if (*((uint32_t *)(cu->cu_inbuf)) != *((uint32_t *)(cu->cu_outbuf))) continue; 439#else 440 if (inlen < sizeof(u_long)) 441 continue; 442 /* see if reply transaction id matches sent id */ 443 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf))) continue; 444#endif 445 /* we now assume we have the proper reply */ 446 break; 447 } 448 449 /* 450 * now decode and validate the response 451 */ 452 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 453 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 454 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 455 if (ok) { 456 _seterr_reply(&reply_msg, &(cu->cu_error)); 457 if (cu->cu_error.re_status == RPC_SUCCESS) { 458 if (! AUTH_VALIDATE(cl->cl_auth, 459 &reply_msg.acpted_rply.ar_verf)) { 460 cu->cu_error.re_status = RPC_AUTHERROR; 461 cu->cu_error.re_why = AUTH_INVALIDRESP; 462 } 463 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 464 xdrs->x_op = XDR_FREE; 465 (void)xdr_opaque_auth(xdrs, 466 &(reply_msg.acpted_rply.ar_verf)); 467 } 468 } /* end successful completion */ 469 else { 470 /* maybe our credentials need to be refreshed ... */ 471 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 472 nrefreshes--; 473 goto call_again; 474 } 475 } /* end of unsuccessful completion */ 476 } /* end of valid reply message */ 477 else { 478 cu->cu_error.re_status = RPC_CANTDECODERES; 479 } 480 return (cu->cu_error.re_status); 481} 482 483static void 484clntudp_geterr(cl, errp) 485 CLIENT *cl; 486 struct rpc_err *errp; 487{ 488 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 489 490 *errp = cu->cu_error; 491} 492 493 494static bool_t 495clntudp_freeres(cl, xdr_res, res_ptr) 496 CLIENT *cl; 497 xdrproc_t xdr_res; 498 caddr_t res_ptr; 499{ 500 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 501 register XDR *xdrs = &(cu->cu_outxdrs); 502 503 xdrs->x_op = XDR_FREE; 504 return ((*xdr_res)(xdrs, res_ptr, 0)); 505} 506 507static void 508clntudp_abort(/*h*/) 509 /*CLIENT *h;*/ 510{ 511} 512 513static bool_t 514clntudp_control(cl, request, info) 515 CLIENT *cl; 516 int request; 517 char *info; 518{ 519 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 520 521 switch (request) { 522 case CLSET_TIMEOUT: 523 cu->cu_total_timeout = *(struct timeval *)info; 524 break; 525 case CLGET_TIMEOUT: 526 *(struct timeval *)info = cu->cu_total_timeout; 527 break; 528 case CLSET_RETRY_TIMEOUT: 529 cu->cu_retry_timeout = *(struct timeval *)info; 530 break; 531 case CLGET_RETRY_TIMEOUT: 532 *(struct timeval *)info = cu->cu_retry_timeout; 533 break; 534 case CLGET_SERVER_ADDR: 535 *(struct sockaddr_in *)info = cu->cu_raddr; 536 break; 537 default: 538 return (FALSE); 539 } 540 return (TRUE); 541} 542 543static void 544clntudp_destroy(cl) 545 CLIENT *cl; 546{ 547 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 548 549 if (cu->cu_closeit) { 550 (void)close(cu->cu_sock); 551 } 552 XDR_DESTROY(&(cu->cu_outxdrs)); 553 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); 554 mem_free((caddr_t)cl, sizeof(CLIENT)); 555} 556