1 /* $OpenBSD: clnt_udp.c,v 1.41 2024/01/22 16:18:06 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 "clnt_udp.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 int 70 clntudp_bufcreate1(struct clntudp_bufcreate_args *args) 71 { 72 args->cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); 73 if (args->cl == NULL) { 74 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 75 rpc_createerr.cf_error.re_errno = errno; 76 return -1; 77 } 78 args->sendsz = ((args->sendsz + 3) / 4) * 4; 79 args->recvsz = ((args->recvsz + 3) / 4) * 4; 80 args->cu = (struct cu_data *)mem_alloc(sizeof(args->cu) + 81 args->sendsz + args->recvsz); 82 if (args->cu == NULL) { 83 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 84 rpc_createerr.cf_error.re_errno = errno; 85 return -1; 86 } 87 args->cu->cu_outbuf = &args->cu->cu_inbuf[args->recvsz]; 88 args->cl->cl_ops = &udp_ops; 89 args->cl->cl_private = (caddr_t)args->cu; 90 args->cu->cu_connected = 0; 91 args->cu->cu_rlen = sizeof (args->cu->cu_raddr); 92 args->cu->cu_wait = args->wait; 93 args->cu->cu_total.tv_sec = -1; 94 args->cu->cu_total.tv_usec = -1; 95 args->cu->cu_sendsz = args->sendsz; 96 args->cu->cu_recvsz = args->recvsz; 97 args->cu->cu_closeit = FALSE; 98 args->call_msg.rm_xid = arc4random(); 99 args->call_msg.rm_direction = CALL; 100 args->call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 101 args->call_msg.rm_call.cb_prog = args->program; 102 args->call_msg.rm_call.cb_vers = args->version; 103 return 0; 104 } 105 106 int 107 clntudp_bufcreate2(struct clntudp_bufcreate_args *args) 108 { 109 xdrmem_create(&(args->cu->cu_outxdrs), args->cu->cu_outbuf, 110 args->sendsz, XDR_ENCODE); 111 if (!xdr_callhdr(&(args->cu->cu_outxdrs), &args->call_msg)) 112 return -1; 113 args->cu->cu_xdrpos = XDR_GETPOS(&(args->cu->cu_outxdrs)); 114 args->cl->cl_auth = authnone_create(); 115 if (args->cl->cl_auth == NULL) { 116 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 117 rpc_createerr.cf_error.re_errno = errno; 118 return -1; 119 } 120 return 0; 121 } 122 123 /* 124 * Create a UDP based client handle. 125 * If *sockp<0, *sockp is set to a newly created UPD socket. (***) 126 * If raddr->sin_port is 0 a binder on the remote machine 127 * is consulted for the correct port number. (***) 128 * NB: It is the client's responsibility to close *sockp, unless 129 * clntudp_bufcreate() was called with *sockp = -1 (so it created 130 * the socket), and CLNT_DESTROY() is used. 131 * NB: The rpch->cl_auth is initialized to null authentication. 132 * Caller may wish to set this something more useful. 133 * 134 * wait is the amount of time used between retransmitting a call if 135 * no response has been heard; retransmission occurs until the actual 136 * rpc call times out. 137 * 138 * sendsz and recvsz are the maximum allowable packet sizes that can be 139 * sent and received. 140 * 141 * This is a reduced-functionality version of clntudp_bufcreate() that 142 * does not allocate socket or binding (***, above). 143 * The official function clntudp_bufcreate(), which does perform those 144 * two steps, is in clnt_udp_bufcreate.c. This split avoids pulling 145 * socket / portmap related code into programs only using getpwent / YP code. 146 */ 147 148 CLIENT * 149 clntudp_bufcreate_simple(struct sockaddr_in *raddr, u_long program, u_long version, 150 struct timeval wait, int *sockp, u_int sendsz, u_int recvsz) 151 { 152 struct clntudp_bufcreate_args args; 153 154 args.raddr = raddr; 155 args.program = program; 156 args.version = version; 157 args.wait = wait; 158 args.sockp = sockp; 159 args.sendsz = sendsz; 160 args.recvsz = recvsz; 161 args.cl = NULL; 162 args.cu = NULL; 163 164 if (clntudp_bufcreate1(&args) == -1) 165 goto fooy; 166 args.cu->cu_raddr = *raddr; 167 args.cu->cu_sock = *sockp; 168 if (clntudp_bufcreate2(&args) == -1) 169 goto fooy; 170 return (args.cl); 171 fooy: 172 if (args.cu) 173 mem_free((caddr_t)args.cu, 174 sizeof(*args.cu) + args.sendsz + args.recvsz); 175 if (args.cl) 176 mem_free((caddr_t)args.cl, sizeof(CLIENT)); 177 return (NULL); 178 } 179 180 static enum clnt_stat 181 clntudp_call(CLIENT *cl, /* client handle */ 182 u_long proc, /* procedure number */ 183 xdrproc_t xargs, /* xdr routine for args */ 184 caddr_t argsp, /* pointer to args */ 185 xdrproc_t xresults, /* xdr routine for results */ 186 caddr_t resultsp, /* pointer to results */ 187 struct timeval utimeout) /* seconds to wait before giving up */ 188 { 189 struct cu_data *cu = (struct cu_data *)cl->cl_private; 190 XDR *xdrs; 191 int outlen; 192 int inlen; 193 int ret; 194 socklen_t fromlen; 195 struct pollfd pfd[1]; 196 struct sockaddr_in from; 197 struct rpc_msg reply_msg; 198 XDR reply_xdrs; 199 struct timespec time_waited, start, after, duration, wait; 200 bool_t ok; 201 int nrefreshes = 2; /* number of times to refresh cred */ 202 struct timespec timeout; 203 204 if (cu->cu_total.tv_usec == -1) 205 TIMEVAL_TO_TIMESPEC(&utimeout, &timeout); /* use supplied timeout */ 206 else 207 TIMEVAL_TO_TIMESPEC(&cu->cu_total, &timeout); /* use default timeout */ 208 209 pfd[0].fd = cu->cu_sock; 210 pfd[0].events = POLLIN; 211 timespecclear(&time_waited); 212 TIMEVAL_TO_TIMESPEC(&cu->cu_wait, &wait); 213 call_again: 214 xdrs = &(cu->cu_outxdrs); 215 xdrs->x_op = XDR_ENCODE; 216 XDR_SETPOS(xdrs, cu->cu_xdrpos); 217 /* 218 * the transaction is the first thing in the out buffer 219 */ 220 (*(u_short *)(cu->cu_outbuf))++; 221 if (!XDR_PUTLONG(xdrs, (long *)&proc) || 222 !AUTH_MARSHALL(cl->cl_auth, xdrs) || 223 !(*xargs)(xdrs, argsp)) { 224 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 225 } 226 outlen = (int)XDR_GETPOS(xdrs); 227 228 send_again: 229 if (cu->cu_connected) 230 ret = send(cu->cu_sock, cu->cu_outbuf, outlen, 0); 231 else 232 ret = sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, 233 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen); 234 if (ret != outlen) { 235 cu->cu_error.re_errno = errno; 236 return (cu->cu_error.re_status = RPC_CANTSEND); 237 } 238 239 /* 240 * Hack to provide rpc-based message passing 241 */ 242 if (!timespecisset(&timeout)) 243 return (cu->cu_error.re_status = RPC_TIMEDOUT); 244 245 /* 246 * sub-optimal code appears here because we have 247 * some clock time to spare while the packets are in flight. 248 * (We assume that this is actually only executed once.) 249 */ 250 reply_msg.acpted_rply.ar_verf = _null_auth; 251 reply_msg.acpted_rply.ar_results.where = resultsp; 252 reply_msg.acpted_rply.ar_results.proc = xresults; 253 254 WRAP(clock_gettime)(CLOCK_MONOTONIC, &start); 255 for (;;) { 256 switch (ppoll(pfd, 1, &wait, NULL)) { 257 case 0: 258 timespecadd(&time_waited, &wait, &time_waited); 259 if (timespeccmp(&time_waited, &timeout, <)) 260 goto send_again; 261 return (cu->cu_error.re_status = RPC_TIMEDOUT); 262 case 1: 263 if (pfd[0].revents & POLLNVAL) 264 errno = EBADF; 265 else if (pfd[0].revents & POLLERR) 266 errno = EIO; 267 else 268 break; 269 /* FALLTHROUGH */ 270 case -1: 271 if (errno == EINTR) { 272 WRAP(clock_gettime)(CLOCK_MONOTONIC, &after); 273 timespecsub(&after, &start, &duration); 274 timespecadd(&time_waited, &duration, &time_waited); 275 if (timespeccmp(&time_waited, &timeout, <)) 276 continue; 277 return (cu->cu_error.re_status = RPC_TIMEDOUT); 278 } 279 cu->cu_error.re_errno = errno; 280 return (cu->cu_error.re_status = RPC_CANTRECV); 281 } 282 283 do { 284 fromlen = sizeof(struct sockaddr); 285 if (cu->cu_connected) { 286 inlen = recv(cu->cu_sock, cu->cu_inbuf, 287 (int) cu->cu_recvsz, 0); 288 } else { 289 290 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 291 (int) cu->cu_recvsz, 0, 292 (struct sockaddr *)&from, &fromlen); 293 } 294 } while (inlen == -1 && errno == EINTR); 295 if (inlen == -1) { 296 if (errno == EWOULDBLOCK) 297 continue; 298 cu->cu_error.re_errno = errno; 299 return (cu->cu_error.re_status = RPC_CANTRECV); 300 } 301 if (inlen < sizeof(u_int32_t)) 302 continue; 303 /* see if reply transaction id matches sent id */ 304 if (((struct rpc_msg *)(cu->cu_inbuf))->rm_xid != 305 ((struct rpc_msg *)(cu->cu_outbuf))->rm_xid) 306 continue; 307 /* we now assume we have the proper reply */ 308 break; 309 } 310 311 /* 312 * now decode and validate the response 313 */ 314 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 315 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 316 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 317 if (ok) { 318 #if 0 319 /* 320 * XXX Would like to check these, but call_msg is not 321 * around. 322 */ 323 if (reply_msg.rm_call.cb_prog != call_msg.rm_call.cb_prog || 324 reply_msg.rm_call.cb_vers != call_msg.rm_call.cb_vers || 325 reply_msg.rm_call.cb_proc != call_msg.rm_call.cb_proc) { 326 goto call_again; /* XXX spin? */ 327 } 328 #endif 329 330 _seterr_reply(&reply_msg, &(cu->cu_error)); 331 if (cu->cu_error.re_status == RPC_SUCCESS) { 332 if (!AUTH_VALIDATE(cl->cl_auth, 333 &reply_msg.acpted_rply.ar_verf)) { 334 cu->cu_error.re_status = RPC_AUTHERROR; 335 cu->cu_error.re_why = AUTH_INVALIDRESP; 336 } 337 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 338 xdrs->x_op = XDR_FREE; 339 (void)xdr_opaque_auth(xdrs, 340 &(reply_msg.acpted_rply.ar_verf)); 341 } 342 } else { 343 /* maybe our credentials need to be refreshed ... */ 344 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 345 nrefreshes--; 346 goto call_again; 347 } 348 } 349 } else { 350 /* xdr_replymsg() may have left some things allocated */ 351 int op = reply_xdrs.x_op; 352 reply_xdrs.x_op = XDR_FREE; 353 xdr_replymsg(&reply_xdrs, &reply_msg); 354 reply_xdrs.x_op = op; 355 cu->cu_error.re_status = RPC_CANTDECODERES; 356 } 357 358 return (cu->cu_error.re_status); 359 } 360 361 static void 362 clntudp_geterr(CLIENT *cl, struct rpc_err *errp) 363 { 364 struct cu_data *cu = (struct cu_data *)cl->cl_private; 365 366 *errp = cu->cu_error; 367 } 368 369 370 static bool_t 371 clntudp_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr) 372 { 373 struct cu_data *cu = (struct cu_data *)cl->cl_private; 374 XDR *xdrs = &(cu->cu_outxdrs); 375 376 xdrs->x_op = XDR_FREE; 377 return ((*xdr_res)(xdrs, res_ptr)); 378 } 379 380 static void 381 clntudp_abort(CLIENT *clnt) 382 { 383 } 384 385 static bool_t 386 clntudp_control(CLIENT *cl, u_int request, void *info) 387 { 388 struct cu_data *cu = (struct cu_data *)cl->cl_private; 389 390 switch (request) { 391 case CLSET_TIMEOUT: 392 cu->cu_total = *(struct timeval *)info; 393 break; 394 case CLGET_TIMEOUT: 395 *(struct timeval *)info = cu->cu_total; 396 break; 397 case CLSET_RETRY_TIMEOUT: 398 cu->cu_wait = *(struct timeval *)info; 399 break; 400 case CLGET_RETRY_TIMEOUT: 401 *(struct timeval *)info = cu->cu_wait; 402 break; 403 case CLGET_SERVER_ADDR: 404 *(struct sockaddr_in *)info = cu->cu_raddr; 405 break; 406 case CLSET_CONNECTED: 407 cu->cu_connected = *(int *)info; 408 break; 409 default: 410 return (FALSE); 411 } 412 return (TRUE); 413 } 414 415 static void 416 clntudp_destroy(CLIENT *cl) 417 { 418 struct cu_data *cu = (struct cu_data *)cl->cl_private; 419 420 if (cu->cu_closeit && cu->cu_sock != -1) { 421 (void)close(cu->cu_sock); 422 } 423 XDR_DESTROY(&(cu->cu_outxdrs)); 424 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); 425 mem_free((caddr_t)cl, sizeof(CLIENT)); 426 } 427