1 /* 2 * Copyright (c) 1996, 1997 3 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Bill Paul. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)from: clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro 33 * @(#)from: clnt_udp.c 2.2 88/08/01 4.0 RPCSRC 34 * $FreeBSD: src/usr.sbin/ypbind/yp_ping.c,v 1.6.2.1 2002/02/15 00:46:59 des Exp $ 35 * $DragonFly: src/usr.sbin/ypbind/yp_ping.c,v 1.6 2004/12/18 22:48:14 swildner Exp $ 36 */ 37 38 /* 39 * What follows is a special version of clntudp_call() that has been 40 * hacked to send requests and receive replies asynchronously. Similar 41 * magic is used inside rpc.nisd(8) for the special non-blocking, 42 * non-fork()ing, non-threading callback support. 43 */ 44 45 /* 46 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 47 * unrestricted use provided that this legend is included on all tape 48 * media and as a part of the software program in whole or part. Users 49 * may copy or modify Sun RPC without charge, but are not authorized 50 * to license or distribute it to anyone else except as part of a product or 51 * program developed by the user. 52 * 53 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 54 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 55 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 56 * 57 * Sun RPC is provided with no support and without any obligation on the 58 * part of Sun Microsystems, Inc. to assist in its use, correction, 59 * modification or enhancement. 60 * 61 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 62 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 63 * OR ANY PART THEREOF. 64 * 65 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 66 * or profits or other special, indirect and consequential damages, even if 67 * Sun has been advised of the possibility of such damages. 68 * 69 * Sun Microsystems, Inc. 70 * 2550 Garcia Avenue 71 * Mountain View, California 94043 72 */ 73 74 /* 75 * clnt_udp.c, Implements a UDP/IP based, client side RPC. 76 * 77 * Copyright (C) 1984, Sun Microsystems, Inc. 78 */ 79 80 #include <errno.h> 81 #include <netdb.h> 82 #include <stdio.h> 83 #include <stdlib.h> 84 #include <string.h> 85 #include <unistd.h> 86 #include <rpc/rpc.h> 87 #include <rpc/pmap_clnt.h> 88 #include <rpc/pmap_prot.h> 89 #include <rpcsvc/yp.h> 90 #include <sys/socket.h> 91 #include <sys/ioctl.h> 92 #include <net/if.h> 93 #include "yp_ping.h" 94 95 #ifndef timeradd 96 #ifndef _KERNEL /* use timevaladd/timevalsub in kernel */ 97 /* NetBSD/OpenBSD compatible interfaces */ 98 #define timeradd(tvp, uvp, vvp) \ 99 do { \ 100 (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ 101 (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ 102 if ((vvp)->tv_usec >= 1000000) { \ 103 (vvp)->tv_sec++; \ 104 (vvp)->tv_usec -= 1000000; \ 105 } \ 106 } while (0) 107 #define timersub(tvp, uvp, vvp) \ 108 do { \ 109 (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ 110 (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ 111 if ((vvp)->tv_usec < 0) { \ 112 (vvp)->tv_sec--; \ 113 (vvp)->tv_usec += 1000000; \ 114 } \ 115 } while (0) 116 #endif 117 #endif 118 119 /* 120 * Private data kept per client handle 121 */ 122 struct cu_data { 123 int cu_sock; 124 bool_t cu_closeit; 125 struct sockaddr_in cu_raddr; 126 int cu_rlen; 127 struct timeval cu_wait; 128 struct timeval cu_total; 129 struct rpc_err cu_error; 130 XDR cu_outxdrs; 131 u_int cu_xdrpos; 132 u_int cu_sendsz; 133 char *cu_outbuf; 134 u_int cu_recvsz; 135 char cu_inbuf[1]; 136 }; 137 138 static enum clnt_stat 139 clntudp_a_call(CLIENT *cl, /* client handle */ 140 u_long proc, /* procedure number */ 141 xdrproc_t xargs, /* xdr routine for args */ 142 caddr_t argsp, /* pointer to args */ 143 xdrproc_t xresults, /* xdr routine for results */ 144 caddr_t resultsp, /* pointer to results */ 145 struct timeval utimeout) /* seconds to wait before giving up */ 146 { 147 struct cu_data *cu = (struct cu_data *)cl->cl_private; 148 XDR *xdrs; 149 int outlen = 0; 150 int inlen; 151 int fromlen; 152 fd_set *fds, readfds; 153 struct sockaddr_in from; 154 struct rpc_msg reply_msg; 155 XDR reply_xdrs; 156 struct timeval time_waited, start, after, tmp1, tmp2, tv; 157 bool_t ok; 158 int nrefreshes = 2; /* number of times to refresh cred */ 159 struct timeval timeout; 160 161 if (cu->cu_total.tv_usec == -1) 162 timeout = utimeout; /* use supplied timeout */ 163 else 164 timeout = cu->cu_total; /* use default timeout */ 165 166 if (cu->cu_sock + 1 > FD_SETSIZE) { 167 int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask); 168 fds = (fd_set *)malloc(bytes); 169 if (fds == NULL) 170 return (cu->cu_error.re_status = RPC_CANTSEND); 171 memset(fds, 0, bytes); 172 } else { 173 fds = &readfds; 174 FD_ZERO(fds); 175 } 176 177 timerclear(&time_waited); 178 179 call_again: 180 xdrs = &(cu->cu_outxdrs); 181 if (xargs == NULL) 182 goto get_reply; 183 xdrs->x_op = XDR_ENCODE; 184 XDR_SETPOS(xdrs, cu->cu_xdrpos); 185 /* 186 * the transaction is the first thing in the out buffer 187 */ 188 (*(u_short *)(cu->cu_outbuf))++; 189 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) || 190 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 191 (! (*xargs)(xdrs, argsp))) { 192 if (fds != &readfds) 193 free(fds); 194 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 195 } 196 outlen = (int)XDR_GETPOS(xdrs); 197 198 send_again: 199 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, 200 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) { 201 cu->cu_error.re_errno = errno; 202 if (fds != &readfds) 203 free(fds); 204 return (cu->cu_error.re_status = RPC_CANTSEND); 205 } 206 207 /* 208 * Hack to provide rpc-based message passing 209 */ 210 if (!timerisset(&timeout)) { 211 if (fds != &readfds) 212 free(fds); 213 return (cu->cu_error.re_status = RPC_TIMEDOUT); 214 } 215 216 get_reply: 217 218 /* 219 * sub-optimal code appears here because we have 220 * some clock time to spare while the packets are in flight. 221 * (We assume that this is actually only executed once.) 222 */ 223 reply_msg.acpted_rply.ar_verf = _null_auth; 224 reply_msg.acpted_rply.ar_results.where = resultsp; 225 reply_msg.acpted_rply.ar_results.proc = xresults; 226 227 gettimeofday(&start, NULL); 228 for (;;) { 229 /* XXX we know the other bits are still clear */ 230 FD_SET(cu->cu_sock, fds); 231 tv = cu->cu_wait; 232 switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) { 233 234 case 0: 235 timeradd(&time_waited, &cu->cu_wait, &tmp1); 236 time_waited = tmp1; 237 if (timercmp(&time_waited, &timeout, <)) 238 goto send_again; 239 if (fds != &readfds) 240 free(fds); 241 return (cu->cu_error.re_status = RPC_TIMEDOUT); 242 243 case -1: 244 if (errno == EINTR) { 245 gettimeofday(&after, NULL); 246 timersub(&after, &start, &tmp1); 247 timeradd(&time_waited, &tmp1, &tmp2); 248 time_waited = tmp2; 249 if (timercmp(&time_waited, &timeout, <)) 250 continue; 251 if (fds != &readfds) 252 free(fds); 253 return (cu->cu_error.re_status = RPC_TIMEDOUT); 254 } 255 cu->cu_error.re_errno = errno; 256 if (fds != &readfds) 257 free(fds); 258 return (cu->cu_error.re_status = RPC_CANTRECV); 259 } 260 261 do { 262 fromlen = sizeof(struct sockaddr); 263 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 264 (int) cu->cu_recvsz, 0, 265 (struct sockaddr *)&from, &fromlen); 266 } while (inlen < 0 && errno == EINTR); 267 if (inlen < 0) { 268 if (errno == EWOULDBLOCK) 269 continue; 270 cu->cu_error.re_errno = errno; 271 if (fds != &readfds) 272 free(fds); 273 return (cu->cu_error.re_status = RPC_CANTRECV); 274 } 275 if (inlen < sizeof(u_int32_t)) 276 continue; 277 #ifdef dont_check_xid 278 /* see if reply transaction id matches sent id */ 279 if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf))) 280 continue; 281 #endif 282 /* we now assume we have the proper reply */ 283 break; 284 } 285 286 /* 287 * now decode and validate the response 288 */ 289 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 290 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 291 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 292 if (ok) { 293 _seterr_reply(&reply_msg, &(cu->cu_error)); 294 if (cu->cu_error.re_status == RPC_SUCCESS) { 295 if (! AUTH_VALIDATE(cl->cl_auth, 296 &reply_msg.acpted_rply.ar_verf)) { 297 cu->cu_error.re_status = RPC_AUTHERROR; 298 cu->cu_error.re_why = AUTH_INVALIDRESP; 299 } 300 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 301 xdrs->x_op = XDR_FREE; 302 xdr_opaque_auth(xdrs, 303 &(reply_msg.acpted_rply.ar_verf)); 304 } 305 } /* end successful completion */ 306 else { 307 /* maybe our credentials need to be refreshed ... */ 308 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 309 nrefreshes--; 310 goto call_again; 311 } 312 } /* end of unsuccessful completion */ 313 } /* end of valid reply message */ 314 else { 315 cu->cu_error.re_status = RPC_CANTDECODERES; 316 } 317 if (fds != &readfds) 318 free(fds); 319 return (cu->cu_error.re_status); 320 } 321 322 323 /* 324 * pmap_getport.c 325 * Client interface to pmap rpc service. 326 * 327 * Copyright (C) 1984, Sun Microsystems, Inc. 328 */ 329 330 331 static struct timeval timeout = { 1, 0 }; 332 static struct timeval tottimeout = { 1, 0 }; 333 334 /* 335 * Find the mapped port for program,version. 336 * Calls the pmap service remotely to do the lookup. 337 * Returns 0 if no map exists. 338 */ 339 static u_short 340 __pmap_getport(struct sockaddr_in *address, 341 u_long program, u_long version, u_int protocol) 342 { 343 u_short port = 0; 344 int sock = -1; 345 CLIENT *client; 346 struct pmap parms; 347 348 address->sin_port = htons(PMAPPORT); 349 350 client = clntudp_bufcreate(address, PMAPPROG, 351 PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); 352 if (client != (CLIENT *)NULL) { 353 parms.pm_prog = program; 354 parms.pm_vers = version; 355 parms.pm_prot = protocol; 356 parms.pm_port = 0; /* not needed or used */ 357 if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms, 358 xdr_u_short, &port, tottimeout) != RPC_SUCCESS){ 359 rpc_createerr.cf_stat = RPC_PMAPFAILURE; 360 clnt_geterr(client, &rpc_createerr.cf_error); 361 } else if (port == 0) { 362 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 363 } 364 CLNT_DESTROY(client); 365 } 366 if (sock != -1) 367 close(sock); 368 address->sin_port = 0; 369 return (port); 370 } 371 372 /* 373 * Transmit to YPPROC_DOMAIN_NONACK, return immediately. 374 */ 375 static bool_t * 376 ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt) 377 { 378 static bool_t clnt_res; 379 struct timeval TIMEOUT = { 0, 0 }; 380 381 memset((char *)&clnt_res, 0, sizeof (clnt_res)); 382 if (clnt_call(clnt, YPPROC_DOMAIN_NONACK, 383 (xdrproc_t) xdr_domainname, (caddr_t) argp, 384 (xdrproc_t) xdr_bool, (caddr_t) &clnt_res, 385 TIMEOUT) != RPC_SUCCESS) { 386 return (NULL); 387 } 388 return (&clnt_res); 389 } 390 391 /* 392 * Receive response from YPPROC_DOMAIN_NONACK asynchronously. 393 */ 394 static bool_t * 395 ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt) 396 { 397 static bool_t clnt_res; 398 struct timeval TIMEOUT = { 0, 0 }; 399 400 memset((char *)&clnt_res, 0, sizeof (clnt_res)); 401 if (clnt_call(clnt, YPPROC_DOMAIN_NONACK, 402 (xdrproc_t) NULL, (caddr_t) argp, 403 (xdrproc_t) xdr_bool, (caddr_t) &clnt_res, 404 TIMEOUT) != RPC_SUCCESS) { 405 return (NULL); 406 } 407 return (&clnt_res); 408 } 409 410 /* 411 * "We have the machine that goes 'ping!'" -- Monty Python 412 * 413 * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures 414 * of the NIS servers listed in restricted_addrs structure. 415 * Whoever replies the fastest becomes our chosen server. 416 * 417 * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast() 418 * for this, but that has the following problems: 419 * - We only get the address of the machine that replied in the 420 * 'eachresult' callback, and on multi-homed machines this can 421 * lead to confusion. 422 * - clnt_broadcast() only transmits to local networks, whereas with 423 * NIS+ you can have a perfectly good server located anywhere on or 424 * off the local network. 425 * - clnt_broadcast() blocks for an arbitrary amount of time which the 426 * caller can't control -- we want to avoid that. 427 * 428 * Also note that this has nothing to do with the NIS_PING procedure used 429 * for replica updates. 430 */ 431 432 struct ping_req { 433 struct sockaddr_in sin; 434 unsigned long xid; 435 }; 436 437 int __yp_ping(struct in_addr *restricted_addrs, int cnt, 438 char *dom, short int *port) 439 { 440 struct timeval tv = { 5, 0 }; 441 struct ping_req **reqs; 442 unsigned long i; 443 struct sockaddr_in sin, *any = NULL; 444 int winner = -1; 445 time_t xid_seed, xid_lookup; 446 int sock, dontblock = 1; 447 CLIENT *clnt; 448 char *foo = dom; 449 struct cu_data *cu; 450 enum clnt_stat (*oldfunc)(); 451 int validsrvs = 0; 452 453 /* Set up handles. */ 454 reqs = calloc(1, sizeof(struct ping_req *) * cnt); 455 xid_seed = time(NULL) ^ getpid(); 456 457 for (i = 0; i < cnt; i++) { 458 bzero((char *)&sin, sizeof(sin)); 459 sin.sin_family = AF_INET; 460 bcopy((char *)&restricted_addrs[i], 461 (char *)&sin.sin_addr, sizeof(struct in_addr)); 462 sin.sin_port = htons(__pmap_getport(&sin, YPPROG, 463 YPVERS, IPPROTO_UDP)); 464 if (sin.sin_port == 0) 465 continue; 466 reqs[i] = calloc(1, sizeof(struct ping_req)); 467 bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin)); 468 any = &reqs[i]->sin; 469 reqs[i]->xid = xid_seed; 470 xid_seed++; 471 validsrvs++; 472 } 473 474 /* Make sure at least one server was assigned */ 475 if (!validsrvs) { 476 free(reqs); 477 return(-1); 478 } 479 480 /* Create RPC handle */ 481 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 482 clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock); 483 if (clnt == NULL) { 484 close(sock); 485 for (i = 0; i < cnt; i++) 486 if (reqs[i] != NULL) 487 free(reqs[i]); 488 free(reqs); 489 return(-1); 490 } 491 clnt->cl_auth = authunix_create_default(); 492 cu = (struct cu_data *)clnt->cl_private; 493 tv.tv_sec = 0; 494 clnt_control(clnt, CLSET_TIMEOUT, &tv); 495 ioctl(sock, FIONBIO, &dontblock); 496 oldfunc = clnt->cl_ops->cl_call; 497 clnt->cl_ops->cl_call = clntudp_a_call; 498 499 /* Transmit */ 500 for (i = 0; i < cnt; i++) { 501 if (reqs[i] != NULL) { 502 /* subtract one; clntudp_call() will increment */ 503 *((u_int32_t *)(cu->cu_outbuf)) = reqs[i]->xid - 1; 504 bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr, 505 sizeof(struct sockaddr_in)); 506 ypproc_domain_nonack_2_send(&foo, clnt); 507 } 508 } 509 510 /* Receive reply */ 511 ypproc_domain_nonack_2_recv(&foo, clnt); 512 513 /* Got a winner -- look him up. */ 514 xid_lookup = *((u_int32_t *)(cu->cu_inbuf)); 515 for (i = 0; i < cnt; i++) { 516 if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) { 517 winner = i; 518 *port = reqs[i]->sin.sin_port; 519 } 520 } 521 522 /* Shut everything down */ 523 clnt->cl_ops->cl_call = oldfunc; 524 auth_destroy(clnt->cl_auth); 525 clnt_destroy(clnt); 526 close(sock); 527 528 for (i = 0; i < cnt; i++) 529 if (reqs[i] != NULL) 530 free(reqs[i]); 531 free(reqs); 532 533 return(winner); 534 } 535