1 /* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */ 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 #if !defined(lint) && defined(SCCSIDS) 31 static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro"; 32 #endif 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 <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 extern int errno; 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 119 cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); 120 if (cl == NULL) { 121 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 122 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 123 rpc_createerr.cf_error.re_errno = errno; 124 goto fooy; 125 } 126 sendsz = ((sendsz + 3) / 4) * 4; 127 recvsz = ((recvsz + 3) / 4) * 4; 128 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); 129 if (cu == NULL) { 130 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 131 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 132 rpc_createerr.cf_error.re_errno = errno; 133 goto fooy; 134 } 135 cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 136 137 (void)gettimeofday(&now, (struct timezone *)0); 138 if (raddr->sin_port == 0) { 139 u_short port; 140 if ((port = 141 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { 142 goto fooy; 143 } 144 raddr->sin_port = htons(port); 145 } 146 cl->cl_ops = &udp_ops; 147 cl->cl_private = (caddr_t)cu; 148 cu->cu_raddr = *raddr; 149 cu->cu_rlen = sizeof (cu->cu_raddr); 150 cu->cu_wait = wait; 151 cu->cu_total.tv_sec = -1; 152 cu->cu_total.tv_usec = -1; 153 cu->cu_sendsz = sendsz; 154 cu->cu_recvsz = recvsz; 155 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; 156 call_msg.rm_direction = CALL; 157 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 158 call_msg.rm_call.cb_prog = program; 159 call_msg.rm_call.cb_vers = version; 160 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, 161 sendsz, XDR_ENCODE); 162 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { 163 goto fooy; 164 } 165 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 166 if (*sockp < 0) { 167 int dontblock = 1; 168 169 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 170 if (*sockp < 0) { 171 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 172 rpc_createerr.cf_error.re_errno = errno; 173 goto fooy; 174 } 175 /* attempt to bind to prov port */ 176 (void)bindresvport(*sockp, (struct sockaddr_in *)0); 177 /* the sockets rpc controls are non-blocking */ 178 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); 179 cu->cu_closeit = TRUE; 180 } else { 181 cu->cu_closeit = FALSE; 182 } 183 cu->cu_sock = *sockp; 184 cl->cl_auth = authnone_create(); 185 return (cl); 186 fooy: 187 if (cu) 188 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); 189 if (cl) 190 mem_free((caddr_t)cl, sizeof(CLIENT)); 191 return ((CLIENT *)NULL); 192 } 193 194 CLIENT * 195 clntudp_create(raddr, program, version, wait, sockp) 196 struct sockaddr_in *raddr; 197 u_long program; 198 u_long version; 199 struct timeval wait; 200 register int *sockp; 201 { 202 203 return(clntudp_bufcreate(raddr, program, version, wait, sockp, 204 UDPMSGSIZE, UDPMSGSIZE)); 205 } 206 207 static enum clnt_stat 208 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 209 register CLIENT *cl; /* client handle */ 210 u_long proc; /* procedure number */ 211 xdrproc_t xargs; /* xdr routine for args */ 212 caddr_t argsp; /* pointer to args */ 213 xdrproc_t xresults; /* xdr routine for results */ 214 caddr_t resultsp; /* pointer to results */ 215 struct timeval utimeout; /* seconds to wait before giving up */ 216 { 217 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 218 register XDR *xdrs; 219 register int outlen; 220 register int inlen; 221 int fromlen; 222 #ifdef FD_SETSIZE 223 fd_set readfds; 224 fd_set mask; 225 #else 226 int readfds; 227 register int mask; 228 #endif /* def FD_SETSIZE */ 229 struct sockaddr_in from; 230 struct rpc_msg reply_msg; 231 XDR reply_xdrs; 232 struct timeval time_waited; 233 bool_t ok; 234 int nrefreshes = 2; /* number of times to refresh cred */ 235 struct timeval timeout; 236 237 if (cu->cu_total.tv_usec == -1) { 238 timeout = utimeout; /* use supplied timeout */ 239 } else { 240 timeout = cu->cu_total; /* use default timeout */ 241 } 242 243 time_waited.tv_sec = 0; 244 time_waited.tv_usec = 0; 245 call_again: 246 xdrs = &(cu->cu_outxdrs); 247 xdrs->x_op = XDR_ENCODE; 248 XDR_SETPOS(xdrs, cu->cu_xdrpos); 249 /* 250 * the transaction is the first thing in the out buffer 251 */ 252 (*(u_short *)(cu->cu_outbuf))++; 253 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) || 254 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 255 (! (*xargs)(xdrs, argsp))) 256 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 257 outlen = (int)XDR_GETPOS(xdrs); 258 259 send_again: 260 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, 261 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) 262 != outlen) { 263 cu->cu_error.re_errno = errno; 264 return (cu->cu_error.re_status = RPC_CANTSEND); 265 } 266 267 /* 268 * Hack to provide rpc-based message passing 269 */ 270 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 271 return (cu->cu_error.re_status = RPC_TIMEDOUT); 272 } 273 /* 274 * sub-optimal code appears here because we have 275 * some clock time to spare while the packets are in flight. 276 * (We assume that this is actually only executed once.) 277 */ 278 reply_msg.acpted_rply.ar_verf = _null_auth; 279 reply_msg.acpted_rply.ar_results.where = resultsp; 280 reply_msg.acpted_rply.ar_results.proc = xresults; 281 #ifdef FD_SETSIZE 282 FD_ZERO(&mask); 283 FD_SET(cu->cu_sock, &mask); 284 #else 285 mask = 1 << cu->cu_sock; 286 #endif /* def FD_SETSIZE */ 287 for (;;) { 288 readfds = mask; 289 switch (select(_rpc_dtablesize(), &readfds, (int *)NULL, 290 (int *)NULL, &(cu->cu_wait))) { 291 292 case 0: 293 time_waited.tv_sec += cu->cu_wait.tv_sec; 294 time_waited.tv_usec += cu->cu_wait.tv_usec; 295 while (time_waited.tv_usec >= 1000000) { 296 time_waited.tv_sec++; 297 time_waited.tv_usec -= 1000000; 298 } 299 if ((time_waited.tv_sec < timeout.tv_sec) || 300 ((time_waited.tv_sec == timeout.tv_sec) && 301 (time_waited.tv_usec < timeout.tv_usec))) 302 goto send_again; 303 return (cu->cu_error.re_status = RPC_TIMEDOUT); 304 305 /* 306 * buggy in other cases because time_waited is not being 307 * updated. 308 */ 309 case -1: 310 if (errno == EINTR) 311 continue; 312 cu->cu_error.re_errno = errno; 313 return (cu->cu_error.re_status = RPC_CANTRECV); 314 } 315 do { 316 fromlen = sizeof(struct sockaddr); 317 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 318 (int) cu->cu_recvsz, 0, 319 (struct sockaddr *)&from, &fromlen); 320 } while (inlen < 0 && errno == EINTR); 321 if (inlen < 0) { 322 if (errno == EWOULDBLOCK) 323 continue; 324 cu->cu_error.re_errno = errno; 325 return (cu->cu_error.re_status = RPC_CANTRECV); 326 } 327 if (inlen < sizeof(u_long)) 328 continue; 329 /* see if reply transaction id matches sent id */ 330 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf))) 331 continue; 332 /* we now assume we have the proper reply */ 333 break; 334 } 335 336 /* 337 * now decode and validate the response 338 */ 339 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 340 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 341 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 342 if (ok) { 343 _seterr_reply(&reply_msg, &(cu->cu_error)); 344 if (cu->cu_error.re_status == RPC_SUCCESS) { 345 if (! AUTH_VALIDATE(cl->cl_auth, 346 &reply_msg.acpted_rply.ar_verf)) { 347 cu->cu_error.re_status = RPC_AUTHERROR; 348 cu->cu_error.re_why = AUTH_INVALIDRESP; 349 } 350 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 351 xdrs->x_op = XDR_FREE; 352 (void)xdr_opaque_auth(xdrs, 353 &(reply_msg.acpted_rply.ar_verf)); 354 } 355 } /* end successful completion */ 356 else { 357 /* maybe our credentials need to be refreshed ... */ 358 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 359 nrefreshes--; 360 goto call_again; 361 } 362 } /* end of unsuccessful completion */ 363 } /* end of valid reply message */ 364 else { 365 cu->cu_error.re_status = RPC_CANTDECODERES; 366 } 367 return (cu->cu_error.re_status); 368 } 369 370 static void 371 clntudp_geterr(cl, errp) 372 CLIENT *cl; 373 struct rpc_err *errp; 374 { 375 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 376 377 *errp = cu->cu_error; 378 } 379 380 381 static bool_t 382 clntudp_freeres(cl, xdr_res, res_ptr) 383 CLIENT *cl; 384 xdrproc_t xdr_res; 385 caddr_t res_ptr; 386 { 387 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 388 register XDR *xdrs = &(cu->cu_outxdrs); 389 390 xdrs->x_op = XDR_FREE; 391 return ((*xdr_res)(xdrs, res_ptr)); 392 } 393 394 static void 395 clntudp_abort(/*h*/) 396 /*CLIENT *h;*/ 397 { 398 } 399 400 static bool_t 401 clntudp_control(cl, request, info) 402 CLIENT *cl; 403 int request; 404 char *info; 405 { 406 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 407 408 switch (request) { 409 case CLSET_TIMEOUT: 410 cu->cu_total = *(struct timeval *)info; 411 break; 412 case CLGET_TIMEOUT: 413 *(struct timeval *)info = cu->cu_total; 414 break; 415 case CLSET_RETRY_TIMEOUT: 416 cu->cu_wait = *(struct timeval *)info; 417 break; 418 case CLGET_RETRY_TIMEOUT: 419 *(struct timeval *)info = cu->cu_wait; 420 break; 421 case CLGET_SERVER_ADDR: 422 *(struct sockaddr_in *)info = cu->cu_raddr; 423 break; 424 default: 425 return (FALSE); 426 } 427 return (TRUE); 428 } 429 430 static void 431 clntudp_destroy(cl) 432 CLIENT *cl; 433 { 434 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 435 436 if (cu->cu_closeit) { 437 (void)close(cu->cu_sock); 438 } 439 XDR_DESTROY(&(cu->cu_outxdrs)); 440 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); 441 mem_free((caddr_t)cl, sizeof(CLIENT)); 442 } 443