1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * Copyright 1999 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 26 /* All Rights Reserved */ 27 /* 28 * Portions of this source code were derived from Berkeley 29 * 4.3 BSD under license from the Regents of the University of 30 * California. 31 */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 /* 36 * clnt_bcast.c 37 * Client interface to broadcast service. 38 * 39 * The following is kludged-up support for simple rpc broadcasts. 40 * Someday a large, complicated system will replace these routines. 41 */ 42 43 #include <string.h> 44 #include <rpc/rpc.h> 45 #include <rpc/trace.h> 46 #include <rpc/nettype.h> 47 #include <sys/poll.h> 48 #include <netdir.h> 49 #ifdef PORTMAP 50 #include <rpc/pmap_prot.h> 51 #include <rpc/pmap_clnt.h> 52 #include <rpc/pmap_rmt.h> 53 #endif 54 #ifdef RPC_DEBUG 55 #include <stdio.h> 56 #endif 57 #include <errno.h> 58 #include <syslog.h> 59 #include <stdlib.h> 60 #include <unistd.h> 61 62 #define MAXBCAST 20 /* Max no of broadcasting transports */ 63 #define INITTIME 4000 /* Time to wait initially */ 64 #define WAITTIME 8000 /* Maximum time to wait */ 65 66 int lowvers = 1; /* by default, broadcast only version 2 over UDP */ 67 #ifndef NETIDLEN 68 #define NETIDLEN 32 69 #endif 70 71 extern int inet_pton(); 72 extern int bzero(caddr_t dst, int len); 73 74 /* 75 * If nettype is NULL, it broadcasts on all the available 76 * datagram_n transports. May potentially lead to broadacst storms 77 * and hence should be used with caution, care and courage. 78 * 79 * The current parameter xdr packet size is limited by the max tsdu 80 * size of the transport. If the max tsdu size of any transport is 81 * smaller than the parameter xdr packet, then broadcast is not 82 * sent on that transport. 83 * 84 * Also, the packet size should be less the packet size of 85 * the data link layer (for ethernet it is 1400 bytes). There is 86 * no easy way to find out the max size of the data link layer and 87 * we are assuming that the args would be smaller than that. 88 * 89 * The result size has to be smaller than the transport tsdu size. 90 * 91 * If PORTMAP has been defined, we send two packets for UDP, one for 92 * rpcbind and one for portmap. For those machines which support 93 * both rpcbind and portmap, it will cause them to reply twice, and 94 * also here it will get two responses ... inefficient and clumsy. 95 */ 96 97 98 enum clnt_stat 99 rpc_broadcast_exp(prog, vers, proc, xargs, argsp, xresults, resultsp, 100 eachresult, inittime, waittime, netclass) 101 rpcprog_t prog; /* program number */ 102 rpcvers_t vers; /* version number */ 103 rpcproc_t proc; /* procedure number */ 104 xdrproc_t xargs; /* xdr routine for args */ 105 caddr_t argsp; /* pointer to args */ 106 xdrproc_t xresults; /* xdr routine for results */ 107 caddr_t resultsp; /* pointer to results */ 108 resultproc_t eachresult; /* call with each result obtained */ 109 int inittime; /* how long to wait initially */ 110 int waittime; /* maximum time to wait */ 111 const char *netclass; /* transport type */ 112 { 113 enum clnt_stat stat = RPC_SUCCESS; /* Return status */ 114 XDR xdr_stream; /* XDR stream */ 115 register XDR *xdrs = &xdr_stream; 116 struct rpc_msg msg; /* RPC message */ 117 struct timeval t; 118 char *outbuf = NULL; /* Broadcast msg buffer */ 119 char *inbuf = NULL; /* Reply buf */ 120 uint_t maxbufsize = 0; 121 AUTH *sys_auth = authsys_create_default(); 122 register int i, j; 123 void *handle; 124 char uaddress[1024]; /* A self imposed limit */ 125 char *uaddrp = uaddress; 126 int pmap_reply_flag; /* reply recvd from PORTMAP */ 127 /* An array of all the suitable broadcast transports */ 128 struct { 129 int fd; /* File descriptor */ 130 bool_t udp_flag; /* this is udp */ 131 struct netconfig *nconf; /* Netconfig structure */ 132 uint_t asize; /* Size of the addr buf */ 133 uint_t dsize; /* Size of the data buf */ 134 struct netbuf raddr; /* Remote address */ 135 struct nd_addrlist *nal; /* Broadcast addrs */ 136 } fdlist[MAXBCAST]; 137 struct pollfd pfd[MAXBCAST]; 138 register int fdlistno = 0; 139 struct r_rpcb_rmtcallargs barg; /* Remote arguments */ 140 struct r_rpcb_rmtcallres bres; /* Remote results */ 141 struct t_unitdata t_udata, t_rdata; 142 struct netconfig *nconf; 143 struct nd_hostserv hs; 144 int msec; 145 int pollretval; 146 int fds_found; 147 char nettype_array[NETIDLEN]; 148 char *nettype = &nettype_array[0]; 149 150 #ifdef PORTMAP 151 rpcport_t *port; /* Remote port number */ 152 int pmap_flag = 0; /* UDP exists ? */ 153 char *outbuf_pmap = NULL; 154 struct p_rmtcallargs barg_pmap; /* Remote arguments */ 155 struct p_rmtcallres bres_pmap; /* Remote results */ 156 struct t_unitdata t_udata_pmap; 157 int udpbufsz = 0; 158 #endif /* PORTMAP */ 159 160 trace4(TR_rpc_broadcast, 0, prog, vers, proc); 161 if (sys_auth == (AUTH *)NULL) { 162 trace4(TR_rpc_broadcast, 1, prog, vers, proc); 163 return (RPC_SYSTEMERROR); 164 } 165 /* 166 * initialization: create a fd, a broadcast address, and send the 167 * request on the broadcast transport. 168 * Listen on all of them and on replies, call the user supplied 169 * function. 170 */ 171 172 if (netclass == NULL) 173 nettype = NULL; 174 else { 175 size_t len = strlen(netclass); 176 if (len >= sizeof (nettype_array)) { 177 trace4(TR_rpc_broadcast, 1, prog, vers, proc); 178 return (RPC_UNKNOWNPROTO); 179 } 180 strcpy(nettype, netclass); 181 } 182 183 if (nettype == NULL) 184 nettype = "datagram_n"; 185 if ((handle = __rpc_setconf((char *)nettype)) == NULL) { 186 trace4(TR_rpc_broadcast, 1, prog, vers, proc); 187 return (RPC_UNKNOWNPROTO); 188 } 189 while (nconf = __rpc_getconf(handle)) { 190 struct t_info tinfo; 191 int fd; 192 uint_t addrlen; 193 194 if (nconf->nc_semantics != NC_TPI_CLTS) 195 continue; 196 if (fdlistno >= MAXBCAST) 197 break; /* No more slots available */ 198 if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1) { 199 stat = RPC_CANTSEND; 200 continue; 201 } 202 if (t_bind(fd, (struct t_bind *)NULL, 203 (struct t_bind *)NULL) == -1) { 204 (void) t_close(fd); 205 stat = RPC_CANTSEND; 206 continue; 207 } 208 /* Do protocol specific negotiating for broadcast */ 209 if (netdir_options(nconf, ND_SET_BROADCAST, fd, NULL)) { 210 (void) t_close(fd); 211 stat = RPC_NOBROADCAST; 212 continue; 213 } 214 fdlist[fdlistno].fd = fd; 215 fdlist[fdlistno].nconf = nconf; 216 fdlist[fdlistno].udp_flag = FALSE; 217 if (((addrlen = __rpc_get_a_size(tinfo.addr)) == 0) || 218 ((fdlist[fdlistno].raddr.buf = malloc(addrlen)) == NULL)) { 219 t_close(fd); 220 stat = RPC_SYSTEMERROR; 221 goto done_broad; 222 } 223 fdlist[fdlistno].raddr.maxlen = addrlen; 224 fdlist[fdlistno].raddr.len = addrlen; 225 pfd[fdlistno].events = POLLIN | POLLPRI | 226 POLLRDNORM | POLLRDBAND; 227 pfd[fdlistno].fd = fdlist[fdlistno].fd = fd; 228 fdlist[fdlistno].asize = addrlen; 229 230 if ((fdlist[fdlistno].dsize = __rpc_get_t_size(0, 231 tinfo.tsdu)) == 0) { 232 t_close(fd); 233 free(fdlist[fdlistno].raddr.buf); 234 stat = RPC_SYSTEMERROR; /* XXX */ 235 goto done_broad; 236 } 237 238 if (maxbufsize <= fdlist[fdlistno].dsize) 239 maxbufsize = fdlist[fdlistno].dsize; 240 #ifdef PORTMAP 241 if (strcmp(nconf->nc_protofmly, NC_INET) == 0 && 242 strcmp(nconf->nc_proto, NC_UDP) == 0) { 243 udpbufsz = fdlist[fdlistno].dsize; 244 if ((outbuf_pmap = malloc(udpbufsz)) == NULL) { 245 t_close(fd); 246 free(fdlist[fdlistno].raddr.buf); 247 stat = RPC_SYSTEMERROR; 248 goto done_broad; 249 } 250 pmap_flag = 1; 251 fdlist[fdlistno].udp_flag = TRUE; 252 } 253 #endif 254 fdlistno++; 255 } 256 257 if (fdlistno == 0) { 258 if (stat == RPC_SUCCESS) 259 stat = RPC_UNKNOWNPROTO; 260 goto done_broad; 261 } 262 if (maxbufsize == 0) { 263 if (stat == RPC_SUCCESS) 264 stat = RPC_CANTSEND; 265 goto done_broad; 266 } 267 inbuf = malloc((size_t)maxbufsize); 268 outbuf = malloc((size_t)maxbufsize); 269 if ((inbuf == NULL) || (outbuf == NULL)) { 270 stat = RPC_SYSTEMERROR; 271 goto done_broad; 272 } 273 274 /* Serialize all the arguments which have to be sent */ 275 (void) gettimeofday(&t, (struct timezone *)0); 276 msg.rm_xid = getpid() ^ t.tv_sec ^ t.tv_usec; 277 msg.rm_direction = CALL; 278 msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 279 msg.rm_call.cb_prog = RPCBPROG; 280 msg.rm_call.cb_vers = RPCBVERS; 281 msg.rm_call.cb_proc = RPCBPROC_CALLIT; 282 barg.prog = prog; 283 barg.vers = vers; 284 barg.proc = proc; 285 barg.args.args_val = argsp; 286 barg.xdr_args = xargs; 287 bres.addr = uaddrp; 288 bres.results.results_val = resultsp; 289 bres.xdr_res = xresults; 290 msg.rm_call.cb_cred = sys_auth->ah_cred; 291 msg.rm_call.cb_verf = sys_auth->ah_verf; 292 xdrmem_create(xdrs, outbuf, maxbufsize, XDR_ENCODE); 293 if ((! xdr_callmsg(xdrs, &msg)) || 294 (! xdr_rpcb_rmtcallargs(xdrs, &barg))) { 295 stat = RPC_CANTENCODEARGS; 296 goto done_broad; 297 } 298 t_udata.opt.len = 0; 299 t_udata.udata.buf = outbuf; 300 t_udata.udata.len = xdr_getpos(xdrs); 301 t_udata.udata.maxlen = t_udata.udata.len; 302 /* XXX Should have set opt to its legal maxlen. */ 303 t_rdata.opt.len = t_rdata.opt.maxlen = 0; 304 xdr_destroy(xdrs); 305 306 #ifdef PORTMAP 307 /* Prepare the packet for version 2 PORTMAP */ 308 if (pmap_flag) { 309 msg.rm_xid++; /* One way to distinguish */ 310 msg.rm_call.cb_prog = PMAPPROG; 311 msg.rm_call.cb_vers = PMAPVERS; 312 msg.rm_call.cb_proc = PMAPPROC_CALLIT; 313 barg_pmap.prog = prog; 314 barg_pmap.vers = vers; 315 barg_pmap.proc = proc; 316 barg_pmap.args.args_val = argsp; 317 barg_pmap.xdr_args = xargs; 318 port = &bres_pmap.port; /* for use later on */ 319 bres_pmap.xdr_res = xresults; 320 bres_pmap.res.res_val = resultsp; 321 xdrmem_create(xdrs, outbuf_pmap, udpbufsz, XDR_ENCODE); 322 if ((! xdr_callmsg(xdrs, &msg)) || 323 (! xdr_rmtcallargs(xdrs, &barg_pmap))) { 324 stat = RPC_CANTENCODEARGS; 325 goto done_broad; 326 } 327 t_udata_pmap.opt.len = 0; 328 t_udata_pmap.udata.buf = outbuf_pmap; 329 t_udata_pmap.udata.len = xdr_getpos(xdrs); 330 xdr_destroy(xdrs); 331 } 332 #endif /* PORTMAP */ 333 334 /* 335 * Basic loop: broadcast the packets to transports which 336 * support data packets of size such that one can encode 337 * all the arguments. 338 * Wait a while for response(s). 339 * The response timeout grows larger per iteration. 340 */ 341 hs.h_host = HOST_BROADCAST; 342 hs.h_serv = "rpcbind"; 343 344 for (msec = inittime; msec <= waittime; msec += msec) { 345 /* Broadcast all the packets now */ 346 for (i = 0; i < fdlistno; i++) { 347 if (strcmp(fdlist[i].nconf->nc_protofmly, 348 NC_INET6) == 0) { 349 /* if it's IPv6 */ 350 351 struct netbuf addr; 352 struct sockaddr_in6 sa6; 353 354 /* fill in the multicast address */ 355 bzero((char *)&sa6, sizeof (sa6)); 356 sa6.sin6_family = AF_INET6; 357 sa6.sin6_port = htons(PMAPPORT); 358 (void) inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, 359 &sa6.sin6_addr); 360 addr.maxlen = sizeof (struct sockaddr_in6); 361 addr.len = addr.maxlen; 362 addr.buf = (char *)&sa6; 363 364 /* now send rpcbind message */ 365 t_udata.addr = addr; 366 367 368 if (t_sndudata(fdlist[i].fd, 369 &t_udata)) { 370 (void) syslog(LOG_ERR, 371 "Cannot send broadcast\ 372 packet: %m"); 373 #ifdef RPC_DEBUG 374 t_error("rpc_broadcast: t_sndudata"); 375 #endif 376 stat = RPC_CANTSEND; 377 continue; 378 } 379 380 } else { 381 382 struct nd_addrlist *addrlist; 383 384 if (fdlist[i].dsize < t_udata.udata.len) { 385 stat = RPC_CANTSEND; 386 continue; 387 } 388 if (netdir_getbyname(fdlist[i].nconf, &hs, 389 &addrlist) || (addrlist->n_cnt == 0)) { 390 stat = RPC_N2AXLATEFAILURE; 391 continue; 392 } 393 394 for (j = 0; j < addrlist->n_cnt; j++) { 395 struct netconfig *nconf = 396 fdlist[i].nconf; 397 398 t_udata.addr = addrlist->n_addrs[j]; 399 400 /* 401 * Only use version 3 if lowvers 402 * is not set or transport is not UDP. 403 */ 404 405 if (!lowvers || !fdlist[i].udp_flag) 406 if (t_sndudata(fdlist[i].fd, 407 &t_udata)) { 408 (void) syslog(LOG_ERR, 409 "Cannot send broadcast\ 410 packet: %m"); 411 #ifdef RPC_DEBUG 412 t_error("rpc_broadcast: t_sndudata"); 413 #endif 414 stat = RPC_CANTSEND; 415 continue; 416 }; 417 #ifdef RPC_DEBUG 418 if (!lowvers || !fdlist[i].udp_flag) 419 fprintf(stderr, "Broadcast\ 420 packet sent for %s\n", nconf->nc_netid); 421 #endif 422 #ifdef PORTMAP 423 /* 424 * Send the version 2 packet also 425 * for UDP/IP 426 */ 427 if (fdlist[i].udp_flag) { 428 t_udata_pmap.addr = 429 t_udata.addr; 430 if (t_sndudata(fdlist[i].fd, 431 &t_udata_pmap)) { 432 (void) syslog(LOG_ERR,\ 433 "Cannot send broadcast packet: %m"); 434 #ifdef RPC_DEBUG 435 t_error("rpc_broadcast:\ 436 t_sndudata"); 437 #endif 438 stat = RPC_CANTSEND; 439 continue; 440 } 441 } 442 #ifdef RPC_DEBUG 443 fprintf(stderr, "PMAP Broadcast packet\ 444 sent for %s\n", nconf->nc_netid); 445 #endif 446 #endif /* PORTMAP */ 447 } 448 /* End for sending all packets on this transport */ 449 (void) netdir_free((char *)addrlist, ND_ADDRLIST); 450 } /* end non-IPv6 */ 451 452 } /* End for sending on all transports */ 453 454 if (eachresult == NULL) { 455 stat = RPC_SUCCESS; 456 goto done_broad; 457 } 458 459 /* 460 * Get all the replies from these broadcast requests 461 */ 462 recv_again: 463 464 switch (pollretval = poll(pfd, fdlistno, msec)) { 465 case 0: /* timed out */ 466 stat = RPC_TIMEDOUT; 467 continue; 468 case -1: /* some kind of error - we ignore it */ 469 goto recv_again; 470 } /* end of poll results switch */ 471 472 t_rdata.udata.buf = inbuf; 473 474 for (i = fds_found = 0; 475 i < fdlistno && fds_found < pollretval; i++) { 476 477 int flag; 478 bool_t done = FALSE; 479 480 if (pfd[i].revents == 0) 481 continue; 482 else if (pfd[i].revents & POLLNVAL) { 483 /* 484 * Something bad has happened to this descri- 485 * ptor. We can cause poll() to ignore 486 * it simply by using a negative fd. We do that 487 * rather than compacting the pfd[] and fdlist[] 488 * arrays. 489 */ 490 pfd[i].fd = -1; 491 fds_found++; 492 continue; 493 } else 494 fds_found++; 495 #ifdef RPC_DEBUG 496 fprintf(stderr, "response for %s\n", 497 fdlist[i].nconf->nc_netid); 498 #endif 499 try_again: 500 t_rdata.udata.maxlen = fdlist[i].dsize; 501 t_rdata.udata.len = 0; 502 t_rdata.addr = fdlist[i].raddr; 503 if (t_rcvudata(fdlist[i].fd, &t_rdata, &flag) == -1) { 504 if (t_errno == TSYSERR && errno == EINTR) 505 goto try_again; 506 507 /* 508 * Ignore any T_UDERR look errors. 509 * We should never see any ICMP port 510 * unreachables when broadcasting but it has 511 * been observed with broken IP 512 * implementations. 513 */ 514 if (t_errno == TLOOK && 515 t_look(fdlist[i].fd) == T_UDERR && 516 t_rcvuderr(fdlist[i].fd, NULL) == 0) 517 goto recv_again; 518 519 (void) syslog(LOG_ERR, 520 "Cannot receive reply to \ 521 broadcast: %m"); 522 stat = RPC_CANTRECV; 523 continue; 524 } 525 /* 526 * Not taking care of flag for T_MORE. 527 * We are assuming that 528 * such calls should not take more than one 529 * transport packet. 530 */ 531 if (flag & T_MORE) 532 continue; /* Drop that and go ahead */ 533 if (t_rdata.udata.len < (uint_t)sizeof (uint32_t)) 534 continue; /* Drop that and go ahead */ 535 /* 536 * see if reply transaction id matches sent id. 537 * If so, decode the results. If return id is xid + 1 538 * it was a PORTMAP reply 539 */ 540 if (*((uint32_t *)(inbuf)) == *((uint32_t *)(outbuf))) { 541 pmap_reply_flag = 0; 542 msg.acpted_rply.ar_verf = _null_auth; 543 msg.acpted_rply.ar_results.where = 544 (caddr_t)&bres; 545 msg.acpted_rply.ar_results.proc = 546 (xdrproc_t)xdr_rpcb_rmtcallres; 547 #ifdef PORTMAP 548 } else if (pmap_flag && 549 *((uint32_t *)(inbuf)) == 550 *((uint32_t *)(outbuf_pmap))) { 551 pmap_reply_flag = 1; 552 msg.acpted_rply.ar_verf = _null_auth; 553 msg.acpted_rply.ar_results.where = 554 (caddr_t)&bres_pmap; 555 msg.acpted_rply.ar_results.proc = 556 (xdrproc_t)xdr_rmtcallres; 557 #endif /* PORTMAP */ 558 } else 559 continue; 560 xdrmem_create(xdrs, inbuf, 561 (uint_t)t_rdata.udata.len, XDR_DECODE); 562 if (xdr_replymsg(xdrs, &msg)) { 563 if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && 564 (msg.acpted_rply.ar_stat == SUCCESS)) { 565 struct netbuf *taddr; 566 #ifdef PORTMAP 567 if (pmap_flag && pmap_reply_flag) { 568 /* convert port to taddr */ 569 ((struct sockaddr_in *) 570 t_rdata.addr.buf)->sin_port = 571 htons((ushort_t)*port); 572 taddr = &t_rdata.addr; 573 } else /* Convert the uaddr to taddr */ 574 #endif 575 taddr = uaddr2taddr( 576 fdlist[i].nconf, 577 uaddrp); 578 done = (*eachresult)(resultsp, taddr, 579 fdlist[i].nconf); 580 #ifdef RPC_DEBUG 581 { 582 int k; 583 584 printf("rmt addr = "); 585 for (k = 0; k < taddr->len; k++) 586 printf("%d ", taddr->buf[k]); 587 printf("\n"); 588 } 589 #endif 590 if (taddr && !pmap_reply_flag) 591 netdir_free((char *)taddr, 592 ND_ADDR); 593 } 594 /* otherwise, we just ignore the errors ... */ 595 } 596 /* else some kind of deserialization problem ... */ 597 598 xdrs->x_op = XDR_FREE; 599 msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; 600 (void) xdr_replymsg(xdrs, &msg); 601 (void) (*xresults)(xdrs, resultsp); 602 XDR_DESTROY(xdrs); 603 if (done) { 604 stat = RPC_SUCCESS; 605 goto done_broad; 606 } else { 607 if (rpc_callerr.re_status == RPC_SYSTEMERROR) { 608 stat = RPC_SYSTEMERROR; 609 goto done_broad; 610 } 611 goto recv_again; 612 } 613 } /* The recv for loop */ 614 } /* The giant for loop */ 615 616 done_broad: 617 if (inbuf) 618 (void) free(inbuf); 619 if (outbuf) 620 (void) free(outbuf); 621 #ifdef PORTMAP 622 if (outbuf_pmap) 623 (void) free(outbuf_pmap); 624 #endif 625 for (i = 0; i < fdlistno; i++) { 626 (void) t_close(fdlist[i].fd); 627 (void) free(fdlist[i].raddr.buf); 628 } 629 AUTH_DESTROY(sys_auth); 630 (void) __rpc_endconf(handle); 631 632 trace4(TR_rpc_broadcast, 1, prog, vers, proc); 633 return (stat); 634 } 635 636 637 enum clnt_stat 638 rpc_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp, 639 eachresult, nettype) 640 rpcprog_t prog; /* program number */ 641 rpcvers_t vers; /* version number */ 642 rpcproc_t proc; /* procedure number */ 643 xdrproc_t xargs; /* xdr routine for args */ 644 caddr_t argsp; /* pointer to args */ 645 xdrproc_t xresults; /* xdr routine for results */ 646 caddr_t resultsp; /* pointer to results */ 647 resultproc_t eachresult; /* call with each result obtained */ 648 const char *nettype; /* transport type */ 649 { 650 enum clnt_stat dummy; 651 652 trace4(TR_rpc_broadcast, 0, prog, vers, proc); 653 dummy = rpc_broadcast_exp(prog, vers, proc, xargs, argsp, 654 xresults, resultsp, eachresult, 655 INITTIME, WAITTIME, nettype); 656 trace1(TR_rpc_broadcast, 1); 657 return (dummy); 658 } 659