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