1 /* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */ 2 /* 3 * Copyright (c) 2010, Oracle America, Inc. 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * * Neither the name of the "Oracle America, Inc." nor the names of 19 * its contributors may be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 23 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 28 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 #if !defined(lint) && defined(SCCSIDS) 35 static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro"; 36 #endif 37 38 /* 39 * clnt_udp.c, Implements a UDP/IP based, client side RPC. 40 */ 41 42 #include <stdio.h> 43 #include <unistd.h> 44 #include <gssrpc/rpc.h> 45 #include <sys/socket.h> 46 #include <sys/ioctl.h> 47 #if defined(sun) 48 #include <sys/filio.h> 49 #endif 50 #include <netdb.h> 51 #include <errno.h> 52 #include <string.h> 53 #include <gssrpc/pmap_clnt.h> 54 #include <port-sockets.h> 55 #include <errno.h> 56 57 #ifndef GETSOCKNAME_ARG3_TYPE 58 #define GETSOCKNAME_ARG3_TYPE int 59 #endif 60 61 /* 62 * UDP bases client side rpc operations 63 */ 64 static enum clnt_stat clntudp_call(CLIENT *, rpcproc_t, xdrproc_t, void *, 65 xdrproc_t, void *, struct timeval); 66 static void clntudp_abort(CLIENT *); 67 static void clntudp_geterr(CLIENT *, struct rpc_err *); 68 static bool_t clntudp_freeres(CLIENT *, xdrproc_t, void *); 69 static bool_t clntudp_control(CLIENT *, int, void *); 70 static void clntudp_destroy(CLIENT *); 71 72 static struct clnt_ops udp_ops = { 73 clntudp_call, 74 clntudp_abort, 75 clntudp_geterr, 76 clntudp_freeres, 77 clntudp_destroy, 78 clntudp_control 79 }; 80 81 /* 82 * Private data kept per client handle 83 */ 84 struct cu_data { 85 SOCKET cu_sock; 86 bool_t cu_closeit; 87 struct sockaddr_in cu_raddr; 88 int cu_rlen; 89 struct sockaddr_in cu_laddr; 90 GETSOCKNAME_ARG3_TYPE cu_llen; 91 struct timeval cu_wait; 92 struct timeval cu_total; 93 struct rpc_err cu_error; 94 XDR cu_outxdrs; 95 u_int cu_xdrpos; 96 u_int cu_sendsz; 97 char *cu_outbuf; 98 u_int cu_recvsz; 99 char cu_inbuf[1]; 100 }; 101 102 /* 103 * Create a UDP based client handle. 104 * If *sockp<0, *sockp is set to a newly created UPD socket. 105 * If raddr->sin_port is 0 a binder on the remote machine 106 * is consulted for the correct port number. 107 * NB: It is the clients responsibility to close *sockp. 108 * NB: The rpch->cl_auth is initialized to null authentication. 109 * Caller may wish to set this something more useful. 110 * 111 * wait is the amount of time used between retransmitting a call if 112 * no response has been heard; retransmission occurs until the actual 113 * rpc call times out. 114 * 115 * sendsz and recvsz are the maximum allowable packet sizes that can be 116 * sent and received. 117 */ 118 CLIENT * 119 clntudp_bufcreate( 120 struct sockaddr_in *raddr, 121 rpcprog_t program, 122 rpcvers_t version, 123 struct timeval wait, 124 int *sockp, 125 u_int sendsz, 126 u_int recvsz) 127 { 128 CLIENT *cl; 129 struct cu_data *cu = 0; 130 struct timeval now; 131 struct rpc_msg call_msg; 132 133 cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); 134 if (cl == NULL) { 135 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 136 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 137 rpc_createerr.cf_error.re_errno = errno; 138 goto fooy; 139 } 140 sendsz = ((sendsz + 3) / 4) * 4; 141 recvsz = ((recvsz + 3) / 4) * 4; 142 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); 143 if (cu == NULL) { 144 (void) fprintf(stderr, "clntudp_create: out of memory\n"); 145 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 146 rpc_createerr.cf_error.re_errno = errno; 147 goto fooy; 148 } 149 cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 150 151 (void)gettimeofday(&now, (struct timezone *)0); 152 if (raddr->sin_port == 0) { 153 u_short port; 154 if ((port = 155 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { 156 goto fooy; 157 } 158 raddr->sin_port = htons(port); 159 } 160 cl->cl_ops = &udp_ops; 161 cl->cl_private = (caddr_t)cu; 162 cu->cu_raddr = *raddr; 163 cu->cu_rlen = sizeof (cu->cu_raddr); 164 cu->cu_wait = wait; 165 cu->cu_total.tv_sec = -1; 166 cu->cu_total.tv_usec = -1; 167 cu->cu_sendsz = sendsz; 168 cu->cu_recvsz = recvsz; 169 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; 170 call_msg.rm_direction = CALL; 171 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 172 call_msg.rm_call.cb_prog = program; 173 call_msg.rm_call.cb_vers = version; 174 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, 175 sendsz, XDR_ENCODE); 176 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { 177 goto fooy; 178 } 179 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 180 if (*sockp < 0) { 181 int dontblock = 1; 182 183 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 184 if (*sockp < 0) { 185 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 186 rpc_createerr.cf_error.re_errno = errno; 187 goto fooy; 188 } 189 /* attempt to bind to prov port */ 190 (void)bindresvport_sa(*sockp, NULL); 191 /* the sockets rpc controls are non-blocking */ 192 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); 193 cu->cu_closeit = TRUE; 194 } else { 195 cu->cu_closeit = FALSE; 196 } 197 if (connect(*sockp, (struct sockaddr *)raddr, sizeof(*raddr)) < 0) 198 goto fooy; 199 cu->cu_llen = sizeof(cu->cu_laddr); 200 if (getsockname(*sockp, (struct sockaddr *)&cu->cu_laddr, &cu->cu_llen) < 0) 201 goto fooy; 202 203 cu->cu_sock = *sockp; 204 cl->cl_auth = authnone_create(); 205 return (cl); 206 fooy: 207 if (cu) 208 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); 209 if (cl) 210 mem_free((caddr_t)cl, sizeof(CLIENT)); 211 return ((CLIENT *)NULL); 212 } 213 214 CLIENT * 215 clntudp_create( 216 struct sockaddr_in *raddr, 217 rpcprog_t program, 218 rpcvers_t version, 219 struct timeval wait, 220 int *sockp) 221 { 222 223 return(clntudp_bufcreate(raddr, program, version, wait, sockp, 224 UDPMSGSIZE, UDPMSGSIZE)); 225 } 226 227 static enum clnt_stat 228 clntudp_call( 229 CLIENT *cl, /* client handle */ 230 rpcproc_t proc, /* procedure number */ 231 xdrproc_t xargs, /* xdr routine for args */ 232 void * argsp, /* pointer to args */ 233 xdrproc_t xresults, /* xdr routine for results */ 234 void * resultsp, /* pointer to results */ 235 struct timeval utimeout /* seconds to wait before 236 * giving up */ 237 ) 238 { 239 struct cu_data *cu = (struct cu_data *)cl->cl_private; 240 XDR *xdrs; 241 int outlen; 242 ssize_t inlen; 243 GETSOCKNAME_ARG3_TYPE fromlen; /* Assumes recvfrom uses same type */ 244 #ifdef FD_SETSIZE 245 fd_set readfds; 246 fd_set mask; 247 #else 248 int readfds; 249 int mask; 250 #endif /* def FD_SETSIZE */ 251 struct sockaddr_in from; 252 struct rpc_msg reply_msg; 253 XDR reply_xdrs; 254 struct timeval time_waited, seltimeout; 255 bool_t ok; 256 int nrefreshes = 2; /* number of times to refresh cred */ 257 struct timeval timeout; 258 long procl = proc; 259 260 if (cu->cu_total.tv_usec == -1) { 261 timeout = utimeout; /* use supplied timeout */ 262 } else { 263 timeout = cu->cu_total; /* use default timeout */ 264 } 265 266 time_waited.tv_sec = 0; 267 time_waited.tv_usec = 0; 268 call_again: 269 xdrs = &(cu->cu_outxdrs); 270 xdrs->x_op = XDR_ENCODE; 271 XDR_SETPOS(xdrs, cu->cu_xdrpos); 272 /* 273 * the transaction is the first thing in the out buffer 274 */ 275 (*(uint32_t *)(void *)(cu->cu_outbuf))++; 276 if ((! XDR_PUTLONG(xdrs, &procl)) || 277 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 278 (! AUTH_WRAP(cl->cl_auth, xdrs, xargs, argsp))) 279 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 280 outlen = (int)XDR_GETPOS(xdrs); 281 282 send_again: 283 if (send(cu->cu_sock, cu->cu_outbuf, (u_int)outlen, 0) != outlen) { 284 cu->cu_error.re_errno = errno; 285 return (cu->cu_error.re_status = RPC_CANTSEND); 286 } 287 288 /* 289 * Hack to provide rpc-based message passing 290 */ 291 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 292 return (cu->cu_error.re_status = RPC_TIMEDOUT); 293 } 294 /* 295 * sub-optimal code appears here because we have 296 * some clock time to spare while the packets are in flight. 297 * (We assume that this is actually only executed once.) 298 */ 299 reply_msg.acpted_rply.ar_verf = gssrpc__null_auth; 300 reply_msg.acpted_rply.ar_results.where = NULL; 301 reply_msg.acpted_rply.ar_results.proc = xdr_void; 302 #ifdef FD_SETSIZE 303 FD_ZERO(&mask); 304 FD_SET(cu->cu_sock, &mask); 305 #else 306 mask = 1 << cu->cu_sock; 307 #endif /* def FD_SETSIZE */ 308 for (;;) { 309 readfds = mask; 310 seltimeout = cu->cu_wait; 311 switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set *)NULL, 312 (fd_set *)NULL, &seltimeout)) { 313 314 case 0: 315 time_waited.tv_sec += cu->cu_wait.tv_sec; 316 time_waited.tv_usec += cu->cu_wait.tv_usec; 317 while (time_waited.tv_usec >= 1000000) { 318 time_waited.tv_sec++; 319 time_waited.tv_usec -= 1000000; 320 } 321 if ((time_waited.tv_sec < timeout.tv_sec) || 322 ((time_waited.tv_sec == timeout.tv_sec) && 323 (time_waited.tv_usec < timeout.tv_usec))) 324 goto send_again; 325 return (cu->cu_error.re_status = RPC_TIMEDOUT); 326 327 /* 328 * buggy in other cases because time_waited is not being 329 * updated. 330 */ 331 case -1: 332 if (errno == EINTR) 333 continue; 334 cu->cu_error.re_errno = errno; 335 return (cu->cu_error.re_status = RPC_CANTRECV); 336 } 337 do { 338 fromlen = sizeof(struct sockaddr); 339 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 340 cu->cu_recvsz, 0, 341 (struct sockaddr *)&from, &fromlen); 342 } while (inlen < 0 && errno == EINTR); 343 if (inlen < 0) { 344 if (errno == EWOULDBLOCK) 345 continue; 346 cu->cu_error.re_errno = errno; 347 return (cu->cu_error.re_status = RPC_CANTRECV); 348 } 349 if ((size_t)inlen < sizeof(uint32_t)) 350 continue; 351 /* see if reply transaction id matches sent id */ 352 if (*((uint32_t *)(void *)(cu->cu_inbuf)) != 353 *((uint32_t *)(void *)(cu->cu_outbuf))) 354 continue; 355 /* we now assume we have the proper reply */ 356 break; 357 } 358 359 /* 360 * now decode and validate the response 361 */ 362 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 363 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 364 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 365 if (ok) { 366 gssrpc__seterr_reply(&reply_msg, &(cu->cu_error)); 367 if (cu->cu_error.re_status == RPC_SUCCESS) { 368 if (! AUTH_VALIDATE(cl->cl_auth, 369 &reply_msg.acpted_rply.ar_verf)) { 370 cu->cu_error.re_status = RPC_AUTHERROR; 371 cu->cu_error.re_why = AUTH_INVALIDRESP; 372 } else if (! AUTH_UNWRAP(cl->cl_auth, &reply_xdrs, 373 xresults, resultsp)) { 374 if (cu->cu_error.re_status == RPC_SUCCESS) 375 cu->cu_error.re_status = RPC_CANTDECODERES; 376 } 377 } /* end successful completion */ 378 else { 379 /* maybe our credentials need to be refreshed ... */ 380 if (nrefreshes > 0 && 381 AUTH_REFRESH(cl->cl_auth, &reply_msg)) { 382 nrefreshes--; 383 goto call_again; 384 } 385 } /* end of unsuccessful completion */ 386 /* free verifier */ 387 if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && 388 (reply_msg.acpted_rply.ar_verf.oa_base != NULL)) { 389 xdrs->x_op = XDR_FREE; 390 (void)xdr_opaque_auth(xdrs, 391 &(reply_msg.acpted_rply.ar_verf)); 392 } 393 } /* end of valid reply message */ 394 else { 395 /* 396 * It's possible for xdr_replymsg() to fail partway 397 * through its attempt to decode the result from the 398 * server. If this happens, it will leave the reply 399 * structure partially populated with dynamically 400 * allocated memory. (This can happen if someone uses 401 * clntudp_bufcreate() to create a CLIENT handle and 402 * specifies a receive buffer size that is too small.) 403 * This memory must be free()ed to avoid a leak. 404 */ 405 enum xdr_op op = reply_xdrs.x_op; 406 reply_xdrs.x_op = XDR_FREE; 407 xdr_replymsg(&reply_xdrs, &reply_msg); 408 reply_xdrs.x_op = op; 409 cu->cu_error.re_status = RPC_CANTDECODERES; 410 } 411 return (cu->cu_error.re_status); 412 } 413 414 static void 415 clntudp_geterr( 416 CLIENT *cl, 417 struct rpc_err *errp) 418 { 419 struct cu_data *cu = (struct cu_data *)cl->cl_private; 420 421 *errp = cu->cu_error; 422 } 423 424 425 static bool_t 426 clntudp_freeres( 427 CLIENT *cl, 428 xdrproc_t xdr_res, 429 void *res_ptr) 430 { 431 struct cu_data *cu = cl->cl_private; 432 XDR *xdrs = &cu->cu_outxdrs; 433 434 xdrs->x_op = XDR_FREE; 435 return ((*xdr_res)(xdrs, res_ptr)); 436 } 437 438 439 /*ARGSUSED*/ 440 static void 441 clntudp_abort(CLIENT *h) 442 { 443 } 444 445 static bool_t 446 clntudp_control( 447 CLIENT *cl, 448 int request, 449 void *info) 450 { 451 struct cu_data *cu = cl->cl_private; 452 453 switch (request) { 454 case CLSET_TIMEOUT: 455 cu->cu_total = *(struct timeval *)info; 456 break; 457 case CLGET_TIMEOUT: 458 *(struct timeval *)info = cu->cu_total; 459 break; 460 case CLSET_RETRY_TIMEOUT: 461 cu->cu_wait = *(struct timeval *)info; 462 break; 463 case CLGET_RETRY_TIMEOUT: 464 *(struct timeval *)info = cu->cu_wait; 465 break; 466 case CLGET_SERVER_ADDR: 467 *(struct sockaddr_in *)info = cu->cu_raddr; 468 break; 469 case CLGET_LOCAL_ADDR: 470 *(struct sockaddr_in *)info = cu->cu_laddr; 471 break; 472 default: 473 return (FALSE); 474 } 475 return (TRUE); 476 } 477 478 static void 479 clntudp_destroy(CLIENT *cl) 480 { 481 struct cu_data *cu = (struct cu_data *)cl->cl_private; 482 483 if (cu->cu_closeit) 484 (void)closesocket(cu->cu_sock); 485 XDR_DESTROY(&(cu->cu_outxdrs)); 486 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); 487 mem_free((caddr_t)cl, sizeof(CLIENT)); 488 } 489