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