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