1 /* 2 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 3 * unrestricted use provided that this legend is included on all tape 4 * media and as a part of the software program in whole or part. Users 5 * may copy or modify Sun RPC without charge, but are not authorized 6 * to license or distribute it to anyone else except as part of a product or 7 * program developed by the user. 8 * 9 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 10 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 11 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 12 * 13 * Sun RPC is provided with no support and without any obligation on the 14 * part of Sun Microsystems, Inc. to assist in its use, correction, 15 * modification or enhancement. 16 * 17 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 18 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 19 * OR ANY PART THEREOF. 20 * 21 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 22 * or profits or other special, indirect and consequential damages, even if 23 * Sun has been advised of the possibility of such damages. 24 * 25 * Sun Microsystems, Inc. 26 * 2550 Garcia Avenue 27 * Mountain View, California 94043 28 */ 29 30 #if defined(LIBC_SCCS) && !defined(lint) 31 static char *rcsid = "$OpenBSD: clnt_udp.c,v 1.16 2000/08/24 17:03:15 deraadt Exp $"; 32 #endif /* LIBC_SCCS and not lint */ 33 34 /* 35 * clnt_udp.c, Implements a UDP/IP based, client side RPC. 36 * 37 * Copyright (C) 1984, Sun Microsystems, Inc. 38 */ 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <rpc/rpc.h> 45 #include <sys/socket.h> 46 #include <sys/ioctl.h> 47 #include <netdb.h> 48 #include <errno.h> 49 #include <rpc/pmap_clnt.h> 50 51 /* 52 * UDP bases client side rpc operations 53 */ 54 static enum clnt_stat clntudp_call(); 55 static void clntudp_abort(); 56 static void clntudp_geterr(); 57 static bool_t clntudp_freeres(); 58 static bool_t clntudp_control(); 59 static void clntudp_destroy(); 60 61 static struct clnt_ops udp_ops = { 62 clntudp_call, 63 clntudp_abort, 64 clntudp_geterr, 65 clntudp_freeres, 66 clntudp_destroy, 67 clntudp_control 68 }; 69 70 /* 71 * Private data kept per client handle 72 */ 73 struct cu_data { 74 int cu_sock; 75 bool_t cu_closeit; 76 struct sockaddr_in cu_raddr; 77 int cu_rlen; 78 struct timeval cu_wait; 79 struct timeval cu_total; 80 struct rpc_err cu_error; 81 XDR cu_outxdrs; 82 u_int cu_xdrpos; 83 u_int cu_sendsz; 84 char *cu_outbuf; 85 u_int cu_recvsz; 86 char cu_inbuf[1]; 87 }; 88 89 /* 90 * Create a UDP based client handle. 91 * If *sockp<0, *sockp is set to a newly created UPD socket. 92 * If raddr->sin_port is 0 a binder on the remote machine 93 * is consulted for the correct port number. 94 * NB: It is the clients responsibility to close *sockp. 95 * NB: The rpch->cl_auth is initialized to null authentication. 96 * Caller may wish to set this something more useful. 97 * 98 * wait is the amount of time used between retransmitting a call if 99 * no response has been heard; retransmition occurs until the actual 100 * rpc call times out. 101 * 102 * sendsz and recvsz are the maximum allowable packet sizes that can be 103 * sent and received. 104 */ 105 CLIENT * 106 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz) 107 struct sockaddr_in *raddr; 108 u_long program; 109 u_long version; 110 struct timeval wait; 111 register int *sockp; 112 u_int sendsz; 113 u_int recvsz; 114 { 115 CLIENT *cl; 116 register struct cu_data *cu = NULL; 117 struct timeval now; 118 struct rpc_msg call_msg; 119 120 cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); 121 if (cl == NULL) { 122 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 123 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 124 rpc_createerr.cf_error.re_errno = errno; 125 goto fooy; 126 } 127 sendsz = ((sendsz + 3) / 4) * 4; 128 recvsz = ((recvsz + 3) / 4) * 4; 129 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); 130 if (cu == NULL) { 131 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 132 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 133 rpc_createerr.cf_error.re_errno = errno; 134 goto fooy; 135 } 136 cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 137 138 (void)gettimeofday(&now, (struct timezone *)0); 139 if (raddr->sin_port == 0) { 140 u_short port; 141 if ((port = 142 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { 143 goto fooy; 144 } 145 raddr->sin_port = htons(port); 146 } 147 cl->cl_ops = &udp_ops; 148 cl->cl_private = (caddr_t)cu; 149 cu->cu_raddr = *raddr; 150 cu->cu_rlen = sizeof (cu->cu_raddr); 151 cu->cu_wait = wait; 152 cu->cu_total.tv_sec = -1; 153 cu->cu_total.tv_usec = -1; 154 cu->cu_sendsz = sendsz; 155 cu->cu_recvsz = recvsz; 156 call_msg.rm_xid = arc4random(); 157 call_msg.rm_direction = CALL; 158 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 159 call_msg.rm_call.cb_prog = program; 160 call_msg.rm_call.cb_vers = version; 161 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, 162 sendsz, XDR_ENCODE); 163 if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { 164 goto fooy; 165 } 166 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 167 if (*sockp < 0) { 168 int dontblock = 1; 169 170 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 171 if (*sockp < 0) { 172 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 173 rpc_createerr.cf_error.re_errno = errno; 174 goto fooy; 175 } 176 /* attempt to bind to priv port */ 177 (void)bindresvport(*sockp, (struct sockaddr_in *)0); 178 /* the sockets rpc controls are non-blocking */ 179 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); 180 cu->cu_closeit = TRUE; 181 } else { 182 cu->cu_closeit = FALSE; 183 } 184 cu->cu_sock = *sockp; 185 cl->cl_auth = authnone_create(); 186 return (cl); 187 fooy: 188 if (cu) 189 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); 190 if (cl) 191 mem_free((caddr_t)cl, sizeof(CLIENT)); 192 return ((CLIENT *)NULL); 193 } 194 195 CLIENT * 196 clntudp_create(raddr, program, version, wait, sockp) 197 struct sockaddr_in *raddr; 198 u_long program; 199 u_long version; 200 struct timeval wait; 201 register int *sockp; 202 { 203 204 return(clntudp_bufcreate(raddr, program, version, wait, sockp, 205 UDPMSGSIZE, UDPMSGSIZE)); 206 } 207 208 static enum clnt_stat 209 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 210 register CLIENT *cl; /* client handle */ 211 u_long proc; /* procedure number */ 212 xdrproc_t xargs; /* xdr routine for args */ 213 caddr_t argsp; /* pointer to args */ 214 xdrproc_t xresults; /* xdr routine for results */ 215 caddr_t resultsp; /* pointer to results */ 216 struct timeval utimeout; /* seconds to wait before giving up */ 217 { 218 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 219 register XDR *xdrs; 220 register int outlen; 221 register int inlen; 222 int fromlen; 223 fd_set *fds, readfds; 224 struct sockaddr_in from; 225 struct rpc_msg reply_msg; 226 XDR reply_xdrs; 227 struct timeval time_waited, start, after, tmp1, tmp2; 228 bool_t ok; 229 int nrefreshes = 2; /* number of times to refresh cred */ 230 struct timeval timeout; 231 232 if (cu->cu_total.tv_usec == -1) 233 timeout = utimeout; /* use supplied timeout */ 234 else 235 timeout = cu->cu_total; /* use default timeout */ 236 237 if (cu->cu_sock+1 > FD_SETSIZE) { 238 int bytes = howmany(cu->cu_sock+1, NFDBITS) * sizeof(fd_mask); 239 fds = (fd_set *)malloc(bytes); 240 if (fds == NULL) 241 return (cu->cu_error.re_status = RPC_CANTSEND); 242 memset(fds, 0, bytes); 243 } else { 244 fds = &readfds; 245 FD_ZERO(fds); 246 } 247 248 timerclear(&time_waited); 249 call_again: 250 xdrs = &(cu->cu_outxdrs); 251 xdrs->x_op = XDR_ENCODE; 252 XDR_SETPOS(xdrs, cu->cu_xdrpos); 253 /* 254 * the transaction is the first thing in the out buffer 255 */ 256 (*(u_short *)(cu->cu_outbuf))++; 257 if (!XDR_PUTLONG(xdrs, (long *)&proc) || 258 !AUTH_MARSHALL(cl->cl_auth, xdrs) || 259 !(*xargs)(xdrs, argsp)) { 260 if (fds != &readfds) 261 free(fds); 262 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 263 } 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) != outlen) { 269 cu->cu_error.re_errno = errno; 270 if (fds != &readfds) 271 free(fds); 272 return (cu->cu_error.re_status = RPC_CANTSEND); 273 } 274 275 /* 276 * Hack to provide rpc-based message passing 277 */ 278 if (!timerisset(&timeout)) { 279 if (fds != &readfds) 280 free(fds); 281 return (cu->cu_error.re_status = RPC_TIMEDOUT); 282 } 283 284 /* 285 * sub-optimal code appears here because we have 286 * some clock time to spare while the packets are in flight. 287 * (We assume that this is actually only executed once.) 288 */ 289 reply_msg.acpted_rply.ar_verf = _null_auth; 290 reply_msg.acpted_rply.ar_results.where = resultsp; 291 reply_msg.acpted_rply.ar_results.proc = xresults; 292 293 gettimeofday(&start, NULL); 294 for (;;) { 295 /* XXX we know the other bits are still clear */ 296 FD_SET(cu->cu_sock, fds); 297 switch (select(cu->cu_sock+1, fds, NULL, NULL, &cu->cu_wait)) { 298 case 0: 299 timeradd(&time_waited, &cu->cu_wait, &tmp1); 300 time_waited = tmp1; 301 if (timercmp(&time_waited, &timeout, <)) 302 goto send_again; 303 if (fds != &readfds) 304 free(fds); 305 return (cu->cu_error.re_status = RPC_TIMEDOUT); 306 case -1: 307 if (errno == EINTR) { 308 gettimeofday(&after, NULL); 309 timersub(&after, &start, &tmp1); 310 timeradd(&time_waited, &tmp1, &tmp2); 311 time_waited = tmp2; 312 if (timercmp(&time_waited, &timeout, <)) 313 continue; 314 if (fds != &readfds) 315 free(fds); 316 return (cu->cu_error.re_status = RPC_TIMEDOUT); 317 } 318 cu->cu_error.re_errno = errno; 319 if (fds != &readfds) 320 free(fds); 321 return (cu->cu_error.re_status = RPC_CANTRECV); 322 } 323 324 do { 325 fromlen = sizeof(struct sockaddr); 326 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 327 (int) cu->cu_recvsz, 0, 328 (struct sockaddr *)&from, &fromlen); 329 } while (inlen < 0 && errno == EINTR); 330 if (inlen < 0) { 331 if (errno == EWOULDBLOCK) 332 continue; 333 cu->cu_error.re_errno = errno; 334 if (fds != &readfds) 335 free(fds); 336 return (cu->cu_error.re_status = RPC_CANTRECV); 337 } 338 if (inlen < sizeof(u_int32_t)) 339 continue; 340 /* see if reply transaction id matches sent id */ 341 if (((struct rpc_msg *)(cu->cu_inbuf))->rm_xid != 342 ((struct rpc_msg *)(cu->cu_outbuf))->rm_xid) 343 continue; 344 /* we now assume we have the proper reply */ 345 break; 346 } 347 348 /* 349 * now decode and validate the response 350 */ 351 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 352 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 353 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 354 if (ok) { 355 #if 0 356 /* 357 * XXX Would like to check these, but call_msg is not 358 * around. 359 */ 360 if (reply_msg.rm_call.cb_prog != call_msg.rm_call.cb_prog || 361 reply_msg.rm_call.cb_vers != call_msg.rm_call.cb_vers || 362 reply_msg.rm_call.cb_proc != call_msg.rm_call.cb_proc) { 363 goto call_again; /* XXX spin? */ 364 } 365 #endif 366 367 _seterr_reply(&reply_msg, &(cu->cu_error)); 368 if (cu->cu_error.re_status == RPC_SUCCESS) { 369 if (!AUTH_VALIDATE(cl->cl_auth, 370 &reply_msg.acpted_rply.ar_verf)) { 371 cu->cu_error.re_status = RPC_AUTHERROR; 372 cu->cu_error.re_why = AUTH_INVALIDRESP; 373 } 374 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 375 xdrs->x_op = XDR_FREE; 376 (void)xdr_opaque_auth(xdrs, 377 &(reply_msg.acpted_rply.ar_verf)); 378 } 379 } else { 380 /* maybe our credentials need to be refreshed ... */ 381 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 382 nrefreshes--; 383 goto call_again; 384 } 385 } 386 } else { 387 /* xdr_replymsg() may have left some things allocated */ 388 int op = reply_xdrs.x_op; 389 reply_xdrs.x_op = XDR_FREE; 390 xdr_replymsg(&reply_xdrs, &reply_msg); 391 reply_xdrs.x_op = op; 392 cu->cu_error.re_status = RPC_CANTDECODERES; 393 } 394 395 if (fds != &readfds) 396 free(fds); 397 return (cu->cu_error.re_status); 398 } 399 400 static void 401 clntudp_geterr(cl, errp) 402 CLIENT *cl; 403 struct rpc_err *errp; 404 { 405 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 406 407 *errp = cu->cu_error; 408 } 409 410 411 static bool_t 412 clntudp_freeres(cl, xdr_res, res_ptr) 413 CLIENT *cl; 414 xdrproc_t xdr_res; 415 caddr_t res_ptr; 416 { 417 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 418 register XDR *xdrs = &(cu->cu_outxdrs); 419 420 xdrs->x_op = XDR_FREE; 421 return ((*xdr_res)(xdrs, res_ptr)); 422 } 423 424 static void 425 clntudp_abort(/*h*/) 426 /*CLIENT *h;*/ 427 { 428 } 429 430 static bool_t 431 clntudp_control(cl, request, info) 432 CLIENT *cl; 433 int request; 434 char *info; 435 { 436 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 437 438 switch (request) { 439 case CLSET_TIMEOUT: 440 cu->cu_total = *(struct timeval *)info; 441 break; 442 case CLGET_TIMEOUT: 443 *(struct timeval *)info = cu->cu_total; 444 break; 445 case CLSET_RETRY_TIMEOUT: 446 cu->cu_wait = *(struct timeval *)info; 447 break; 448 case CLGET_RETRY_TIMEOUT: 449 *(struct timeval *)info = cu->cu_wait; 450 break; 451 case CLGET_SERVER_ADDR: 452 *(struct sockaddr_in *)info = cu->cu_raddr; 453 break; 454 default: 455 return (FALSE); 456 } 457 return (TRUE); 458 } 459 460 static void 461 clntudp_destroy(cl) 462 CLIENT *cl; 463 { 464 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 465 466 if (cu->cu_closeit) { 467 (void)close(cu->cu_sock); 468 } 469 XDR_DESTROY(&(cu->cu_outxdrs)); 470 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); 471 mem_free((caddr_t)cl, sizeof(CLIENT)); 472 } 473