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