1 /*- 2 * Copyright (c) 2009, Sun Microsystems, Inc. 3 * 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 are met: 7 * - Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * - Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 * - Neither the name of Sun Microsystems, Inc. nor the names of its 13 * contributors may be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 * 28 * @(#)clnt_bcast.c 1.18 94/05/03 SMI; 1.15 89/04/21 Copyr 1988 Sun Micro 29 * $NetBSD: clnt_bcast.c,v 1.3 2000/07/06 03:05:20 christos Exp $ 30 * $FreeBSD: src/lib/libc/rpc/clnt_bcast.c,v 1.9 2006/09/09 22:14:42 mbr Exp $ 31 */ 32 /* 33 * Copyright (c) 1986-1991 by Sun Microsystems Inc. 34 */ 35 36 /* 37 * clnt_bcast.c 38 * Client interface to broadcast service. 39 * 40 * Copyright (C) 1988, Sun Microsystems, Inc. 41 * 42 * The following is kludged-up support for simple rpc broadcasts. 43 * Someday a large, complicated system will replace these routines. 44 */ 45 46 #include "namespace.h" 47 #include <sys/types.h> 48 #include <sys/socket.h> 49 #include <sys/queue.h> 50 #include <net/if.h> 51 #include <netinet/in.h> 52 #include <ifaddrs.h> 53 #include <sys/poll.h> 54 #include <rpc/rpc.h> 55 #ifdef PORTMAP 56 #include <rpc/pmap_prot.h> 57 #include <rpc/pmap_clnt.h> 58 #include <rpc/pmap_rmt.h> 59 #endif /* PORTMAP */ 60 #include <rpc/nettype.h> 61 #include <arpa/inet.h> 62 #ifdef RPC_DEBUG 63 #include <stdio.h> 64 #endif 65 #include <errno.h> 66 #include <stdlib.h> 67 #include <unistd.h> 68 #include <netdb.h> 69 #include <err.h> 70 #include <string.h> 71 #include "un-namespace.h" 72 73 #include "rpc_com.h" 74 75 #define MAXBCAST 20 /* Max no of broadcasting transports */ 76 #define INITTIME 4000 /* Time to wait initially */ 77 #define WAITTIME 8000 /* Maximum time to wait */ 78 79 /* 80 * If nettype is NULL, it broadcasts on all the available 81 * datagram_n transports. May potentially lead to broadacst storms 82 * and hence should be used with caution, care and courage. 83 * 84 * The current parameter xdr packet size is limited by the max tsdu 85 * size of the transport. If the max tsdu size of any transport is 86 * smaller than the parameter xdr packet, then broadcast is not 87 * sent on that transport. 88 * 89 * Also, the packet size should be less the packet size of 90 * the data link layer (for ethernet it is 1400 bytes). There is 91 * no easy way to find out the max size of the data link layer and 92 * we are assuming that the args would be smaller than that. 93 * 94 * The result size has to be smaller than the transport tsdu size. 95 * 96 * If PORTMAP has been defined, we send two packets for UDP, one for 97 * rpcbind and one for portmap. For those machines which support 98 * both rpcbind and portmap, it will cause them to reply twice, and 99 * also here it will get two responses ... inefficient and clumsy. 100 */ 101 102 struct broadif { 103 int index; 104 struct sockaddr_storage broadaddr; 105 TAILQ_ENTRY(broadif) link; 106 }; 107 108 typedef TAILQ_HEAD(, broadif) broadlist_t; 109 110 int __rpc_broadenable(int, int, struct broadif *); 111 void __rpc_freebroadifs(broadlist_t *); 112 int __rpc_getbroadifs(int, int, int, broadlist_t *); 113 114 int __rpc_lowvers = 0; 115 116 int 117 __rpc_getbroadifs(int af, int proto, int socktype, broadlist_t *list) 118 { 119 int count = 0; 120 struct broadif *bip; 121 struct ifaddrs *ifap, *ifp; 122 #ifdef INET6 123 struct sockaddr_in6 *sin6; 124 #endif 125 struct sockaddr_in *sin; 126 struct addrinfo hints, *res; 127 128 if (getifaddrs(&ifp) < 0) 129 return 0; 130 131 memset(&hints, 0, sizeof hints); 132 133 hints.ai_family = af; 134 hints.ai_protocol = proto; 135 hints.ai_socktype = socktype; 136 137 if (getaddrinfo(NULL, "sunrpc", &hints, &res) != 0) { 138 freeifaddrs(ifp); 139 return 0; 140 } 141 142 for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { 143 if (ifap->ifa_addr->sa_family != af || 144 !(ifap->ifa_flags & IFF_UP)) 145 continue; 146 bip = (struct broadif *)malloc(sizeof *bip); 147 if (bip == NULL) 148 break; 149 bip->index = if_nametoindex(ifap->ifa_name); 150 if ( 151 #ifdef INET6 152 af != AF_INET6 && 153 #endif 154 (ifap->ifa_flags & IFF_BROADCAST) && 155 ifap->ifa_broadaddr) { 156 memcpy(&bip->broadaddr, ifap->ifa_broadaddr, 157 (size_t)ifap->ifa_broadaddr->sa_len); 158 sin = (struct sockaddr_in *)(void *)&bip->broadaddr; 159 sin->sin_port = 160 ((struct sockaddr_in *) 161 (void *)res->ai_addr)->sin_port; 162 } else 163 #ifdef INET6 164 if (af == AF_INET6 && (ifap->ifa_flags & IFF_MULTICAST)) { 165 sin6 = (struct sockaddr_in6 *)(void *)&bip->broadaddr; 166 inet_pton(af, RPCB_MULTICAST_ADDR, &sin6->sin6_addr); 167 sin6->sin6_family = af; 168 sin6->sin6_len = sizeof *sin6; 169 sin6->sin6_port = 170 ((struct sockaddr_in6 *) 171 (void *)res->ai_addr)->sin6_port; 172 sin6->sin6_scope_id = bip->index; 173 } else 174 #endif 175 { 176 free(bip); 177 continue; 178 } 179 TAILQ_INSERT_TAIL(list, bip, link); 180 count++; 181 } 182 freeifaddrs(ifp); 183 freeaddrinfo(res); 184 185 return count; 186 } 187 188 void 189 __rpc_freebroadifs(broadlist_t *list) 190 { 191 struct broadif *bip, *next; 192 193 bip = TAILQ_FIRST(list); 194 195 while (bip != NULL) { 196 next = TAILQ_NEXT(bip, link); 197 free(bip); 198 bip = next; 199 } 200 } 201 202 int 203 /*ARGSUSED*/ 204 __rpc_broadenable(int af __unused, int s, struct broadif *bip __unused) 205 { 206 int o = 1; 207 208 #if 0 209 if (af == AF_INET6) { 210 fprintf(stderr, "set v6 multicast if to %d\n", bip->index); 211 if (_setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &bip->index, 212 sizeof bip->index) < 0) 213 return -1; 214 } else 215 #endif 216 if (_setsockopt(s, SOL_SOCKET, SO_BROADCAST, &o, sizeof o) < 0) 217 return -1; 218 219 return 0; 220 } 221 222 223 enum clnt_stat 224 rpc_broadcast_exp( 225 rpcprog_t prog, /* program number */ 226 rpcvers_t vers, /* version number */ 227 rpcproc_t proc, /* procedure number */ 228 xdrproc_t xargs, /* xdr routine for args */ 229 caddr_t argsp, /* pointer to args */ 230 xdrproc_t xresults, /* xdr routine for results */ 231 caddr_t resultsp, /* pointer to results */ 232 resultproc_t eachresult, /* call with each result obtained */ 233 int inittime, /* how long to wait initially */ 234 int waittime, /* maximum time to wait */ 235 const char *nettype /* transport type */ 236 ) 237 { 238 enum clnt_stat stat = RPC_SUCCESS; /* Return status */ 239 XDR xdr_stream; /* XDR stream */ 240 XDR *xdrs = &xdr_stream; 241 struct rpc_msg msg; /* RPC message */ 242 struct timeval t; 243 char *outbuf = NULL; /* Broadcast msg buffer */ 244 char *inbuf = NULL; /* Reply buf */ 245 int inlen; 246 u_int maxbufsize = 0; 247 AUTH *sys_auth = authunix_create_default(); 248 int i; 249 void *handle; 250 char uaddress[1024]; /* A self imposed limit */ 251 char *uaddrp = uaddress; 252 int pmap_reply_flag; /* reply recvd from PORTMAP */ 253 /* An array of all the suitable broadcast transports */ 254 struct { 255 int fd; /* File descriptor */ 256 int af; 257 int proto; 258 struct netconfig *nconf; /* Netconfig structure */ 259 u_int asize; /* Size of the addr buf */ 260 u_int dsize; /* Size of the data buf */ 261 struct sockaddr_storage raddr; /* Remote address */ 262 broadlist_t nal; 263 } fdlist[MAXBCAST]; 264 struct pollfd pfd[MAXBCAST]; 265 size_t fdlistno = 0; 266 struct r_rpcb_rmtcallargs barg; /* Remote arguments */ 267 struct r_rpcb_rmtcallres bres; /* Remote results */ 268 size_t outlen; 269 struct netconfig *nconf; 270 int msec; 271 int pollretval; 272 int fds_found; 273 274 #ifdef PORTMAP 275 size_t outlen_pmap = 0; 276 u_long port; /* Remote port number */ 277 int pmap_flag = 0; /* UDP exists ? */ 278 char *outbuf_pmap = NULL; 279 struct rmtcallargs barg_pmap; /* Remote arguments */ 280 struct rmtcallres bres_pmap; /* Remote results */ 281 u_int udpbufsz = 0; 282 #endif /* PORTMAP */ 283 284 if (sys_auth == NULL) { 285 return (RPC_SYSTEMERROR); 286 } 287 /* 288 * initialization: create a fd, a broadcast address, and send the 289 * request on the broadcast transport. 290 * Listen on all of them and on replies, call the user supplied 291 * function. 292 */ 293 294 if (nettype == NULL) 295 nettype = "datagram_n"; 296 if ((handle = __rpc_setconf(nettype)) == NULL) { 297 AUTH_DESTROY(sys_auth); 298 return (RPC_UNKNOWNPROTO); 299 } 300 while ((nconf = __rpc_getconf(handle)) != NULL) { 301 int fd; 302 struct __rpc_sockinfo si; 303 304 if (nconf->nc_semantics != NC_TPI_CLTS) 305 continue; 306 if (fdlistno >= MAXBCAST) 307 break; /* No more slots available */ 308 if (!__rpc_nconf2sockinfo(nconf, &si)) 309 continue; 310 311 TAILQ_INIT(&fdlist[fdlistno].nal); 312 if (__rpc_getbroadifs(si.si_af, si.si_proto, si.si_socktype, 313 &fdlist[fdlistno].nal) == 0) 314 continue; 315 316 fd = _socket(si.si_af, si.si_socktype, si.si_proto); 317 if (fd < 0) { 318 stat = RPC_CANTSEND; 319 continue; 320 } 321 fdlist[fdlistno].af = si.si_af; 322 fdlist[fdlistno].proto = si.si_proto; 323 fdlist[fdlistno].fd = fd; 324 fdlist[fdlistno].nconf = nconf; 325 fdlist[fdlistno].asize = __rpc_get_a_size(si.si_af); 326 pfd[fdlistno].events = POLLIN | POLLPRI | 327 POLLRDNORM | POLLRDBAND; 328 pfd[fdlistno].fd = fdlist[fdlistno].fd = fd; 329 fdlist[fdlistno].dsize = __rpc_get_t_size(si.si_af, si.si_proto, 330 0); 331 332 if (maxbufsize <= fdlist[fdlistno].dsize) 333 maxbufsize = fdlist[fdlistno].dsize; 334 335 #ifdef PORTMAP 336 if (si.si_af == AF_INET && si.si_proto == IPPROTO_UDP) { 337 udpbufsz = fdlist[fdlistno].dsize; 338 if ((outbuf_pmap = malloc(udpbufsz)) == NULL) { 339 _close(fd); 340 stat = RPC_SYSTEMERROR; 341 goto done_broad; 342 } 343 pmap_flag = 1; 344 } 345 #endif /* PORTMAP */ 346 fdlistno++; 347 } 348 349 if (fdlistno == 0) { 350 if (stat == RPC_SUCCESS) 351 stat = RPC_UNKNOWNPROTO; 352 goto done_broad; 353 } 354 if (maxbufsize == 0) { 355 if (stat == RPC_SUCCESS) 356 stat = RPC_CANTSEND; 357 goto done_broad; 358 } 359 inbuf = malloc(maxbufsize); 360 outbuf = malloc(maxbufsize); 361 if ((inbuf == NULL) || (outbuf == NULL)) { 362 stat = RPC_SYSTEMERROR; 363 goto done_broad; 364 } 365 366 /* Serialize all the arguments which have to be sent */ 367 gettimeofday(&t, NULL); 368 msg.rm_xid = __RPC_GETXID(&t); 369 msg.rm_direction = CALL; 370 msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 371 msg.rm_call.cb_prog = RPCBPROG; 372 msg.rm_call.cb_vers = RPCBVERS; 373 msg.rm_call.cb_proc = RPCBPROC_CALLIT; 374 barg.prog = prog; 375 barg.vers = vers; 376 barg.proc = proc; 377 barg.args.args_val = argsp; 378 barg.xdr_args = xargs; 379 bres.addr = uaddrp; 380 bres.results.results_val = resultsp; 381 bres.xdr_res = xresults; 382 msg.rm_call.cb_cred = sys_auth->ah_cred; 383 msg.rm_call.cb_verf = sys_auth->ah_verf; 384 xdrmem_create(xdrs, outbuf, maxbufsize, XDR_ENCODE); 385 if ((!xdr_callmsg(xdrs, &msg)) || 386 (!xdr_rpcb_rmtcallargs(xdrs, 387 (struct rpcb_rmtcallargs *)(void *)&barg))) { 388 stat = RPC_CANTENCODEARGS; 389 goto done_broad; 390 } 391 outlen = xdr_getpos(xdrs); 392 xdr_destroy(xdrs); 393 394 #ifdef PORTMAP 395 /* Prepare the packet for version 2 PORTMAP */ 396 if (pmap_flag) { 397 msg.rm_xid++; /* One way to distinguish */ 398 msg.rm_call.cb_prog = PMAPPROG; 399 msg.rm_call.cb_vers = PMAPVERS; 400 msg.rm_call.cb_proc = PMAPPROC_CALLIT; 401 barg_pmap.prog = prog; 402 barg_pmap.vers = vers; 403 barg_pmap.proc = proc; 404 barg_pmap.args_ptr = argsp; 405 barg_pmap.xdr_args = xargs; 406 bres_pmap.port_ptr = &port; 407 bres_pmap.xdr_results = xresults; 408 bres_pmap.results_ptr = resultsp; 409 xdrmem_create(xdrs, outbuf_pmap, udpbufsz, XDR_ENCODE); 410 if ((! xdr_callmsg(xdrs, &msg)) || 411 (! xdr_rmtcall_args(xdrs, &barg_pmap))) { 412 stat = RPC_CANTENCODEARGS; 413 goto done_broad; 414 } 415 outlen_pmap = xdr_getpos(xdrs); 416 xdr_destroy(xdrs); 417 } 418 #endif /* PORTMAP */ 419 420 /* 421 * Basic loop: broadcast the packets to transports which 422 * support data packets of size such that one can encode 423 * all the arguments. 424 * Wait a while for response(s). 425 * The response timeout grows larger per iteration. 426 */ 427 for (msec = inittime; msec <= waittime; msec += msec) { 428 struct broadif *bip; 429 430 /* Broadcast all the packets now */ 431 for (i = 0; i < fdlistno; i++) { 432 if (fdlist[i].dsize < outlen) { 433 stat = RPC_CANTSEND; 434 continue; 435 } 436 for (bip = TAILQ_FIRST(&fdlist[i].nal); bip != NULL; 437 bip = TAILQ_NEXT(bip, link)) { 438 void *addr; 439 440 addr = &bip->broadaddr; 441 442 __rpc_broadenable(fdlist[i].af, fdlist[i].fd, 443 bip); 444 445 /* 446 * Only use version 3 if lowvers is not set 447 */ 448 449 if (!__rpc_lowvers) 450 if (_sendto(fdlist[i].fd, outbuf, 451 outlen, 0, (struct sockaddr*)addr, 452 (size_t)fdlist[i].asize) != 453 outlen) { 454 #ifdef RPC_DEBUG 455 perror("sendto"); 456 #endif 457 warnx("clnt_bcast: cannot send" 458 "broadcast packet"); 459 stat = RPC_CANTSEND; 460 continue; 461 } 462 #ifdef RPC_DEBUG 463 if (!__rpc_lowvers) 464 fprintf(stderr, "Broadcast packet sent " 465 "for %s\n", 466 fdlist[i].nconf->nc_netid); 467 #endif 468 #ifdef PORTMAP 469 /* 470 * Send the version 2 packet also 471 * for UDP/IP 472 */ 473 if (pmap_flag && 474 fdlist[i].proto == IPPROTO_UDP) { 475 if (_sendto(fdlist[i].fd, outbuf_pmap, 476 outlen_pmap, 0, addr, 477 (size_t)fdlist[i].asize) != 478 outlen_pmap) { 479 warnx("clnt_bcast: " 480 "Cannot send broadcast packet"); 481 stat = RPC_CANTSEND; 482 continue; 483 } 484 } 485 #ifdef RPC_DEBUG 486 fprintf(stderr, "PMAP Broadcast packet " 487 "sent for %s\n", 488 fdlist[i].nconf->nc_netid); 489 #endif 490 #endif /* PORTMAP */ 491 } 492 /* End for sending all packets on this transport */ 493 } /* End for sending on all transports */ 494 495 if (eachresult == NULL) { 496 stat = RPC_SUCCESS; 497 goto done_broad; 498 } 499 500 /* 501 * Get all the replies from these broadcast requests 502 */ 503 recv_again: 504 505 switch (pollretval = _poll(pfd, fdlistno, msec)) { 506 case 0: /* timed out */ 507 stat = RPC_TIMEDOUT; 508 continue; 509 case -1: /* some kind of error - we ignore it */ 510 goto recv_again; 511 } /* end of poll results switch */ 512 513 for (i = fds_found = 0; 514 i < fdlistno && fds_found < pollretval; i++) { 515 bool_t done = FALSE; 516 517 if (pfd[i].revents == 0) 518 continue; 519 else if (pfd[i].revents & POLLNVAL) { 520 /* 521 * Something bad has happened to this descri- 522 * ptor. We can cause _poll() to ignore 523 * it simply by using a negative fd. We do that 524 * rather than compacting the pfd[] and fdlist[] 525 * arrays. 526 */ 527 pfd[i].fd = -1; 528 fds_found++; 529 continue; 530 } else 531 fds_found++; 532 #ifdef RPC_DEBUG 533 fprintf(stderr, "response for %s\n", 534 fdlist[i].nconf->nc_netid); 535 #endif 536 try_again: 537 inlen = _recvfrom(fdlist[i].fd, inbuf, fdlist[i].dsize, 538 0, (struct sockaddr *)(void *)&fdlist[i].raddr, 539 &fdlist[i].asize); 540 if (inlen < 0) { 541 if (errno == EINTR) 542 goto try_again; 543 warnx("clnt_bcast: Cannot receive reply to " 544 "broadcast"); 545 stat = RPC_CANTRECV; 546 continue; 547 } 548 if (inlen < sizeof (u_int32_t)) 549 continue; /* Drop that and go ahead */ 550 /* 551 * see if reply transaction id matches sent id. 552 * If so, decode the results. If return id is xid + 1 553 * it was a PORTMAP reply 554 */ 555 if (*((u_int32_t *)(void *)(inbuf)) == 556 *((u_int32_t *)(void *)(outbuf))) { 557 pmap_reply_flag = 0; 558 msg.acpted_rply.ar_verf = _null_auth; 559 msg.acpted_rply.ar_results.where = 560 (caddr_t)(void *)&bres; 561 msg.acpted_rply.ar_results.proc = 562 (xdrproc_t)xdr_rpcb_rmtcallres; 563 #ifdef PORTMAP 564 } else if (pmap_flag && 565 *((u_int32_t *)(void *)(inbuf)) == 566 *((u_int32_t *)(void *)(outbuf_pmap))) { 567 pmap_reply_flag = 1; 568 msg.acpted_rply.ar_verf = _null_auth; 569 msg.acpted_rply.ar_results.where = 570 (caddr_t)(void *)&bres_pmap; 571 msg.acpted_rply.ar_results.proc = 572 (xdrproc_t)xdr_rmtcallres; 573 #endif /* PORTMAP */ 574 } else 575 continue; 576 xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE); 577 if (xdr_replymsg(xdrs, &msg)) { 578 if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && 579 (msg.acpted_rply.ar_stat == SUCCESS)) { 580 struct netbuf taddr, *np; 581 struct sockaddr_in *sin; 582 583 #ifdef PORTMAP 584 if (pmap_flag && pmap_reply_flag) { 585 sin = (struct sockaddr_in *) 586 (void *)&fdlist[i].raddr; 587 sin->sin_port = 588 htons((u_short)port); 589 taddr.len = taddr.maxlen = 590 fdlist[i].raddr.ss_len; 591 taddr.buf = &fdlist[i].raddr; 592 done = (*eachresult)(resultsp, 593 &taddr, fdlist[i].nconf); 594 } else { 595 #endif /* PORTMAP */ 596 #ifdef RPC_DEBUG 597 fprintf(stderr, "uaddr %s\n", 598 uaddrp); 599 #endif 600 np = uaddr2taddr( 601 fdlist[i].nconf, uaddrp); 602 done = (*eachresult)(resultsp, 603 np, fdlist[i].nconf); 604 free(np); 605 #ifdef PORTMAP 606 } 607 #endif /* PORTMAP */ 608 } 609 /* otherwise, we just ignore the errors ... */ 610 } 611 /* else some kind of deserialization problem ... */ 612 613 xdrs->x_op = XDR_FREE; 614 msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; 615 xdr_replymsg(xdrs, &msg); 616 (*xresults)(xdrs, resultsp); 617 XDR_DESTROY(xdrs); 618 if (done) { 619 stat = RPC_SUCCESS; 620 goto done_broad; 621 } else { 622 goto recv_again; 623 } 624 } /* The recv for loop */ 625 } /* The giant for loop */ 626 627 done_broad: 628 if (inbuf) 629 free(inbuf); 630 if (outbuf) 631 free(outbuf); 632 #ifdef PORTMAP 633 if (outbuf_pmap) 634 free(outbuf_pmap); 635 #endif /* PORTMAP */ 636 for (i = 0; i < fdlistno; i++) { 637 _close(fdlist[i].fd); 638 __rpc_freebroadifs(&fdlist[i].nal); 639 } 640 AUTH_DESTROY(sys_auth); 641 __rpc_endconf(handle); 642 643 return (stat); 644 } 645 646 647 enum clnt_stat 648 rpc_broadcast( 649 rpcprog_t prog, /* program number */ 650 rpcvers_t vers, /* version number */ 651 rpcproc_t proc, /* procedure number */ 652 xdrproc_t xargs, /* xdr routine for args */ 653 caddr_t argsp, /* pointer to args */ 654 xdrproc_t xresults, /* xdr routine for results */ 655 caddr_t resultsp, /* pointer to results */ 656 resultproc_t eachresult, /* call with each result obtained */ 657 const char *nettype /* transport type */ 658 ) 659 { 660 enum clnt_stat dummy; 661 662 dummy = rpc_broadcast_exp(prog, vers, proc, xargs, argsp, 663 xresults, resultsp, eachresult, 664 INITTIME, WAITTIME, nettype); 665 return (dummy); 666 } 667