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.19 2002/09/06 18:35:12 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(CLIENT *, u_long, xdrproc_t, caddr_t, 55 xdrproc_t, caddr_t, struct timeval); 56 static void clntudp_abort(CLIENT *); 57 static void clntudp_geterr(CLIENT *, struct rpc_err *); 58 static bool_t clntudp_freeres(CLIENT *, xdrproc_t, caddr_t); 59 static bool_t clntudp_control(CLIENT *, u_int, void *); 60 static void clntudp_destroy(CLIENT *); 61 62 static struct clnt_ops udp_ops = { 63 clntudp_call, 64 clntudp_abort, 65 clntudp_geterr, 66 clntudp_freeres, 67 clntudp_destroy, 68 clntudp_control 69 }; 70 71 /* 72 * Private data kept per client handle 73 */ 74 struct cu_data { 75 int cu_sock; 76 bool_t cu_closeit; 77 struct sockaddr_in cu_raddr; 78 int cu_rlen; 79 struct timeval cu_wait; 80 struct timeval cu_total; 81 struct rpc_err cu_error; 82 XDR cu_outxdrs; 83 u_int cu_xdrpos; 84 u_int cu_sendsz; 85 char *cu_outbuf; 86 u_int cu_recvsz; 87 char cu_inbuf[1]; 88 }; 89 90 /* 91 * Create a UDP based client handle. 92 * If *sockp<0, *sockp is set to a newly created UPD socket. 93 * If raddr->sin_port is 0 a binder on the remote machine 94 * is consulted for the correct port number. 95 * NB: It is the clients responsibility to close *sockp. 96 * NB: The rpch->cl_auth is initialized to null authentication. 97 * Caller may wish to set this something more useful. 98 * 99 * wait is the amount of time used between retransmitting a call if 100 * no response has been heard; retransmission occurs until the actual 101 * rpc call times out. 102 * 103 * sendsz and recvsz are the maximum allowable packet sizes that can be 104 * sent and received. 105 */ 106 CLIENT * 107 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz) 108 struct sockaddr_in *raddr; 109 u_long program; 110 u_long version; 111 struct timeval wait; 112 int *sockp; 113 u_int sendsz; 114 u_int recvsz; 115 { 116 CLIENT *cl; 117 struct cu_data *cu = NULL; 118 struct timeval now; 119 struct rpc_msg call_msg; 120 121 cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); 122 if (cl == NULL) { 123 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 124 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 125 rpc_createerr.cf_error.re_errno = errno; 126 goto fooy; 127 } 128 sendsz = ((sendsz + 3) / 4) * 4; 129 recvsz = ((recvsz + 3) / 4) * 4; 130 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); 131 if (cu == NULL) { 132 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 133 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 134 rpc_createerr.cf_error.re_errno = errno; 135 goto fooy; 136 } 137 cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 138 139 (void)gettimeofday(&now, (struct timezone *)0); 140 if (raddr->sin_port == 0) { 141 u_short port; 142 if ((port = 143 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { 144 goto fooy; 145 } 146 raddr->sin_port = htons(port); 147 } 148 cl->cl_ops = &udp_ops; 149 cl->cl_private = (caddr_t)cu; 150 cu->cu_raddr = *raddr; 151 cu->cu_rlen = sizeof (cu->cu_raddr); 152 cu->cu_wait = wait; 153 cu->cu_total.tv_sec = -1; 154 cu->cu_total.tv_usec = -1; 155 cu->cu_sendsz = sendsz; 156 cu->cu_recvsz = recvsz; 157 call_msg.rm_xid = arc4random(); 158 call_msg.rm_direction = CALL; 159 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 160 call_msg.rm_call.cb_prog = program; 161 call_msg.rm_call.cb_vers = version; 162 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, 163 sendsz, XDR_ENCODE); 164 if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { 165 goto fooy; 166 } 167 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 168 if (*sockp < 0) { 169 int dontblock = 1; 170 171 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 172 if (*sockp < 0) { 173 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 174 rpc_createerr.cf_error.re_errno = errno; 175 goto fooy; 176 } 177 /* attempt to bind to priv port */ 178 (void)bindresvport(*sockp, (struct sockaddr_in *)0); 179 /* the sockets rpc controls are non-blocking */ 180 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); 181 cu->cu_closeit = TRUE; 182 } else { 183 cu->cu_closeit = FALSE; 184 } 185 cu->cu_sock = *sockp; 186 cl->cl_auth = authnone_create(); 187 return (cl); 188 fooy: 189 if (cu) 190 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); 191 if (cl) 192 mem_free((caddr_t)cl, sizeof(CLIENT)); 193 return ((CLIENT *)NULL); 194 } 195 196 CLIENT * 197 clntudp_create(raddr, program, version, wait, sockp) 198 struct sockaddr_in *raddr; 199 u_long program; 200 u_long version; 201 struct timeval wait; 202 int *sockp; 203 { 204 205 return(clntudp_bufcreate(raddr, program, version, wait, sockp, 206 UDPMSGSIZE, UDPMSGSIZE)); 207 } 208 209 static enum clnt_stat 210 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 211 CLIENT *cl; /* client handle */ 212 u_long proc; /* procedure number */ 213 xdrproc_t xargs; /* xdr routine for args */ 214 caddr_t argsp; /* pointer to args */ 215 xdrproc_t xresults; /* xdr routine for results */ 216 caddr_t resultsp; /* pointer to results */ 217 struct timeval utimeout; /* seconds to wait before giving up */ 218 { 219 struct cu_data *cu = (struct cu_data *)cl->cl_private; 220 XDR *xdrs; 221 int outlen; 222 int inlen; 223 socklen_t fromlen; 224 fd_set *fds, readfds; 225 struct sockaddr_in from; 226 struct rpc_msg reply_msg; 227 XDR reply_xdrs; 228 struct timeval time_waited, start, after, tmp1, tmp2; 229 bool_t ok; 230 int nrefreshes = 2; /* number of times to refresh cred */ 231 struct timeval timeout; 232 233 if (cu->cu_total.tv_usec == -1) 234 timeout = utimeout; /* use supplied timeout */ 235 else 236 timeout = cu->cu_total; /* use default timeout */ 237 238 if (cu->cu_sock+1 > FD_SETSIZE) { 239 int bytes = howmany(cu->cu_sock+1, NFDBITS) * sizeof(fd_mask); 240 fds = (fd_set *)malloc(bytes); 241 if (fds == NULL) 242 return (cu->cu_error.re_status = RPC_CANTSEND); 243 memset(fds, 0, bytes); 244 } else { 245 fds = &readfds; 246 FD_ZERO(fds); 247 } 248 249 timerclear(&time_waited); 250 call_again: 251 xdrs = &(cu->cu_outxdrs); 252 xdrs->x_op = XDR_ENCODE; 253 XDR_SETPOS(xdrs, cu->cu_xdrpos); 254 /* 255 * the transaction is the first thing in the out buffer 256 */ 257 (*(u_short *)(cu->cu_outbuf))++; 258 if (!XDR_PUTLONG(xdrs, (long *)&proc) || 259 !AUTH_MARSHALL(cl->cl_auth, xdrs) || 260 !(*xargs)(xdrs, argsp)) { 261 if (fds != &readfds) 262 free(fds); 263 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 264 } 265 outlen = (int)XDR_GETPOS(xdrs); 266 267 send_again: 268 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, 269 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) { 270 cu->cu_error.re_errno = errno; 271 if (fds != &readfds) 272 free(fds); 273 return (cu->cu_error.re_status = RPC_CANTSEND); 274 } 275 276 /* 277 * Hack to provide rpc-based message passing 278 */ 279 if (!timerisset(&timeout)) { 280 if (fds != &readfds) 281 free(fds); 282 return (cu->cu_error.re_status = RPC_TIMEDOUT); 283 } 284 285 /* 286 * sub-optimal code appears here because we have 287 * some clock time to spare while the packets are in flight. 288 * (We assume that this is actually only executed once.) 289 */ 290 reply_msg.acpted_rply.ar_verf = _null_auth; 291 reply_msg.acpted_rply.ar_results.where = resultsp; 292 reply_msg.acpted_rply.ar_results.proc = xresults; 293 294 gettimeofday(&start, NULL); 295 for (;;) { 296 /* XXX we know the other bits are still clear */ 297 FD_SET(cu->cu_sock, fds); 298 switch (select(cu->cu_sock+1, fds, NULL, NULL, &cu->cu_wait)) { 299 case 0: 300 timeradd(&time_waited, &cu->cu_wait, &tmp1); 301 time_waited = tmp1; 302 if (timercmp(&time_waited, &timeout, <)) 303 goto send_again; 304 if (fds != &readfds) 305 free(fds); 306 return (cu->cu_error.re_status = RPC_TIMEDOUT); 307 case -1: 308 if (errno == EINTR) { 309 gettimeofday(&after, NULL); 310 timersub(&after, &start, &tmp1); 311 timeradd(&time_waited, &tmp1, &tmp2); 312 time_waited = tmp2; 313 if (timercmp(&time_waited, &timeout, <)) 314 continue; 315 if (fds != &readfds) 316 free(fds); 317 return (cu->cu_error.re_status = RPC_TIMEDOUT); 318 } 319 cu->cu_error.re_errno = errno; 320 if (fds != &readfds) 321 free(fds); 322 return (cu->cu_error.re_status = RPC_CANTRECV); 323 } 324 325 do { 326 fromlen = sizeof(struct sockaddr); 327 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 328 (int) cu->cu_recvsz, 0, 329 (struct sockaddr *)&from, &fromlen); 330 } while (inlen < 0 && errno == EINTR); 331 if (inlen < 0) { 332 if (errno == EWOULDBLOCK) 333 continue; 334 cu->cu_error.re_errno = errno; 335 if (fds != &readfds) 336 free(fds); 337 return (cu->cu_error.re_status = RPC_CANTRECV); 338 } 339 if (inlen < sizeof(u_int32_t)) 340 continue; 341 /* see if reply transaction id matches sent id */ 342 if (((struct rpc_msg *)(cu->cu_inbuf))->rm_xid != 343 ((struct rpc_msg *)(cu->cu_outbuf))->rm_xid) 344 continue; 345 /* we now assume we have the proper reply */ 346 break; 347 } 348 349 /* 350 * now decode and validate the response 351 */ 352 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 353 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 354 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 355 if (ok) { 356 #if 0 357 /* 358 * XXX Would like to check these, but call_msg is not 359 * around. 360 */ 361 if (reply_msg.rm_call.cb_prog != call_msg.rm_call.cb_prog || 362 reply_msg.rm_call.cb_vers != call_msg.rm_call.cb_vers || 363 reply_msg.rm_call.cb_proc != call_msg.rm_call.cb_proc) { 364 goto call_again; /* XXX spin? */ 365 } 366 #endif 367 368 _seterr_reply(&reply_msg, &(cu->cu_error)); 369 if (cu->cu_error.re_status == RPC_SUCCESS) { 370 if (!AUTH_VALIDATE(cl->cl_auth, 371 &reply_msg.acpted_rply.ar_verf)) { 372 cu->cu_error.re_status = RPC_AUTHERROR; 373 cu->cu_error.re_why = AUTH_INVALIDRESP; 374 } 375 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 376 xdrs->x_op = XDR_FREE; 377 (void)xdr_opaque_auth(xdrs, 378 &(reply_msg.acpted_rply.ar_verf)); 379 } 380 } else { 381 /* maybe our credentials need to be refreshed ... */ 382 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 383 nrefreshes--; 384 goto call_again; 385 } 386 } 387 } else { 388 /* xdr_replymsg() may have left some things allocated */ 389 int op = reply_xdrs.x_op; 390 reply_xdrs.x_op = XDR_FREE; 391 xdr_replymsg(&reply_xdrs, &reply_msg); 392 reply_xdrs.x_op = op; 393 cu->cu_error.re_status = RPC_CANTDECODERES; 394 } 395 396 if (fds != &readfds) 397 free(fds); 398 return (cu->cu_error.re_status); 399 } 400 401 static void 402 clntudp_geterr(cl, errp) 403 CLIENT *cl; 404 struct rpc_err *errp; 405 { 406 struct cu_data *cu = (struct cu_data *)cl->cl_private; 407 408 *errp = cu->cu_error; 409 } 410 411 412 static bool_t 413 clntudp_freeres(cl, xdr_res, res_ptr) 414 CLIENT *cl; 415 xdrproc_t xdr_res; 416 caddr_t res_ptr; 417 { 418 struct cu_data *cu = (struct cu_data *)cl->cl_private; 419 XDR *xdrs = &(cu->cu_outxdrs); 420 421 xdrs->x_op = XDR_FREE; 422 return ((*xdr_res)(xdrs, res_ptr)); 423 } 424 425 static void 426 clntudp_abort(CLIENT *clnt) 427 { 428 } 429 430 static bool_t 431 clntudp_control(cl, request, info) 432 CLIENT *cl; 433 u_int request; 434 void *info; 435 { 436 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(CLIENT *cl) 462 { 463 struct cu_data *cu = (struct cu_data *)cl->cl_private; 464 465 if (cu->cu_closeit) { 466 (void)close(cu->cu_sock); 467 } 468 XDR_DESTROY(&(cu->cu_outxdrs)); 469 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); 470 mem_free((caddr_t)cl, sizeof(CLIENT)); 471 } 472