int XdmcpFill (int fd, XdmcpBufferPtr buffer, XdmcpNetaddr from, int *fromlen) { BYTE *newBuf; #ifdef STREAMSCONN struct t_unitdata dataunit; int gotallflag, result; #endif if (buffer->size < XDM_MAX_MSGLEN) { newBuf = (BYTE *) malloc(XDM_MAX_MSGLEN); if (newBuf) { free(buffer->data); buffer->data = newBuf; buffer->size = XDM_MAX_MSGLEN; } } buffer->pointer = 0; #ifdef STREAMSCONN dataunit.addr.buf = from; dataunit.addr.maxlen = *fromlen; dataunit.opt.maxlen = 0; /* don't care to know about options */ dataunit.udata.buf = (char *)buffer->data; dataunit.udata.maxlen = buffer->size; result = t_rcvudata (fd, &dataunit, &gotallflag); if (result < 0) { return FALSE; } buffer->count = dataunit.udata.len; *fromlen = dataunit.addr.len; #else buffer->count = recvfrom (fd, (char*)buffer->data, buffer->size, 0, (struct sockaddr *)from, (void *)fromlen); #endif if (buffer->count < 6) { buffer->count = 0; return FALSE; } return TRUE; }
static enum clnt_stat clnt_dg_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp, xdrproc_t xresults, caddr_t resultsp, struct timeval utimeout) { /* LINTED pointer alignment */ struct cu_data *cu = (struct cu_data *)cl->cl_private; XDR *xdrs; int outlen; struct rpc_msg reply_msg; XDR reply_xdrs; struct timeval time_waited; bool_t ok; int nrefreshes = 2; /* number of times to refresh cred */ struct timeval timeout; struct timeval retransmit_time; struct timeval poll_time; struct timeval startime, curtime; struct t_unitdata tu_data; int res; /* result of operations */ uint32_t x_id; if (rpc_fd_lock(dgtbl, cu->cu_fd)) { rpc_callerr.re_status = RPC_FAILED; rpc_callerr.re_errno = errno; rpc_fd_unlock(dgtbl, cu->cu_fd); return (RPC_FAILED); } if (cu->cu_total.tv_usec == -1) { timeout = utimeout; /* use supplied timeout */ } else { timeout = cu->cu_total; /* use default timeout */ } time_waited.tv_sec = 0; time_waited.tv_usec = 0; retransmit_time = cu->cu_wait; tu_data.addr = cu->cu_raddr; call_again: xdrs = &(cu->cu_outxdrs); xdrs->x_op = XDR_ENCODE; XDR_SETPOS(xdrs, 0); /* * Due to little endian byte order, it is necessary to convert to host * format before incrementing xid. */ /* LINTED pointer cast */ x_id = ntohl(*(uint32_t *)(cu->cu_outbuf)) + 1; /* set XID */ /* LINTED pointer cast */ *(uint32_t *)cu->cu_outbuf = htonl(x_id); if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { if ((!XDR_PUTBYTES(xdrs, cu->cu_outbuf, cu->cu_xdrpos)) || (!XDR_PUTINT32(xdrs, (int32_t *)&proc)) || (!AUTH_MARSHALL(cl->cl_auth, xdrs)) || (!xargs(xdrs, argsp))) { rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_CANTENCODEARGS); } } else { /* LINTED pointer alignment */ uint32_t *u = (uint32_t *)&cu->cu_outbuf[cu->cu_xdrpos]; IXDR_PUT_U_INT32(u, proc); if (!__rpc_gss_wrap(cl->cl_auth, cu->cu_outbuf, ((char *)u) - cu->cu_outbuf, xdrs, xargs, argsp)) { rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_CANTENCODEARGS); } } outlen = (int)XDR_GETPOS(xdrs); send_again: tu_data.udata.buf = cu->cu_outbuf_start; tu_data.udata.len = outlen; tu_data.opt.len = 0; if (t_sndudata(cu->cu_fd, &tu_data) == -1) { rpc_callerr.re_terrno = t_errno; rpc_callerr.re_errno = errno; rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_CANTSEND); } /* * Hack to provide rpc-based message passing */ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_TIMEDOUT); } /* * sub-optimal code appears here because we have * some clock time to spare while the packets are in flight. * (We assume that this is actually only executed once.) */ reply_msg.acpted_rply.ar_verf = _null_auth; reply_msg.acpted_rply.ar_results.where = NULL; reply_msg.acpted_rply.ar_results.proc = xdr_void; /* * Set polling time so that we don't wait for * longer than specified by the total time to wait, * or the retransmit time. */ poll_time.tv_sec = timeout.tv_sec - time_waited.tv_sec; poll_time.tv_usec = timeout.tv_usec - time_waited.tv_usec; while (poll_time.tv_usec < 0) { poll_time.tv_usec += 1000000; poll_time.tv_sec--; } if (poll_time.tv_sec < 0 || (poll_time.tv_sec == 0 && poll_time.tv_usec == 0)) { /* * this could happen if time_waited >= timeout */ rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_TIMEDOUT); } if (poll_time.tv_sec > retransmit_time.tv_sec || (poll_time.tv_sec == retransmit_time.tv_sec && poll_time.tv_usec > retransmit_time.tv_usec)) poll_time = retransmit_time; for (;;) { (void) gettimeofday(&startime, NULL); switch (poll(&cu->pfdp, 1, __rpc_timeval_to_msec(&poll_time))) { case -1: if (errno != EINTR && errno != EAGAIN) { rpc_callerr.re_errno = errno; rpc_callerr.re_terrno = 0; rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_CANTRECV); } /*FALLTHROUGH*/ case 0: /* * update time waited */ timeout: (void) gettimeofday(&curtime, NULL); time_waited.tv_sec += curtime.tv_sec - startime.tv_sec; time_waited.tv_usec += curtime.tv_usec - startime.tv_usec; while (time_waited.tv_usec >= 1000000) { time_waited.tv_usec -= 1000000; time_waited.tv_sec++; } while (time_waited.tv_usec < 0) { time_waited.tv_usec += 1000000; time_waited.tv_sec--; } /* * decrement time left to poll by same amount */ poll_time.tv_sec -= curtime.tv_sec - startime.tv_sec; poll_time.tv_usec -= curtime.tv_usec - startime.tv_usec; while (poll_time.tv_usec >= 1000000) { poll_time.tv_usec -= 1000000; poll_time.tv_sec++; } while (poll_time.tv_usec < 0) { poll_time.tv_usec += 1000000; poll_time.tv_sec--; } /* * if there's time left to poll, poll again */ if (poll_time.tv_sec > 0 || (poll_time.tv_sec == 0 && poll_time.tv_usec > 0)) continue; /* * if there's more time left, retransmit; * otherwise, return timeout error */ if (time_waited.tv_sec < timeout.tv_sec || (time_waited.tv_sec == timeout.tv_sec && time_waited.tv_usec < timeout.tv_usec)) { /* * update retransmit_time */ retransmit_time.tv_usec *= 2; retransmit_time.tv_sec *= 2; while (retransmit_time.tv_usec >= 1000000) { retransmit_time.tv_usec -= 1000000; retransmit_time.tv_sec++; } if (retransmit_time.tv_sec >= RPC_MAX_BACKOFF) { retransmit_time.tv_sec = RPC_MAX_BACKOFF; retransmit_time.tv_usec = 0; } /* * redo AUTH_MARSHAL if AUTH_DES or RPCSEC_GSS. */ if (cl->cl_auth->ah_cred.oa_flavor == AUTH_DES || cl->cl_auth->ah_cred.oa_flavor == RPCSEC_GSS) goto call_again; else goto send_again; } rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_TIMEDOUT); default: break; } if (cu->pfdp.revents & POLLNVAL || (cu->pfdp.revents == 0)) { rpc_callerr.re_status = RPC_CANTRECV; /* * Note: we're faking errno here because we * previously would have expected select() to * return -1 with errno EBADF. Poll(BA_OS) * returns 0 and sets the POLLNVAL revents flag * instead. */ rpc_callerr.re_errno = errno = EBADF; rpc_fd_unlock(dgtbl, cu->cu_fd); return (-1); } /* We have some data now */ do { int moreflag; /* flag indicating more data */ moreflag = 0; res = t_rcvudata(cu->cu_fd, cu->cu_tr_data, &moreflag); if (moreflag & T_MORE) { /* * Drop this packet. I aint got any * more space. */ res = -1; /* I should not really be doing this */ errno = 0; /* * XXX: Not really Buffer overflow in the * sense of TLI. */ t_errno = TBUFOVFLW; } } while (res < 0 && (t_errno == TSYSERR && errno == EINTR)); if (res < 0) { int err, errnoflag = FALSE; #ifdef sun if (t_errno == TSYSERR && errno == EWOULDBLOCK) #else if (t_errno == TSYSERR && errno == EAGAIN) #endif continue; if (t_errno == TLOOK) { if ((err = _rcv_unitdata_err(cu)) == 0) continue; else if (err == 1) errnoflag = TRUE; } else { rpc_callerr.re_terrno = t_errno; } if (errnoflag == FALSE) rpc_callerr.re_errno = errno; rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status = RPC_CANTRECV); } if (cu->cu_tr_data->udata.len < (uint_t)sizeof (uint32_t)) continue; /* see if reply transaction id matches sent id */ /* LINTED pointer alignment */ if (*((uint32_t *)(cu->cu_inbuf)) != /* LINTED pointer alignment */ *((uint32_t *)(cu->cu_outbuf))) goto timeout; /* we now assume we have the proper reply */ break; } /* * now decode and validate the response */ xdrmem_create(&reply_xdrs, cu->cu_inbuf, (uint_t)cu->cu_tr_data->udata.len, XDR_DECODE); ok = xdr_replymsg(&reply_xdrs, &reply_msg); /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ if (ok) { if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && (reply_msg.acpted_rply.ar_stat == SUCCESS)) rpc_callerr.re_status = RPC_SUCCESS; else __seterr_reply(&reply_msg, &(rpc_callerr)); if (rpc_callerr.re_status == RPC_SUCCESS) { if (!AUTH_VALIDATE(cl->cl_auth, &reply_msg.acpted_rply.ar_verf)) { rpc_callerr.re_status = RPC_AUTHERROR; rpc_callerr.re_why = AUTH_INVALIDRESP; } else if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { if (!(*xresults)(&reply_xdrs, resultsp)) { if (rpc_callerr.re_status == RPC_SUCCESS) rpc_callerr.re_status = RPC_CANTDECODERES; } } else if (!__rpc_gss_unwrap(cl->cl_auth, &reply_xdrs, xresults, resultsp)) { if (rpc_callerr.re_status == RPC_SUCCESS) rpc_callerr.re_status = RPC_CANTDECODERES; } } /* end successful completion */ /* * If unsuccesful AND error is an authentication error * then refresh credentials and try again, else break */ else if (rpc_callerr.re_status == RPC_AUTHERROR) /* maybe our credentials need to be refreshed ... */ if (nrefreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg)) goto call_again; else /* * We are setting rpc_callerr here given that * libnsl is not reentrant thereby * reinitializing the TSD. If not set here then * success could be returned even though refresh * failed. */ rpc_callerr.re_status = RPC_AUTHERROR; /* end of unsuccessful completion */ /* free verifier */ if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED && reply_msg.acpted_rply.ar_verf.oa_base != NULL) { xdrs->x_op = XDR_FREE; (void) xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf)); } } /* end of valid reply message */ else { rpc_callerr.re_status = RPC_CANTDECODERES; } rpc_fd_unlock(dgtbl, cu->cu_fd); return (rpc_callerr.re_status); }
/* * Called to read and interpret the event on a connectionless descriptor. * Returns 0 if successful, or a UNIX error code if failure. */ static int do_poll_clts_action(int fd, int conn_index) { int error; int ret; int flags; struct netconfig *nconf = &conn_polled[conn_index].nc; static struct t_unitdata *unitdata = NULL; static struct t_uderr *uderr = NULL; static int oldfd = -1; struct nd_hostservlist *host = NULL; struct strbuf ctl[1], data[1]; /* * We just need to have some space to consume the * message in the event we can't use the TLI interface to do the * job. * * We flush the message using getmsg(). For the control part * we allocate enough for any TPI header plus 32 bytes for address * and options. For the data part, there is nothing magic about * the size of the array, but 256 bytes is probably better than * 1 byte, and we don't expect any data portion anyway. * * If the array sizes are too small, we handle this because getmsg() * (called to consume the message) will return MOREDATA|MORECTL. * Thus we just call getmsg() until it's read the message. */ char ctlbuf[sizeof (union T_primitives) + 32]; char databuf[256]; /* * If this is the same descriptor as the last time * do_poll_clts_action was called, we can save some * de-allocation and allocation. */ if (oldfd != fd) { oldfd = fd; if (unitdata) { (void) t_free((char *)unitdata, T_UNITDATA); unitdata = NULL; } if (uderr) { (void) t_free((char *)uderr, T_UDERROR); uderr = NULL; } } /* * Allocate a unitdata structure for receiving the event. */ if (unitdata == NULL) { /* LINTED pointer alignment */ unitdata = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ALL); if (unitdata == NULL) { if (t_errno == TSYSERR) { /* * Save the error code across * syslog(), just in case * syslog() gets its own error * and therefore overwrites errno. */ error = errno; (void) syslog(LOG_ERR, "t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed: %m", fd, nconf->nc_proto); return (error); } (void) syslog(LOG_ERR, "t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed TLI error %d", fd, nconf->nc_proto, t_errno); goto flush_it; } } try_again: flags = 0; /* * The idea is we wait for T_UNITDATA_IND's. Of course, * we don't get any, because rpcmod filters them out. * However, we need to call t_rcvudata() to let TLI * tell us we have a T_UDERROR_IND. * * algorithm is: * t_rcvudata(), expecting TLOOK. * t_look(), expecting T_UDERR. * t_rcvuderr(), expecting success (0). * expand destination address into ASCII, * and dump it. */ ret = t_rcvudata(fd, unitdata, &flags); if (ret == 0 || t_errno == TBUFOVFLW) { (void) syslog(LOG_WARNING, "t_rcvudata(file descriptor %d/transport %s) got unexpected data, %d bytes", fd, nconf->nc_proto, unitdata->udata.len); /* * Even though we don't expect any data, in case we do, * keep reading until there is no more. */ if (flags & T_MORE) goto try_again; return (0); } switch (t_errno) { case TNODATA: return (0); case TSYSERR: /* * System errors are returned to caller. * Save the error code across * syslog(), just in case * syslog() gets its own error * and therefore overwrites errno. */ error = errno; (void) syslog(LOG_ERR, "t_rcvudata(file descriptor %d/transport %s) %m", fd, nconf->nc_proto); return (error); case TLOOK: break; default: (void) syslog(LOG_ERR, "t_rcvudata(file descriptor %d/transport %s) TLI error %d", fd, nconf->nc_proto, t_errno); goto flush_it; } ret = t_look(fd); switch (ret) { case 0: return (0); case -1: /* * System errors are returned to caller. */ if (t_errno == TSYSERR) { /* * Save the error code across * syslog(), just in case * syslog() gets its own error * and therefore overwrites errno. */ error = errno; (void) syslog(LOG_ERR, "t_look(file descriptor %d/transport %s) %m", fd, nconf->nc_proto); return (error); } (void) syslog(LOG_ERR, "t_look(file descriptor %d/transport %s) TLI error %d", fd, nconf->nc_proto, t_errno); goto flush_it; case T_UDERR: break; default: (void) syslog(LOG_WARNING, "t_look(file descriptor %d/transport %s) returned %d not T_UDERR (%d)", fd, nconf->nc_proto, ret, T_UDERR); } if (uderr == NULL) { /* LINTED pointer alignment */ uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL); if (uderr == NULL) { if (t_errno == TSYSERR) { /* * Save the error code across * syslog(), just in case * syslog() gets its own error * and therefore overwrites errno. */ error = errno; (void) syslog(LOG_ERR, "t_alloc(file descriptor %d/transport %s, T_UDERROR) failed: %m", fd, nconf->nc_proto); return (error); } (void) syslog(LOG_ERR, "t_alloc(file descriptor %d/transport %s, T_UDERROR) failed TLI error: %d", fd, nconf->nc_proto, t_errno); goto flush_it; } } ret = t_rcvuderr(fd, uderr); if (ret == 0) { /* * Save the datagram error in errno, so that the * %m argument to syslog picks up the error string. */ errno = uderr->error; /* * Log the datagram error, then log the host that * probably triggerred. Cannot log both in the * same transaction because of packet size limitations * in /dev/log. */ (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, "NFS response over <file descriptor %d/transport %s> generated error: %m", fd, nconf->nc_proto); /* * Try to map the client's address back to a * name. */ ret = netdir_getbyaddr(nconf, &host, &uderr->addr); if (ret != -1 && host && host->h_cnt > 0 && host->h_hostservs) { (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, "Bad NFS response was sent to client with host name: %s; service port: %s", host->h_hostservs->h_host, host->h_hostservs->h_serv); } else { int i, j; char *buf; char *hex = "0123456789abcdef"; /* * Mapping failed, print the whole thing * in ASCII hex. */ buf = (char *)malloc(uderr->addr.len * 2 + 1); for (i = 0, j = 0; i < uderr->addr.len; i++, j += 2) { buf[j] = hex[((uderr->addr.buf[i]) >> 4) & 0xf]; buf[j+1] = hex[uderr->addr.buf[i] & 0xf]; } buf[j] = '\0'; (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, "Bad NFS response was sent to client with transport address: 0x%s", buf); free((void *)buf); } if (ret == 0 && host != NULL) netdir_free((void *)host, ND_HOSTSERVLIST); return (0); }
/* * This routine is designed to be able to "ping" * a list of hosts and create a list of responding * hosts sorted by response time. * This must be done without any prior * contact with the host - therefore the "ping" * must be to a "well-known" address. The outstanding * candidate here is the address of "rpcbind". * * A response to a ping is no guarantee that the host * is running NFS, has a mount daemon, or exports * the required filesystem. If the subsequent * mount attempt fails then the host will be marked * "ignore" and the host list will be re-pinged * (sans the bad host). This process continues * until a successful mount is achieved or until * there are no hosts left to try. */ enum clnt_stat nfs_cast(struct mapfs *mfs_in, struct mapfs **mfs_out, int timeout) { enum clnt_stat stat; AUTH *sys_auth = authsys_create_default(); XDR xdr_stream; register XDR *xdrs = &xdr_stream; int outlen; int if_inx; int tsec; int flag; int sent, addr_cnt, rcvd, if_cnt; fd_set readfds, mask; register ulong_t xid; /* xid - unique per addr */ register int i; struct rpc_msg msg; struct timeval t, rcv_timeout; char outbuf[UDPMSGSIZE], inbuf[UDPMSGSIZE]; struct t_unitdata t_udata, t_rdata; struct nd_hostserv hs; struct nd_addrlist *retaddrs; struct transp *tr_head; struct transp *trans, *prev_trans; struct addrs *a, *prev_addr; struct tstamps *ts, *prev_ts; NCONF_HANDLE *nc = NULL; struct netconfig *nconf; struct rlimit rl; int dtbsize; struct mapfs *mfs; /* * For each connectionless transport get a list of * host addresses. Any single host may have * addresses on several transports. */ addr_cnt = sent = rcvd = 0; tr_head = NULL; FD_ZERO(&mask); /* * Set the default select size to be the maximum FD_SETSIZE, unless * the current rlimit is lower. */ dtbsize = FD_SETSIZE; if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { if (rl.rlim_cur < FD_SETSIZE) dtbsize = rl.rlim_cur; } prev_trans = NULL; prev_addr = NULL; prev_ts = NULL; for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) { if (trace > 2) trace_prt(1, "nfs_cast: host=%s\n", mfs->mfs_host); nc = setnetconfig(); if (nc == NULL) { stat = RPC_CANTSEND; goto done_broad; } while (nconf = getnetconfig(nc)) { if (!(nconf->nc_flag & NC_VISIBLE) || nconf->nc_semantics != NC_TPI_CLTS || (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)) continue; trans = (struct transp *)malloc(sizeof (*trans)); if (trans == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(trans, 0, sizeof (*trans)); if (tr_head == NULL) tr_head = trans; else prev_trans->tr_next = trans; prev_trans = trans; trans->tr_fd = t_open(nconf->nc_device, O_RDWR, NULL); if (trans->tr_fd < 0) { syslog(LOG_ERR, "nfscast: t_open: %s:%m", nconf->nc_device); stat = RPC_CANTSEND; goto done_broad; } if (t_bind(trans->tr_fd, (struct t_bind *)NULL, (struct t_bind *)NULL) < 0) { syslog(LOG_ERR, "nfscast: t_bind: %m"); stat = RPC_CANTSEND; goto done_broad; } trans->tr_taddr = /* LINTED pointer alignment */ (struct t_bind *)t_alloc(trans->tr_fd, T_BIND, T_ADDR); if (trans->tr_taddr == (struct t_bind *)NULL) { syslog(LOG_ERR, "nfscast: t_alloc: %m"); stat = RPC_SYSTEMERROR; goto done_broad; } trans->tr_device = nconf->nc_device; FD_SET(trans->tr_fd, &mask); if_inx = 0; hs.h_host = mfs->mfs_host; hs.h_serv = "rpcbind"; if (netdir_getbyname(nconf, &hs, &retaddrs) == ND_OK) { /* * If mfs->ignore is previously set for * this map, clear it. Because a host can * have either v6 or v4 address */ if (mfs->mfs_ignore == 1) mfs->mfs_ignore = 0; a = (struct addrs *)malloc(sizeof (*a)); if (a == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(a, 0, sizeof (*a)); if (trans->tr_addrs == NULL) trans->tr_addrs = a; else prev_addr->addr_next = a; prev_addr = a; a->addr_if_tstamps = NULL; a->addr_mfs = mfs; a->addr_addrs = retaddrs; if_cnt = retaddrs->n_cnt; while (if_cnt--) { ts = (struct tstamps *) malloc(sizeof (*ts)); if (ts == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(ts, 0, sizeof (*ts)); ts->ts_penalty = mfs->mfs_penalty; if (a->addr_if_tstamps == NULL) a->addr_if_tstamps = ts; else prev_ts->ts_next = ts; prev_ts = ts; ts->ts_inx = if_inx++; addr_cnt++; } break; } else { mfs->mfs_ignore = 1; if (verbose) syslog(LOG_ERR, "%s:%s address not known", mfs->mfs_host, strcmp(nconf->nc_proto, NC_INET)?"IPv6":"IPv4"); } } /* while */ endnetconfig(nc); nc = NULL; } /* for */ if (addr_cnt == 0) { syslog(LOG_ERR, "nfscast: couldn't find addresses"); stat = RPC_CANTSEND; goto done_broad; } (void) gettimeofday(&t, (struct timezone *)0); xid = (getpid() ^ t.tv_sec ^ t.tv_usec) & ~0xFF; t.tv_usec = 0; /* serialize the RPC header */ msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = RPCBPROG; /* * we can not use RPCBVERS here since it doesn't exist in 4.X, * the fix to bug 1139883 has made the 4.X portmapper silent to * version mismatches. This causes the RPC call to the remote * portmapper to simply be ignored if it's not Version 2. */ msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = NULLPROC; if (sys_auth == (AUTH *)NULL) { stat = RPC_SYSTEMERROR; goto done_broad; } msg.rm_call.cb_cred = sys_auth->ah_cred; msg.rm_call.cb_verf = sys_auth->ah_verf; xdrmem_create(xdrs, outbuf, sizeof (outbuf), XDR_ENCODE); if (! xdr_callmsg(xdrs, &msg)) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = (int)xdr_getpos(xdrs); xdr_destroy(xdrs); t_udata.opt.len = 0; t_udata.udata.buf = outbuf; t_udata.udata.len = outlen; /* * Basic loop: send packet to all hosts and wait for response(s). * The response timeout grows larger per iteration. * A unique xid is assigned to each address in order to * correctly match the replies. */ for (tsec = 4; timeout > 0; tsec *= 2) { timeout -= tsec; if (timeout <= 0) tsec += timeout; rcv_timeout.tv_sec = tsec; rcv_timeout.tv_usec = 0; sent = 0; for (trans = tr_head; trans; trans = trans->tr_next) { for (a = trans->tr_addrs; a; a = a->addr_next) { struct netbuf *if_netbuf = a->addr_addrs->n_addrs; ts = a->addr_if_tstamps; if_cnt = a->addr_addrs->n_cnt; while (if_cnt--) { /* * xid is the first thing in * preserialized buffer */ /* LINTED pointer alignment */ *((ulong_t *)outbuf) = htonl(xid + ts->ts_inx); (void) gettimeofday(&(ts->ts_timeval), (struct timezone *)0); /* * Check if already received * from a previous iteration. */ if (ts->ts_rcvd) { sent++; ts = ts->ts_next; continue; } t_udata.addr = *if_netbuf++; if (t_sndudata(trans->tr_fd, &t_udata) == 0) { sent++; } ts = ts->ts_next; } } } if (sent == 0) { /* no packets sent ? */ stat = RPC_CANTSEND; goto done_broad; } /* * Have sent all the packets. Now collect the responses... */ rcvd = 0; recv_again: msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.proc = xdr_void; readfds = mask; switch (select(dtbsize, &readfds, (fd_set *)NULL, (fd_set *)NULL, &rcv_timeout)) { case 0: /* Timed out */ /* * If we got at least one response in the * last interval, then don't wait for any * more. In theory we should wait for * the max weighting (penalty) value so * that a very slow server has a chance to * respond but this could take a long time * if the admin has set a high weighting * value. */ if (rcvd > 0) goto done_broad; stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error */ if (errno == EINTR) goto recv_again; syslog(LOG_ERR, "nfscast: select: %m"); if (rcvd == 0) stat = RPC_CANTRECV; goto done_broad; } /* end of select results switch */ for (trans = tr_head; trans; trans = trans->tr_next) { if (FD_ISSET(trans->tr_fd, &readfds)) break; } if (trans == NULL) goto recv_again; try_again: t_rdata.addr = trans->tr_taddr->addr; t_rdata.udata.buf = inbuf; t_rdata.udata.maxlen = sizeof (inbuf); t_rdata.udata.len = 0; t_rdata.opt.len = 0; if (t_rcvudata(trans->tr_fd, &t_rdata, &flag) < 0) { if (errno == EINTR) goto try_again; syslog(LOG_ERR, "nfscast: t_rcvudata: %s:%m", trans->tr_device); stat = RPC_CANTRECV; continue; } if (t_rdata.udata.len < sizeof (ulong_t)) goto recv_again; if (flag & T_MORE) { syslog(LOG_ERR, "nfscast: t_rcvudata: %s: buffer overflow", trans->tr_device); goto recv_again; } /* * see if reply transaction id matches sent id. * If so, decode the results. * Note: received addr is ignored, it could be * different from the send addr if the host has * more than one addr. */ xdrmem_create(xdrs, inbuf, (uint_t)t_rdata.udata.len, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if (msg.rm_reply.rp_stat == MSG_ACCEPTED && (msg.rm_xid & ~0xFF) == xid) { struct addrs *curr_addr; i = msg.rm_xid & 0xFF; for (curr_addr = trans->tr_addrs; curr_addr; curr_addr = curr_addr->addr_next) { for (ts = curr_addr->addr_if_tstamps; ts; ts = ts->ts_next) if (ts->ts_inx == i && !ts->ts_rcvd) { ts->ts_rcvd = 1; calc_resp_time(&ts->ts_timeval); stat = RPC_SUCCESS; rcvd++; break; } } } /* otherwise, we just ignore the errors ... */ } xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = xdr_void; (void) xdr_replymsg(xdrs, &msg); XDR_DESTROY(xdrs); if (rcvd == sent) goto done_broad; else goto recv_again; } if (!rcvd) stat = RPC_TIMEDOUT; done_broad: if (rcvd) { *mfs_out = sort_responses(tr_head); stat = RPC_SUCCESS; } if (nc) endnetconfig(nc); free_transports(tr_head); AUTH_DESTROY(sys_auth); return (stat); }
/* **************************************************************** * Recebe a resposta * **************************************************************** */ int receive_answer (void) { T_UNITDATA ud; INADDR client_addr; int flags; PMAP_REPLY dg; #if (0) /*******************************************************/ typedef struct { int r_xid; /* No. do pedido */ int r_mtype; /* Pedido/resposta */ int r_rstat; /* A Mensagem foi aceita? */ int r_verf[2]; int r_astat; /* O RPC foi executado? */ int r_port; /* Porta pedida */ } PMAP_REPLY; #endif /*******************************************************/ /* * x */ ud.addr.maxlen = sizeof (INADDR); ud.addr.buf = &client_addr; ud.udata.maxlen = sizeof (dg); ud.udata.buf = &dg; ud.opt.len = 0; /* * x */ if (t_rcvudata (udp_fd, &ud, &flags) < 0) error ("$*Não consegui receber o datagrama"); printf ("receive_answer: Recebi %d bytes\n", ud.udata.len); { int fd = creat ("rcv.dg", 0644); write (fd, &dg, ud.udata.len); } /* * Analisa a resposta */ printf ("receive_answer: Porta = %d\n", long_endian_cv (dg.r_port)); return (0); } /* end receive_answer */