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.3 2003/11/22 19:30:57 asmodai 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(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 140 register CLIENT *cl; /* client handle */ 141 u_long proc; /* procedure number */ 142 xdrproc_t xargs; /* xdr routine for args */ 143 caddr_t argsp; /* pointer to args */ 144 xdrproc_t xresults; /* xdr routine for results */ 145 caddr_t resultsp; /* pointer to results */ 146 struct timeval utimeout; /* seconds to wait before giving up */ 147 { 148 register struct cu_data *cu = (struct cu_data *)cl->cl_private; 149 register XDR *xdrs; 150 register int outlen = 0; 151 register int inlen; 152 int fromlen; 153 fd_set *fds, readfds; 154 struct sockaddr_in from; 155 struct rpc_msg reply_msg; 156 XDR reply_xdrs; 157 struct timeval time_waited, start, after, tmp1, tmp2, tv; 158 bool_t ok; 159 int nrefreshes = 2; /* number of times to refresh cred */ 160 struct timeval timeout; 161 162 if (cu->cu_total.tv_usec == -1) 163 timeout = utimeout; /* use supplied timeout */ 164 else 165 timeout = cu->cu_total; /* use default timeout */ 166 167 if (cu->cu_sock + 1 > FD_SETSIZE) { 168 int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask); 169 fds = (fd_set *)malloc(bytes); 170 if (fds == NULL) 171 return (cu->cu_error.re_status = RPC_CANTSEND); 172 memset(fds, 0, bytes); 173 } else { 174 fds = &readfds; 175 FD_ZERO(fds); 176 } 177 178 timerclear(&time_waited); 179 180 call_again: 181 xdrs = &(cu->cu_outxdrs); 182 if (xargs == NULL) 183 goto get_reply; 184 xdrs->x_op = XDR_ENCODE; 185 XDR_SETPOS(xdrs, cu->cu_xdrpos); 186 /* 187 * the transaction is the first thing in the out buffer 188 */ 189 (*(u_short *)(cu->cu_outbuf))++; 190 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) || 191 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 192 (! (*xargs)(xdrs, argsp))) { 193 if (fds != &readfds) 194 free(fds); 195 return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 196 } 197 outlen = (int)XDR_GETPOS(xdrs); 198 199 send_again: 200 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, 201 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) { 202 cu->cu_error.re_errno = errno; 203 if (fds != &readfds) 204 free(fds); 205 return (cu->cu_error.re_status = RPC_CANTSEND); 206 } 207 208 /* 209 * Hack to provide rpc-based message passing 210 */ 211 if (!timerisset(&timeout)) { 212 if (fds != &readfds) 213 free(fds); 214 return (cu->cu_error.re_status = RPC_TIMEDOUT); 215 } 216 217 get_reply: 218 219 /* 220 * sub-optimal code appears here because we have 221 * some clock time to spare while the packets are in flight. 222 * (We assume that this is actually only executed once.) 223 */ 224 reply_msg.acpted_rply.ar_verf = _null_auth; 225 reply_msg.acpted_rply.ar_results.where = resultsp; 226 reply_msg.acpted_rply.ar_results.proc = xresults; 227 228 gettimeofday(&start, NULL); 229 for (;;) { 230 /* XXX we know the other bits are still clear */ 231 FD_SET(cu->cu_sock, fds); 232 tv = cu->cu_wait; 233 switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) { 234 235 case 0: 236 timeradd(&time_waited, &cu->cu_wait, &tmp1); 237 time_waited = tmp1; 238 if (timercmp(&time_waited, &timeout, <)) 239 goto send_again; 240 if (fds != &readfds) 241 free(fds); 242 return (cu->cu_error.re_status = RPC_TIMEDOUT); 243 244 case -1: 245 if (errno == EINTR) { 246 gettimeofday(&after, NULL); 247 timersub(&after, &start, &tmp1); 248 timeradd(&time_waited, &tmp1, &tmp2); 249 time_waited = tmp2; 250 if (timercmp(&time_waited, &timeout, <)) 251 continue; 252 if (fds != &readfds) 253 free(fds); 254 return (cu->cu_error.re_status = RPC_TIMEDOUT); 255 } 256 cu->cu_error.re_errno = errno; 257 if (fds != &readfds) 258 free(fds); 259 return (cu->cu_error.re_status = RPC_CANTRECV); 260 } 261 262 do { 263 fromlen = sizeof(struct sockaddr); 264 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, 265 (int) cu->cu_recvsz, 0, 266 (struct sockaddr *)&from, &fromlen); 267 } while (inlen < 0 && errno == EINTR); 268 if (inlen < 0) { 269 if (errno == EWOULDBLOCK) 270 continue; 271 cu->cu_error.re_errno = errno; 272 if (fds != &readfds) 273 free(fds); 274 return (cu->cu_error.re_status = RPC_CANTRECV); 275 } 276 if (inlen < sizeof(u_int32_t)) 277 continue; 278 #ifdef dont_check_xid 279 /* see if reply transaction id matches sent id */ 280 if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf))) 281 continue; 282 #endif 283 /* we now assume we have the proper reply */ 284 break; 285 } 286 287 /* 288 * now decode and validate the response 289 */ 290 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 291 ok = xdr_replymsg(&reply_xdrs, &reply_msg); 292 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 293 if (ok) { 294 _seterr_reply(&reply_msg, &(cu->cu_error)); 295 if (cu->cu_error.re_status == RPC_SUCCESS) { 296 if (! AUTH_VALIDATE(cl->cl_auth, 297 &reply_msg.acpted_rply.ar_verf)) { 298 cu->cu_error.re_status = RPC_AUTHERROR; 299 cu->cu_error.re_why = AUTH_INVALIDRESP; 300 } 301 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 302 xdrs->x_op = XDR_FREE; 303 (void)xdr_opaque_auth(xdrs, 304 &(reply_msg.acpted_rply.ar_verf)); 305 } 306 } /* end successful completion */ 307 else { 308 /* maybe our credentials need to be refreshed ... */ 309 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { 310 nrefreshes--; 311 goto call_again; 312 } 313 } /* end of unsuccessful completion */ 314 } /* end of valid reply message */ 315 else { 316 cu->cu_error.re_status = RPC_CANTDECODERES; 317 } 318 if (fds != &readfds) 319 free(fds); 320 return (cu->cu_error.re_status); 321 } 322 323 324 /* 325 * pmap_getport.c 326 * Client interface to pmap rpc service. 327 * 328 * Copyright (C) 1984, Sun Microsystems, Inc. 329 */ 330 331 332 static struct timeval timeout = { 1, 0 }; 333 static struct timeval tottimeout = { 1, 0 }; 334 335 /* 336 * Find the mapped port for program,version. 337 * Calls the pmap service remotely to do the lookup. 338 * Returns 0 if no map exists. 339 */ 340 static u_short 341 __pmap_getport(address, program, version, protocol) 342 struct sockaddr_in *address; 343 u_long program; 344 u_long version; 345 u_int protocol; 346 { 347 u_short port = 0; 348 int sock = -1; 349 register CLIENT *client; 350 struct pmap parms; 351 352 address->sin_port = htons(PMAPPORT); 353 354 client = clntudp_bufcreate(address, PMAPPROG, 355 PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); 356 if (client != (CLIENT *)NULL) { 357 parms.pm_prog = program; 358 parms.pm_vers = version; 359 parms.pm_prot = protocol; 360 parms.pm_port = 0; /* not needed or used */ 361 if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms, 362 xdr_u_short, &port, tottimeout) != RPC_SUCCESS){ 363 rpc_createerr.cf_stat = RPC_PMAPFAILURE; 364 clnt_geterr(client, &rpc_createerr.cf_error); 365 } else if (port == 0) { 366 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 367 } 368 CLNT_DESTROY(client); 369 } 370 if (sock != -1) 371 (void)close(sock); 372 address->sin_port = 0; 373 return (port); 374 } 375 376 /* 377 * Transmit to YPPROC_DOMAIN_NONACK, return immediately. 378 */ 379 static bool_t * 380 ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt) 381 { 382 static bool_t clnt_res; 383 struct timeval TIMEOUT = { 0, 0 }; 384 385 memset((char *)&clnt_res, 0, sizeof (clnt_res)); 386 if (clnt_call(clnt, YPPROC_DOMAIN_NONACK, 387 (xdrproc_t) xdr_domainname, (caddr_t) argp, 388 (xdrproc_t) xdr_bool, (caddr_t) &clnt_res, 389 TIMEOUT) != RPC_SUCCESS) { 390 return (NULL); 391 } 392 return (&clnt_res); 393 } 394 395 /* 396 * Receive response from YPPROC_DOMAIN_NONACK asynchronously. 397 */ 398 static bool_t * 399 ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt) 400 { 401 static bool_t clnt_res; 402 struct timeval TIMEOUT = { 0, 0 }; 403 404 memset((char *)&clnt_res, 0, sizeof (clnt_res)); 405 if (clnt_call(clnt, YPPROC_DOMAIN_NONACK, 406 (xdrproc_t) NULL, (caddr_t) argp, 407 (xdrproc_t) xdr_bool, (caddr_t) &clnt_res, 408 TIMEOUT) != RPC_SUCCESS) { 409 return (NULL); 410 } 411 return (&clnt_res); 412 } 413 414 /* 415 * "We have the machine that goes 'ping!'" -- Monty Python 416 * 417 * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures 418 * of the NIS servers listed in restricted_addrs structure. 419 * Whoever replies the fastest becomes our chosen server. 420 * 421 * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast() 422 * for this, but that has the following problems: 423 * - We only get the address of the machine that replied in the 424 * 'eachresult' callback, and on multi-homed machines this can 425 * lead to confusion. 426 * - clnt_broadcast() only transmits to local networks, whereas with 427 * NIS+ you can have a perfectly good server located anywhere on or 428 * off the local network. 429 * - clnt_broadcast() blocks for an arbitrary amount of time which the 430 * caller can't control -- we want to avoid that. 431 * 432 * Also note that this has nothing to do with the NIS_PING procedure used 433 * for replica updates. 434 */ 435 436 struct ping_req { 437 struct sockaddr_in sin; 438 unsigned long xid; 439 }; 440 441 int __yp_ping(restricted_addrs, cnt, dom, port) 442 struct in_addr *restricted_addrs; 443 int cnt; 444 char *dom; 445 short *port; 446 { 447 struct timeval tv = { 5, 0 }; 448 struct ping_req **reqs; 449 unsigned long i; 450 struct sockaddr_in sin, *any = NULL; 451 int winner = -1; 452 time_t xid_seed, xid_lookup; 453 int sock, dontblock = 1; 454 CLIENT *clnt; 455 char *foo = dom; 456 struct cu_data *cu; 457 enum clnt_stat (*oldfunc)(); 458 int validsrvs = 0; 459 460 /* Set up handles. */ 461 reqs = calloc(1, sizeof(struct ping_req *) * cnt); 462 xid_seed = time(NULL) ^ getpid(); 463 464 for (i = 0; i < cnt; i++) { 465 bzero((char *)&sin, sizeof(sin)); 466 sin.sin_family = AF_INET; 467 bcopy((char *)&restricted_addrs[i], 468 (char *)&sin.sin_addr, sizeof(struct in_addr)); 469 sin.sin_port = htons(__pmap_getport(&sin, YPPROG, 470 YPVERS, IPPROTO_UDP)); 471 if (sin.sin_port == 0) 472 continue; 473 reqs[i] = calloc(1, sizeof(struct ping_req)); 474 bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin)); 475 any = &reqs[i]->sin; 476 reqs[i]->xid = xid_seed; 477 xid_seed++; 478 validsrvs++; 479 } 480 481 /* Make sure at least one server was assigned */ 482 if (!validsrvs) { 483 free(reqs); 484 return(-1); 485 } 486 487 /* Create RPC handle */ 488 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 489 clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock); 490 if (clnt == NULL) { 491 close(sock); 492 for (i = 0; i < cnt; i++) 493 if (reqs[i] != NULL) 494 free(reqs[i]); 495 free(reqs); 496 return(-1); 497 } 498 clnt->cl_auth = authunix_create_default(); 499 cu = (struct cu_data *)clnt->cl_private; 500 tv.tv_sec = 0; 501 clnt_control(clnt, CLSET_TIMEOUT, &tv); 502 ioctl(sock, FIONBIO, &dontblock); 503 oldfunc = clnt->cl_ops->cl_call; 504 clnt->cl_ops->cl_call = clntudp_a_call; 505 506 /* Transmit */ 507 for (i = 0; i < cnt; i++) { 508 if (reqs[i] != NULL) { 509 /* subtract one; clntudp_call() will increment */ 510 *((u_int32_t *)(cu->cu_outbuf)) = reqs[i]->xid - 1; 511 bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr, 512 sizeof(struct sockaddr_in)); 513 ypproc_domain_nonack_2_send(&foo, clnt); 514 } 515 } 516 517 /* Receive reply */ 518 ypproc_domain_nonack_2_recv(&foo, clnt); 519 520 /* Got a winner -- look him up. */ 521 xid_lookup = *((u_int32_t *)(cu->cu_inbuf)); 522 for (i = 0; i < cnt; i++) { 523 if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) { 524 winner = i; 525 *port = reqs[i]->sin.sin_port; 526 } 527 } 528 529 /* Shut everything down */ 530 clnt->cl_ops->cl_call = oldfunc; 531 auth_destroy(clnt->cl_auth); 532 clnt_destroy(clnt); 533 close(sock); 534 535 for (i = 0; i < cnt; i++) 536 if (reqs[i] != NULL) 537 free(reqs[i]); 538 free(reqs); 539 540 return(winner); 541 } 542