1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright (c) 1984, 1986, 1987, 1988, 1989, 1996 AT&T 29 * All Rights Reserved 30 */ 31 32 /* 33 * University Copyright- Copyright (c) 1982, 1986, 1988 34 * The Regents of the University of California 35 * All Rights Reserved 36 * 37 * University Acknowledgment- Portions of this document are derived from 38 * software developed by the University of California, Berkeley, and its 39 * contributors. 40 */ 41 42 /* 43 * clnt_udp.c, Implements a UDP/IP based, client side RPC. 44 */ 45 46 #include <rpc/rpc.h> 47 #include <sys/socket.h> 48 #include <sys/time.h> 49 #include <sys/ioctl.h> 50 #include <netdb.h> 51 #include <errno.h> 52 #include <rpc/pmap_clnt.h> 53 #include <rpc/clnt_soc.h> 54 #include <syslog.h> 55 #include <sys/filio.h> 56 #include <malloc.h> 57 #include <unistd.h> 58 #include <stropts.h> 59 #include <stdio.h> 60 61 62 extern int errno; 63 64 extern int _socket(int, int, int); 65 extern pid_t getpid(); 66 extern int bindresvport(int, struct sockaddr_in *); 67 extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *); 68 extern int _sendto(int, const char *, int, int, 69 const struct sockaddr *, int); 70 extern int _recvfrom(int, char *, int, int, 71 struct sockaddr *, int *); 72 73 74 static struct clnt_ops *clntudp_ops(); 75 76 /* 77 * Private data kept per client handle 78 */ 79 struct cu_data { 80 int cu_sock; 81 bool_t cu_closeit; 82 struct sockaddr_in cu_raddr; 83 int cu_rlen; 84 struct timeval cu_wait; 85 struct timeval cu_total; 86 struct rpc_err cu_error; 87 XDR cu_outxdrs; 88 u_int cu_xdrpos; 89 u_int cu_sendsz; 90 char *cu_outbuf; 91 u_int cu_recvsz; 92 char cu_inbuf[1]; 93 }; 94 95 /* 96 * Create a UDP based client handle. 97 * If *sockp<0, *sockp is set to a newly created UPD socket. 98 * If raddr->sin_port is 0 a binder on the remote machine 99 * is consulted for the correct port number. 100 * NB: It is the clients responsibility to close *sockp. 101 * NB: The rpch->cl_auth is initialized to null authentication. 102 * Caller may wish to set this something more useful. 103 * 104 * wait is the amount of time used between retransmitting a call if 105 * no response has been heard; retransmition occurs until the actual 106 * rpc call times out. 107 * 108 * sendsz and recvsz are the maximum allowable packet sizes that can be 109 * sent and received. 110 */ 111 CLIENT * 112 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz) 113 struct sockaddr_in *raddr; 114 rpcprog_t program; 115 rpcvers_t version; 116 struct timeval wait; 117 register int *sockp; 118 u_int sendsz; 119 u_int recvsz; 120 { 121 CLIENT *cl; 122 register struct cu_data *cu; 123 struct timeval now; 124 struct rpc_msg call_msg; 125 126 cl = (CLIENT *)mem_alloc(sizeof (CLIENT)); 127 if (cl == NULL) { 128 (void) syslog(LOG_ERR, "clntudp_create: out of memory"); 129 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 130 rpc_createerr.cf_error.re_errno = errno; 131 goto fooy; 132 } 133 sendsz = ((sendsz + 3) / 4) * 4; 134 recvsz = ((recvsz + 3) / 4) * 4; 135 cu = (struct cu_data *)mem_alloc(sizeof (*cu) + sendsz + recvsz); 136 if (cu == NULL) { 137 (void) syslog(LOG_ERR, "clntudp_create: out of memory"); 138 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 139 rpc_createerr.cf_error.re_errno = errno; 140 goto fooy; 141 } 142 cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 143 144 (void) gettimeofday(&now, (struct timezone *)0); 145 if (raddr->sin_port == 0) { 146 u_short port; 147 if ((port = 148 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { 149 goto fooy; 150 } 151 raddr->sin_port = htons(port); 152 } 153 cl->cl_ops = clntudp_ops(); 154 cl->cl_private = (caddr_t)cu; 155 cu->cu_raddr = *raddr; 156 cu->cu_rlen = sizeof (cu->cu_raddr); 157 cu->cu_wait = wait; 158 cu->cu_total.tv_sec = -1; 159 cu->cu_total.tv_usec = -1; 160 cu->cu_sendsz = sendsz; 161 cu->cu_recvsz = recvsz; 162 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; 163 call_msg.rm_direction = CALL; 164 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 165 call_msg.rm_call.cb_prog = program; 166 call_msg.rm_call.cb_vers = version; 167 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, 168 sendsz, XDR_ENCODE); 169 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { 170 goto fooy; 171 } 172 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 173 if (*sockp < 0) { 174 int dontblock = 1; 175 176 *sockp = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 177 if (*sockp < 0) { 178 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 179 rpc_createerr.cf_error.re_errno = errno; 180 goto fooy; 181 } 182 /* attempt to bind to prov port */ 183 (void) bindresvport(*sockp, (struct sockaddr_in *)0); 184 /* the sockets rpc controls are non-blocking */ 185 (void) ioctl(*sockp, FIONBIO, (char *) &dontblock); 186 cu->cu_closeit = TRUE; 187 } else { 188 cu->cu_closeit = FALSE; 189 } 190 cu->cu_sock = *sockp; 191 cl->cl_auth = authnone_create(); 192 return (cl); 193 fooy: 194 if (cu) 195 mem_free((caddr_t)cu, sizeof (*cu) + sendsz + recvsz); 196 if (cl) 197 mem_free((caddr_t)cl, sizeof (CLIENT)); 198 return ((CLIENT *)NULL); 199 } 200 201 CLIENT * 202 clntudp_create(raddr, program, version, wait, sockp) 203 struct sockaddr_in *raddr; 204 rpcprog_t program; 205 rpcvers_t version; 206 struct timeval wait; 207 register int *sockp; 208 { 209 210 return (clntudp_bufcreate(raddr, program, version, wait, sockp, 211 UDPMSGSIZE, UDPMSGSIZE)); 212 } 213 214 static enum clnt_stat 215 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 216 register CLIENT *cl; /* client handle */ 217 rpcproc_t proc; /* procedure number */ 218 xdrproc_t xargs; /* xdr routine for args */ 219 caddr_t argsp; /* pointer to args */ 220 xdrproc_t xresults; /* xdr routine for results */ 221 caddr_t resultsp; /* pointer to results */ 222 struct timeval utimeout; /* seconds to wait before giving up */ 223 { 224 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 225 register XDR *xdrs; 226 register int outlen; 227 register int inlen; 228 int fromlen; 229 fd_set readfds; 230 fd_set mask; 231 struct sockaddr_in from; 232 struct rpc_msg reply_msg; 233 XDR reply_xdrs; 234 struct timeval startime, curtime; 235 int firsttimeout = 1; 236 struct timeval time_waited; 237 struct timeval retransmit_time; 238 bool_t ok; 239 int nrefreshes = 2; /* number of times to refresh cred */ 240 struct timeval timeout; 241 242 if (cu->cu_total.tv_usec == -1) { 243 timeout = utimeout; /* use supplied timeout */ 244 } else { 245 timeout = cu->cu_total; /* use default timeout */ 246 } 247 248 time_waited.tv_sec = 0; 249 time_waited.tv_usec = 0; 250 retransmit_time = cu->cu_wait; 251 252 call_again: 253 xdrs = &(cu->cu_outxdrs); 254 xdrs->x_op = XDR_ENCODE; 255 XDR_SETPOS(xdrs, cu->cu_xdrpos); 256 /* 257 * the transaction is the first thing in the out buffer 258 */ 259 (*(u_short *)(cu->cu_outbuf))++; 260 if ((! XDR_PUTINT32(xdrs, (int32_t *)&proc)) || 261 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 262 (! (*xargs)(xdrs, argsp))) 263 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 264 outlen = (int)XDR_GETPOS(xdrs); 265 266 send_again: 267 if (_sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, 268 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) 269 != outlen) { 270 cu->cu_error.re_errno = errno; 271 return (cu->cu_error.re_status = RPC_CANTSEND); 272 } 273 274 /* 275 * Hack to provide rpc-based message passing 276 */ 277 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 278 return (cu->cu_error.re_status = RPC_TIMEDOUT); 279 } 280 /* 281 * sub-optimal code appears here because we have 282 * some clock time to spare while the packets are in flight. 283 * (We assume that this is actually only executed once.) 284 */ 285 reply_msg.acpted_rply.ar_verf = _null_auth; 286 reply_msg.acpted_rply.ar_results.where = resultsp; 287 reply_msg.acpted_rply.ar_results.proc = xresults; 288 FD_ZERO(&mask); 289 FD_SET(cu->cu_sock, &mask); 290 for (;;) { 291 readfds = mask; 292 switch (select(__rpc_dtbsize(), &readfds, NULL, 293 NULL, &(retransmit_time))) { 294 295 case 0: 296 time_waited.tv_sec += retransmit_time.tv_sec; 297 time_waited.tv_usec += retransmit_time.tv_usec; 298 while (time_waited.tv_usec >= 1000000) { 299 time_waited.tv_sec++; 300 time_waited.tv_usec -= 1000000; 301 } 302 303 /* update retransmit_time */ 304 305 if (retransmit_time.tv_sec < RPC_MAX_BACKOFF) { 306 retransmit_time.tv_usec += retransmit_time.tv_usec; 307 retransmit_time.tv_sec += retransmit_time.tv_sec; 308 while (retransmit_time.tv_usec >= 1000000) { 309 retransmit_time.tv_sec++; 310 retransmit_time.tv_usec -= 1000000; 311 } 312 } 313 314 if ((time_waited.tv_sec < timeout.tv_sec) || 315 ((time_waited.tv_sec == timeout.tv_sec) && 316 (time_waited.tv_usec < timeout.tv_usec))) 317 goto send_again; 318 return (cu->cu_error.re_status = RPC_TIMEDOUT); 319 320 /* 321 * buggy in other cases because time_waited is not being 322 * updated. 323 */ 324 case -1: 325 if (errno != EINTR) { 326 cu->cu_error.re_errno = errno; 327 return (cu->cu_error.re_status = RPC_CANTRECV); 328 } 329 330 /* interrupted by another signal, update time_waited */ 331 if (firsttimeout) { 332 /* 333 * Could have done gettimeofday before clnt_call 334 * but that means 1 more system call per each 335 * clnt_call, so do it after first time out 336 */ 337 if (gettimeofday(&startime, 338 (struct timezone *) NULL) == -1) { 339 errno = 0; 340 continue; 341 } 342 firsttimeout = 0; 343 errno = 0; 344 continue; 345 }; 346 347 if (gettimeofday(&curtime, 348 (struct timezone *) NULL) == -1) { 349 errno = 0; 350 continue; 351 }; 352 353 time_waited.tv_sec += curtime.tv_sec - startime.tv_sec; 354 time_waited.tv_usec += curtime.tv_usec - 355 startime.tv_usec; 356 while (time_waited.tv_usec < 0) { 357 time_waited.tv_sec--; 358 time_waited.tv_usec += 1000000; 359 }; 360 while (time_waited.tv_usec >= 1000000) { 361 time_waited.tv_sec++; 362 time_waited.tv_usec -= 1000000; 363 } 364 startime.tv_sec = curtime.tv_sec; 365 startime.tv_usec = curtime.tv_usec; 366 if ((time_waited.tv_sec > timeout.tv_sec) || 367 ((time_waited.tv_sec == timeout.tv_sec) && 368 (time_waited.tv_usec > timeout.tv_usec))) { 369 return (cu->cu_error.re_status = RPC_TIMEDOUT); 370 } 371 errno = 0; /* reset it */ 372 continue; 373 374 } 375 do { 376 fromlen = sizeof (struct sockaddr); 377 inlen = _recvfrom(cu->cu_sock, cu->cu_inbuf, 378 (int) cu->cu_recvsz, 0, 379 (struct sockaddr *)&from, &fromlen); 380 } while (inlen < 0 && errno == EINTR); 381 if (inlen < 0) { 382 if (errno == EWOULDBLOCK) 383 continue; 384 cu->cu_error.re_errno = errno; 385 return (cu->cu_error.re_status = RPC_CANTRECV); 386 } 387 if (inlen < sizeof (uint32_t)) 388 continue; 389 /* see if reply transaction id matches sent id */ 390 if (*((uint32_t *)(cu->cu_inbuf)) != 391 *((uint32_t *)(cu->cu_outbuf))) 392 continue; 393 /* we now assume we have the proper reply */ 394 break; 395 } 396 397 /* 398 * now decode and validate the response 399 */ 400 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 401 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 402 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 403 if (ok) { 404 __seterr_reply(&reply_msg, &(cu->cu_error)); 405 if (cu->cu_error.re_status == RPC_SUCCESS) { 406 if (! AUTH_VALIDATE(cl->cl_auth, 407 &reply_msg.acpted_rply.ar_verf)) { 408 cu->cu_error.re_status = RPC_AUTHERROR; 409 cu->cu_error.re_why = AUTH_INVALIDRESP; 410 } 411 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 412 xdrs->x_op = XDR_FREE; 413 (void) xdr_opaque_auth(xdrs, 414 &(reply_msg.acpted_rply.ar_verf)); 415 } 416 } /* end successful completion */ 417 else { 418 /* maybe our credentials need to be refreshed ... */ 419 if (nrefreshes > 0 && 420 AUTH_REFRESH(cl->cl_auth, &reply_msg)) { 421 nrefreshes--; 422 goto call_again; 423 } 424 } /* end of unsuccessful completion */ 425 } /* end of valid reply message */ 426 else { 427 cu->cu_error.re_status = RPC_CANTDECODERES; 428 } 429 return (cu->cu_error.re_status); 430 } 431 432 static void 433 clntudp_geterr(cl, errp) 434 CLIENT *cl; 435 struct rpc_err *errp; 436 { 437 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 438 439 *errp = cu->cu_error; 440 } 441 442 443 static bool_t 444 clntudp_freeres(cl, xdr_res, res_ptr) 445 CLIENT *cl; 446 xdrproc_t xdr_res; 447 caddr_t res_ptr; 448 { 449 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 450 register XDR *xdrs = &(cu->cu_outxdrs); 451 452 xdrs->x_op = XDR_FREE; 453 return ((*xdr_res)(xdrs, res_ptr)); 454 } 455 456 static void 457 clntudp_abort() 458 /* CLIENT *h; */ 459 { 460 } 461 462 static bool_t 463 clntudp_control(cl, request, info) 464 CLIENT *cl; 465 int request; 466 char *info; 467 { 468 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 469 470 switch (request) { 471 case CLSET_TIMEOUT: 472 cu->cu_total = *(struct timeval *)info; 473 break; 474 case CLGET_TIMEOUT: 475 *(struct timeval *)info = cu->cu_total; 476 break; 477 case CLSET_RETRY_TIMEOUT: 478 cu->cu_wait = *(struct timeval *)info; 479 break; 480 case CLGET_RETRY_TIMEOUT: 481 *(struct timeval *)info = cu->cu_wait; 482 break; 483 case CLGET_SERVER_ADDR: 484 *(struct sockaddr_in *)info = cu->cu_raddr; 485 break; 486 case CLGET_FD: 487 *(int *)info = cu->cu_sock; 488 break; 489 case CLSET_FD_CLOSE: 490 cu->cu_closeit = TRUE; 491 break; 492 case CLSET_FD_NCLOSE: 493 cu->cu_closeit = FALSE; 494 break; 495 default: 496 return (FALSE); 497 } 498 return (TRUE); 499 } 500 501 static void 502 clntudp_destroy(cl) 503 CLIENT *cl; 504 { 505 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 506 507 if (cu->cu_closeit) { 508 (void) close(cu->cu_sock); 509 } 510 XDR_DESTROY(&(cu->cu_outxdrs)); 511 mem_free((caddr_t)cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz)); 512 mem_free((caddr_t)cl, sizeof (CLIENT)); 513 } 514 515 static struct clnt_ops * 516 clntudp_ops() 517 { 518 static struct clnt_ops ops; 519 520 if (ops.cl_call == NULL) { 521 ops.cl_call = clntudp_call; 522 ops.cl_abort = clntudp_abort; 523 ops.cl_geterr = clntudp_geterr; 524 ops.cl_freeres = clntudp_freeres; 525 ops.cl_destroy = clntudp_destroy; 526 ops.cl_control = clntudp_control; 527 } 528 return (&ops); 529 } 530