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