1 /* $OpenBSD: clnt_udp.c,v 1.39 2022/07/15 17:33:28 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2010, Oracle America, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following 14 * disclaimer in the documentation and/or other materials 15 * provided with the distribution. 16 * * Neither the name of the "Oracle America, Inc." nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * clnt_udp.c, Implements a UDP/IP based, client side RPC. 36 */ 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <fcntl.h> 43 #include <rpc/rpc.h> 44 #include <sys/socket.h> 45 #include <netdb.h> 46 #include <errno.h> 47 #include <rpc/pmap_clnt.h> 48 49 /* 50 * UDP bases client side rpc operations 51 */ 52 static enum clnt_stat clntudp_call(CLIENT *, u_long, xdrproc_t, caddr_t, 53 xdrproc_t, caddr_t, struct timeval); 54 static void clntudp_abort(CLIENT *); 55 static void clntudp_geterr(CLIENT *, struct rpc_err *); 56 static bool_t clntudp_freeres(CLIENT *, xdrproc_t, caddr_t); 57 static bool_t clntudp_control(CLIENT *, u_int, void *); 58 static void clntudp_destroy(CLIENT *); 59 60 static const 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_connected; /* use send() instead */ 77 int cu_rlen; 78 struct timeval cu_wait; 79 struct timeval cu_total; 80 struct rpc_err cu_error; 81 XDR cu_outxdrs; 82 u_int cu_xdrpos; 83 u_int cu_sendsz; 84 char *cu_outbuf; 85 u_int cu_recvsz; 86 char cu_inbuf[1]; 87 }; 88 89 /* 90 * Create a UDP based client handle. 91 * If *sockp<0, *sockp is set to a newly created UPD socket. 92 * If raddr->sin_port is 0 a binder on the remote machine 93 * is consulted for the correct port number. 94 * NB: It is the client's responsibility to close *sockp, unless 95 * clntudp_bufcreate() was called with *sockp = -1 (so it created 96 * the socket), and CLNT_DESTROY() is used. 97 * NB: The rpch->cl_auth is initialized to null authentication. 98 * Caller may wish to set this something more useful. 99 * 100 * wait is the amount of time used between retransmitting a call if 101 * no response has been heard; retransmission occurs until the actual 102 * rpc call times out. 103 * 104 * sendsz and recvsz are the maximum allowable packet sizes that can be 105 * sent and received. 106 */ 107 CLIENT * 108 clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version, 109 struct timeval wait, int *sockp, u_int sendsz, u_int recvsz) 110 { 111 CLIENT *cl; 112 struct cu_data *cu = NULL; 113 struct rpc_msg call_msg; 114 115 cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); 116 if (cl == NULL) { 117 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 118 rpc_createerr.cf_error.re_errno = errno; 119 goto fooy; 120 } 121 sendsz = ((sendsz + 3) / 4) * 4; 122 recvsz = ((recvsz + 3) / 4) * 4; 123 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); 124 if (cu == NULL) { 125 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 126 rpc_createerr.cf_error.re_errno = errno; 127 goto fooy; 128 } 129 cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 130 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_connected = 0; 143 cu->cu_rlen = sizeof (cu->cu_raddr); 144 cu->cu_wait = wait; 145 cu->cu_total.tv_sec = -1; 146 cu->cu_total.tv_usec = -1; 147 cu->cu_sendsz = sendsz; 148 cu->cu_recvsz = recvsz; 149 call_msg.rm_xid = arc4random(); 150 call_msg.rm_direction = CALL; 151 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 152 call_msg.rm_call.cb_prog = program; 153 call_msg.rm_call.cb_vers = version; 154 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, 155 sendsz, XDR_ENCODE); 156 if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { 157 goto fooy; 158 } 159 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 160 if (*sockp < 0) { 161 *sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 162 IPPROTO_UDP); 163 if (*sockp == -1) { 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 cu->cu_closeit = TRUE; 171 } else { 172 cu->cu_closeit = FALSE; 173 } 174 cu->cu_sock = *sockp; 175 cl->cl_auth = authnone_create(); 176 if (cl->cl_auth == NULL) { 177 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 178 rpc_createerr.cf_error.re_errno = errno; 179 goto fooy; 180 } 181 return (cl); 182 fooy: 183 if (cu) 184 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); 185 if (cl) 186 mem_free((caddr_t)cl, sizeof(CLIENT)); 187 return (NULL); 188 } 189 DEF_WEAK(clntudp_bufcreate); 190 191 CLIENT * 192 clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version, 193 struct timeval wait, int *sockp) 194 { 195 196 return(clntudp_bufcreate(raddr, program, version, wait, sockp, 197 UDPMSGSIZE, UDPMSGSIZE)); 198 } 199 DEF_WEAK(clntudp_create); 200 201 static enum clnt_stat 202 clntudp_call(CLIENT *cl, /* client handle */ 203 u_long proc, /* procedure number */ 204 xdrproc_t xargs, /* xdr routine for args */ 205 caddr_t argsp, /* pointer to args */ 206 xdrproc_t xresults, /* xdr routine for results */ 207 caddr_t resultsp, /* pointer to results */ 208 struct timeval utimeout) /* seconds to wait before giving up */ 209 { 210 struct cu_data *cu = (struct cu_data *)cl->cl_private; 211 XDR *xdrs; 212 int outlen; 213 int inlen; 214 int ret; 215 socklen_t fromlen; 216 struct pollfd pfd[1]; 217 struct sockaddr_in from; 218 struct rpc_msg reply_msg; 219 XDR reply_xdrs; 220 struct timespec time_waited, start, after, duration, wait; 221 bool_t ok; 222 int nrefreshes = 2; /* number of times to refresh cred */ 223 struct timespec timeout; 224 225 if (cu->cu_total.tv_usec == -1) 226 TIMEVAL_TO_TIMESPEC(&utimeout, &timeout); /* use supplied timeout */ 227 else 228 TIMEVAL_TO_TIMESPEC(&cu->cu_total, &timeout); /* use default timeout */ 229 230 pfd[0].fd = cu->cu_sock; 231 pfd[0].events = POLLIN; 232 timespecclear(&time_waited); 233 TIMEVAL_TO_TIMESPEC(&cu->cu_wait, &wait); 234 call_again: 235 xdrs = &(cu->cu_outxdrs); 236 xdrs->x_op = XDR_ENCODE; 237 XDR_SETPOS(xdrs, cu->cu_xdrpos); 238 /* 239 * the transaction is the first thing in the out buffer 240 */ 241 (*(u_short *)(cu->cu_outbuf))++; 242 if (!XDR_PUTLONG(xdrs, (long *)&proc) || 243 !AUTH_MARSHALL(cl->cl_auth, xdrs) || 244 !(*xargs)(xdrs, argsp)) { 245 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 246 } 247 outlen = (int)XDR_GETPOS(xdrs); 248 249 send_again: 250 if (cu->cu_connected) 251 ret = send(cu->cu_sock, cu->cu_outbuf, outlen, 0); 252 else 253 ret = sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, 254 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen); 255 if (ret != outlen) { 256 cu->cu_error.re_errno = errno; 257 return (cu->cu_error.re_status = RPC_CANTSEND); 258 } 259 260 /* 261 * Hack to provide rpc-based message passing 262 */ 263 if (!timespecisset(&timeout)) 264 return (cu->cu_error.re_status = RPC_TIMEDOUT); 265 266 /* 267 * sub-optimal code appears here because we have 268 * some clock time to spare while the packets are in flight. 269 * (We assume that this is actually only executed once.) 270 */ 271 reply_msg.acpted_rply.ar_verf = _null_auth; 272 reply_msg.acpted_rply.ar_results.where = resultsp; 273 reply_msg.acpted_rply.ar_results.proc = xresults; 274 275 WRAP(clock_gettime)(CLOCK_MONOTONIC, &start); 276 for (;;) { 277 switch (ppoll(pfd, 1, &wait, NULL)) { 278 case 0: 279 timespecadd(&time_waited, &wait, &time_waited); 280 if (timespeccmp(&time_waited, &timeout, <)) 281 goto send_again; 282 return (cu->cu_error.re_status = RPC_TIMEDOUT); 283 case 1: 284 if (pfd[0].revents & POLLNVAL) 285 errno = EBADF; 286 else if (pfd[0].revents & POLLERR) 287 errno = EIO; 288 else 289 break; 290 /* FALLTHROUGH */ 291 case -1: 292 if (errno == EINTR) { 293 WRAP(clock_gettime)(CLOCK_MONOTONIC, &after); 294 timespecsub(&after, &start, &duration); 295 timespecadd(&time_waited, &duration, &time_waited); 296 if (timespeccmp(&time_waited, &timeout, <)) 297 continue; 298 return (cu->cu_error.re_status = RPC_TIMEDOUT); 299 } 300 cu->cu_error.re_errno = errno; 301 return (cu->cu_error.re_status = RPC_CANTRECV); 302 } 303 304 do { 305 fromlen = sizeof(struct sockaddr); 306 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 307 (int) cu->cu_recvsz, 0, 308 (struct sockaddr *)&from, &fromlen); 309 } while (inlen == -1 && errno == EINTR); 310 if (inlen == -1) { 311 if (errno == EWOULDBLOCK) 312 continue; 313 cu->cu_error.re_errno = errno; 314 return (cu->cu_error.re_status = RPC_CANTRECV); 315 } 316 if (inlen < sizeof(u_int32_t)) 317 continue; 318 /* see if reply transaction id matches sent id */ 319 if (((struct rpc_msg *)(cu->cu_inbuf))->rm_xid != 320 ((struct rpc_msg *)(cu->cu_outbuf))->rm_xid) 321 continue; 322 /* we now assume we have the proper reply */ 323 break; 324 } 325 326 /* 327 * now decode and validate the response 328 */ 329 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 330 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 331 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 332 if (ok) { 333 #if 0 334 /* 335 * XXX Would like to check these, but call_msg is not 336 * around. 337 */ 338 if (reply_msg.rm_call.cb_prog != call_msg.rm_call.cb_prog || 339 reply_msg.rm_call.cb_vers != call_msg.rm_call.cb_vers || 340 reply_msg.rm_call.cb_proc != call_msg.rm_call.cb_proc) { 341 goto call_again; /* XXX spin? */ 342 } 343 #endif 344 345 _seterr_reply(&reply_msg, &(cu->cu_error)); 346 if (cu->cu_error.re_status == RPC_SUCCESS) { 347 if (!AUTH_VALIDATE(cl->cl_auth, 348 &reply_msg.acpted_rply.ar_verf)) { 349 cu->cu_error.re_status = RPC_AUTHERROR; 350 cu->cu_error.re_why = AUTH_INVALIDRESP; 351 } 352 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 353 xdrs->x_op = XDR_FREE; 354 (void)xdr_opaque_auth(xdrs, 355 &(reply_msg.acpted_rply.ar_verf)); 356 } 357 } else { 358 /* maybe our credentials need to be refreshed ... */ 359 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 360 nrefreshes--; 361 goto call_again; 362 } 363 } 364 } else { 365 /* xdr_replymsg() may have left some things allocated */ 366 int op = reply_xdrs.x_op; 367 reply_xdrs.x_op = XDR_FREE; 368 xdr_replymsg(&reply_xdrs, &reply_msg); 369 reply_xdrs.x_op = op; 370 cu->cu_error.re_status = RPC_CANTDECODERES; 371 } 372 373 return (cu->cu_error.re_status); 374 } 375 376 static void 377 clntudp_geterr(CLIENT *cl, struct rpc_err *errp) 378 { 379 struct cu_data *cu = (struct cu_data *)cl->cl_private; 380 381 *errp = cu->cu_error; 382 } 383 384 385 static bool_t 386 clntudp_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr) 387 { 388 struct cu_data *cu = (struct cu_data *)cl->cl_private; 389 XDR *xdrs = &(cu->cu_outxdrs); 390 391 xdrs->x_op = XDR_FREE; 392 return ((*xdr_res)(xdrs, res_ptr)); 393 } 394 395 static void 396 clntudp_abort(CLIENT *clnt) 397 { 398 } 399 400 static bool_t 401 clntudp_control(CLIENT *cl, u_int request, void *info) 402 { 403 struct cu_data *cu = (struct cu_data *)cl->cl_private; 404 405 switch (request) { 406 case CLSET_TIMEOUT: 407 cu->cu_total = *(struct timeval *)info; 408 break; 409 case CLGET_TIMEOUT: 410 *(struct timeval *)info = cu->cu_total; 411 break; 412 case CLSET_RETRY_TIMEOUT: 413 cu->cu_wait = *(struct timeval *)info; 414 break; 415 case CLGET_RETRY_TIMEOUT: 416 *(struct timeval *)info = cu->cu_wait; 417 break; 418 case CLGET_SERVER_ADDR: 419 *(struct sockaddr_in *)info = cu->cu_raddr; 420 break; 421 case CLSET_CONNECTED: 422 cu->cu_connected = *(int *)info; 423 break; 424 default: 425 return (FALSE); 426 } 427 return (TRUE); 428 } 429 430 static void 431 clntudp_destroy(CLIENT *cl) 432 { 433 struct cu_data *cu = (struct cu_data *)cl->cl_private; 434 435 if (cu->cu_closeit && cu->cu_sock != -1) { 436 (void)close(cu->cu_sock); 437 } 438 XDR_DESTROY(&(cu->cu_outxdrs)); 439 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); 440 mem_free((caddr_t)cl, sizeof(CLIENT)); 441 } 442