int rpc_request_to_xdr(struct rpc_msg *request, char *dest, size_t len, struct iovec *dst) { XDR xdr; int ret = -1; GF_VALIDATE_OR_GOTO("rpc", dest, out); GF_VALIDATE_OR_GOTO("rpc", request, out); GF_VALIDATE_OR_GOTO("rpc", dst, out); xdrmem_create(&xdr, dest, len, XDR_ENCODE); if (!xdr_callmsg(&xdr, request)) { gf_log("rpc", GF_LOG_WARNING, "failed to encode call msg"); goto out; } dst->iov_base = dest; dst->iov_len = xdr_encoded_length(xdr); ret = 0; out: return ret; }
static bool_t svcudp_recv( register SVCXPRT *xprt, struct rpc_msg *msg) { register struct svcudp_data *su = su_data(xprt); register XDR *xdrs = &(su->su_xdrs); register ssize_t rlen; char *reply; unsigned long replylen; socklen_t len; again: len = xprt->xp_addrlen = sizeof(struct sockaddr_in); rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 0, (struct sockaddr *)&(xprt->xp_raddr), &len); if (rlen == -1 && errno == EINTR) goto again; if (rlen < 4*sizeof(uint32_t)) return (FALSE); xprt->xp_addrlen = len; xdrs->x_op = XDR_DECODE; XDR_SETPOS(xdrs, 0); if (! xdr_callmsg(xdrs, msg)) return (FALSE); su->su_xid = msg->rm_xid; if (su->su_cache != NULL) { if (cache_get(xprt, msg, &reply, &replylen)) { (void) sendto(xprt->xp_sock, reply, replylen, 0, (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen); return (TRUE); } } return (TRUE); }
static bool_t Svcudp_recv(register SVCXPRT * xprt, struct rpc_msg *msg) { register struct Svcudp_data *su = Su_data(xprt); register XDR *xdrs = &(su->su_xdrs); register int rlen; again: xprt->xp_addrlen = sizeof(struct sockaddr_in); #ifdef _FREEBSD rlen = recvfrom(xprt->xp_fd, rpc_buffer(xprt), (int)su->su_iosz, 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); #else rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int)su->su_iosz, 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); #endif if(rlen == -1 && errno == EINTR) goto again; if(rlen == -1 || rlen < 4 * sizeof(u_int32_t)) return (FALSE); xdrs->x_op = XDR_DECODE; XDR_SETPOS(xdrs, 0); if(!xdr_callmsg(xdrs, msg)) return (FALSE); su->su_xid = msg->rm_xid; return (TRUE); }
bool_t byz_recv(SVCXPRT *xpt, struct rpc_msg *m) { if (!xdr_callmsg(in_stream(xpt), m)) return FALSE; /* Save xid to put in reply */ xpt->xp_sock = m->rm_xid; return TRUE; }
static bool_t svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg) { struct svc_dg_data *su; XDR *xdrs; char *reply; struct sockaddr_storage ss; socklen_t alen; size_t replylen; ssize_t rlen; _DIAGASSERT(xprt != NULL); _DIAGASSERT(msg != NULL); su = su_data(xprt); xdrs = &(su->su_xdrs); again: alen = sizeof (struct sockaddr_storage); rlen = recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, 0, (struct sockaddr *)(void *)&ss, &alen); if (rlen == -1 && errno == EINTR) goto again; if (rlen == -1 || (rlen < (ssize_t)(4 * sizeof (u_int32_t)))) return (FALSE); if (xprt->xp_rtaddr.len < alen) { if (xprt->xp_rtaddr.len != 0) mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.len); xprt->xp_rtaddr.buf = mem_alloc(alen); xprt->xp_rtaddr.len = alen; } memcpy(xprt->xp_rtaddr.buf, &ss, alen); #ifdef PORTMAP if (ss.ss_family == AF_INET) { xprt->xp_raddr = *(struct sockaddr_in *)xprt->xp_rtaddr.buf; xprt->xp_addrlen = sizeof (struct sockaddr_in); } #endif xdrs->x_op = XDR_DECODE; XDR_SETPOS(xdrs, 0); if (! xdr_callmsg(xdrs, msg)) { return (FALSE); } su->su_xid = msg->rm_xid; if (su->su_cache != NULL) { if (cache_get(xprt, msg, &reply, &replylen)) { (void)sendto(xprt->xp_fd, reply, replylen, 0, (struct sockaddr *)(void *)&ss, alen); return (FALSE); } } return (TRUE); }
static bool_t svcudp_recv( SVCXPRT *xprt, struct rpc_msg *msg) { struct msghdr dummy; struct iovec dummy_iov[1]; struct svcudp_data *su = su_data(xprt); XDR *xdrs = &su->su_xdrs; int rlen; char *reply; uint32_t replylen; socklen_t addrlen; again: memset(&dummy, 0, sizeof(dummy)); dummy_iov[0].iov_base = rpc_buffer(xprt); dummy_iov[0].iov_len = (int) su->su_iosz; dummy.msg_iov = dummy_iov; dummy.msg_iovlen = 1; dummy.msg_namelen = xprt->xp_laddrlen = sizeof(struct sockaddr_in); dummy.msg_name = (char *) &xprt->xp_laddr; rlen = recvmsg(xprt->xp_sock, &dummy, MSG_PEEK); if (rlen == -1) { if (errno == EINTR) goto again; else return (FALSE); } addrlen = sizeof(struct sockaddr_in); rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 0, (struct sockaddr *)&(xprt->xp_raddr), &addrlen); if (rlen == -1 && errno == EINTR) goto again; if (rlen < (int) (4*sizeof(uint32_t))) return (FALSE); xprt->xp_addrlen = addrlen; xdrs->x_op = XDR_DECODE; XDR_SETPOS(xdrs, 0); if (! xdr_callmsg(xdrs, msg)) return (FALSE); su->su_xid = msg->rm_xid; if (su->su_cache != NULL) { if (cache_get(xprt, msg, &reply, &replylen)) { (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen); return (TRUE); } } return (TRUE); }
static bool_t Svctcp_recv(SVCXPRT * xprt, register struct rpc_msg *msg) { register struct tcp_conn *cd = (struct tcp_conn *)(xprt->xp_p1); register XDR *xdrs = &(cd->xdrs); xdrs->x_op = XDR_DECODE; (void)xdrrec_skiprecord(xdrs); if(xdr_callmsg(xdrs, msg)) { cd->x_id = msg->rm_xid; return (TRUE); } return (FALSE); }
static bool_t svcraw_recv(SVCXPRT *xprt, struct rpc_msg *msg) { register struct svcraw_private *srp = svcraw_private; register XDR *xdrs; if (srp == 0) return (0); xdrs = &srp->xdr_stream; xdrs->x_op = XDR_DECODE; XDR_SETPOS(xdrs, 0); if (! xdr_callmsg(xdrs, msg)) return (FALSE); return (TRUE); }
static bool_t svctcp_recv (SVCXPRT *xprt, struct rpc_msg *msg) { struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1); XDR *xdrs = &(cd->xdrs); xdrs->x_op = XDR_DECODE; (void) xdrrec_skiprecord (xdrs); if (xdr_callmsg (xdrs, msg)) { cd->x_id = msg->rm_xid; return TRUE; } cd->strm_stat = XPRT_DIED; /* XXXX */ return FALSE; }
struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_decode_bufsize) { struct rpc_pdu *pdu; struct rpc_msg msg; if (rpc == NULL) { printf("trying to allocate rpc pdu on NULL context\n"); return NULL; } pdu = malloc(sizeof(struct rpc_pdu)); if (pdu == NULL) { printf("Failed to allocate pdu structure\n"); return NULL; } bzero(pdu, sizeof(struct rpc_pdu)); pdu->xid = rpc->xid++; pdu->cb = cb; pdu->private_data = private_data; pdu->xdr_decode_fn = xdr_decode_fn; pdu->xdr_decode_bufsize = xdr_decode_bufsize; xdrmem_create(&pdu->xdr, rpc->encodebuf, rpc->encodebuflen, XDR_ENCODE); xdr_setpos(&pdu->xdr, 4); /* skip past the record marker */ bzero(&msg, sizeof(struct rpc_msg)); msg.rm_xid = pdu->xid; msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = program; msg.rm_call.cb_vers = version; msg.rm_call.cb_proc = procedure; msg.rm_call.cb_cred = rpc->auth->ah_cred; msg.rm_call.cb_verf = rpc->auth->ah_verf; if (xdr_callmsg(&pdu->xdr, &msg) == 0) { printf("xdr_callmsg failed\n"); xdr_destroy(&pdu->xdr); free(pdu); return NULL; } return pdu; }
/*ARGSUSED*/ static bool_t svc_raw_recv(SVCXPRT *xprt, struct rpc_msg *msg) { struct svc_raw_private *srp; XDR *xdrs; (void) mutex_lock(&svcraw_lock); srp = svc_raw_private; if (srp == NULL) { (void) mutex_unlock(&svcraw_lock); return (FALSE); } (void) mutex_unlock(&svcraw_lock); xdrs = &srp->xdr_stream; xdrs->x_op = XDR_DECODE; (void) XDR_SETPOS(xdrs, 0); return (xdr_callmsg(xdrs, msg)); }
/* Decodes the XDR format in msgbuf into rpc_msg. * The remaining payload is returned into payload. */ int xdr_to_rpc_call (char *msgbuf, size_t len, struct rpc_msg *call, struct iovec *payload, char *credbytes, char *verfbytes) { XDR xdr; char opaquebytes[MAX_AUTH_BYTES]; struct opaque_auth *oa = NULL; int ret = -1; GF_VALIDATE_OR_GOTO ("rpc", msgbuf, out); GF_VALIDATE_OR_GOTO ("rpc", call, out); memset (call, 0, sizeof (*call)); oa = &call->rm_call.cb_cred; if (!credbytes) oa->oa_base = opaquebytes; else oa->oa_base = credbytes; oa = &call->rm_call.cb_verf; if (!verfbytes) oa->oa_base = opaquebytes; else oa->oa_base = verfbytes; xdrmem_create (&xdr, msgbuf, len, XDR_DECODE); if (!xdr_callmsg (&xdr, call)) { gf_log ("rpc", GF_LOG_WARNING, "failed to decode call msg"); goto out; } if (payload) { payload->iov_base = xdr_decoded_remaining_addr (xdr); payload->iov_len = xdr_decoded_remaining_len (xdr); } ret = 0; out: return ret; }
static bool_t svcunix_recv (SVCXPRT *xprt, struct rpc_msg *msg) { struct unix_conn *cd = (struct unix_conn *) (xprt->xp_p1); XDR *xdrs = &(cd->xdrs); xdrs->x_op = XDR_DECODE; xdrrec_skiprecord (xdrs); if (xdr_callmsg (xdrs, msg)) { cd->x_id = msg->rm_xid; /* set up verifiers */ #ifdef SCM_CREDENTIALS msg->rm_call.cb_verf.oa_flavor = AUTH_UNIX; msg->rm_call.cb_verf.oa_base = (caddr_t) &cm; msg->rm_call.cb_verf.oa_length = sizeof (cm); #endif return TRUE; } cd->strm_stat = XPRT_DIED; /* XXXX */ return FALSE; }
/*ARGSUSED*/ static bool svc_raw_recv(SVCXPRT *xprt, struct svc_req *req) { struct rpc_msg *msg = req->rq_msg; struct svc_raw_private *srp; XDR *xdrs; mutex_lock(&svcraw_lock); srp = svc_raw_private; if (srp == NULL) { mutex_unlock(&svcraw_lock); return (false); } mutex_unlock(&svcraw_lock); xdrs = &srp->xdr_stream; xdrs->x_op = XDR_DECODE; (void)XDR_SETPOS(xdrs, 0); if (!xdr_callmsg(xdrs, msg)) return (false); return (true); }
static bool_t svc_vc_backchannel_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr **addrp, struct mbuf **mp) { struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1; struct ct_data *ct; struct mbuf *m; XDR xdrs; sx_xlock(&xprt->xp_lock); ct = (struct ct_data *)xprt->xp_p2; if (ct == NULL) { sx_xunlock(&xprt->xp_lock); return (FALSE); } mtx_lock(&ct->ct_lock); m = cd->mreq; if (m == NULL) { xprt_inactive_self(xprt); mtx_unlock(&ct->ct_lock); sx_xunlock(&xprt->xp_lock); return (FALSE); } cd->mreq = m->m_nextpkt; mtx_unlock(&ct->ct_lock); sx_xunlock(&xprt->xp_lock); xdrmbuf_create(&xdrs, m, XDR_DECODE); if (! xdr_callmsg(&xdrs, msg)) { XDR_DESTROY(&xdrs); return (FALSE); } *addrp = NULL; *mp = xdrmbuf_getall(&xdrs); XDR_DESTROY(&xdrs); return (TRUE); }
static bool_t svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr **addrp, struct mbuf **mp) { struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1; struct uio uio; struct mbuf *m; struct socket* so = xprt->xp_socket; XDR xdrs; int error, rcvflag; uint32_t xid_plus_direction[2]; /* * Serialise access to the socket and our own record parsing * state. */ sx_xlock(&xprt->xp_lock); for (;;) { /* If we have no request ready, check pending queue. */ while (cd->mpending && (cd->mreq == NULL || cd->resid != 0 || !cd->eor)) { if (!svc_vc_process_pending(xprt)) break; } /* Process and return complete request in cd->mreq. */ if (cd->mreq != NULL && cd->resid == 0 && cd->eor) { /* * Now, check for a backchannel reply. * The XID is in the first uint32_t of the reply * and the message direction is the second one. */ if ((cd->mreq->m_len >= sizeof(xid_plus_direction) || m_length(cd->mreq, NULL) >= sizeof(xid_plus_direction)) && xprt->xp_p2 != NULL) { m_copydata(cd->mreq, 0, sizeof(xid_plus_direction), (char *)xid_plus_direction); xid_plus_direction[0] = ntohl(xid_plus_direction[0]); xid_plus_direction[1] = ntohl(xid_plus_direction[1]); /* Check message direction. */ if (xid_plus_direction[1] == REPLY) { clnt_bck_svccall(xprt->xp_p2, cd->mreq, xid_plus_direction[0]); cd->mreq = NULL; continue; } } xdrmbuf_create(&xdrs, cd->mreq, XDR_DECODE); cd->mreq = NULL; /* Check for next request in a pending queue. */ svc_vc_process_pending(xprt); if (cd->mreq == NULL || cd->resid != 0) { SOCKBUF_LOCK(&so->so_rcv); if (!soreadable(so)) xprt_inactive_self(xprt); SOCKBUF_UNLOCK(&so->so_rcv); } sx_xunlock(&xprt->xp_lock); if (! xdr_callmsg(&xdrs, msg)) { XDR_DESTROY(&xdrs); return (FALSE); } *addrp = NULL; *mp = xdrmbuf_getall(&xdrs); XDR_DESTROY(&xdrs); return (TRUE); } /* * The socket upcall calls xprt_active() which will eventually * cause the server to call us here. We attempt to * read as much as possible from the socket and put * the result in cd->mpending. If the read fails, * we have drained both cd->mpending and the socket so * we can call xprt_inactive(). */ uio.uio_resid = 1000000000; uio.uio_td = curthread; m = NULL; rcvflag = MSG_DONTWAIT; error = soreceive(so, NULL, &uio, &m, NULL, &rcvflag); if (error == EWOULDBLOCK) { /* * We must re-test for readability after * taking the lock to protect us in the case * where a new packet arrives on the socket * after our call to soreceive fails with * EWOULDBLOCK. */ SOCKBUF_LOCK(&so->so_rcv); if (!soreadable(so)) xprt_inactive_self(xprt); SOCKBUF_UNLOCK(&so->so_rcv); sx_xunlock(&xprt->xp_lock); return (FALSE); } if (error) { SOCKBUF_LOCK(&so->so_rcv); if (xprt->xp_upcallset) { xprt->xp_upcallset = 0; soupcall_clear(so, SO_RCV); } SOCKBUF_UNLOCK(&so->so_rcv); xprt_inactive_self(xprt); cd->strm_stat = XPRT_DIED; sx_xunlock(&xprt->xp_lock); return (FALSE); } if (!m) { /* * EOF - the other end has closed the socket. */ xprt_inactive_self(xprt); cd->strm_stat = XPRT_DIED; sx_xunlock(&xprt->xp_lock); return (FALSE); } if (cd->mpending) m_last(cd->mpending)->m_next = m; else cd->mpending = m; } }
int rpc_decode(u_char *buf, int len, struct rpc_msg *msg) { XDR xdrs; u_int32_t fraghdr; u_char *p, *tmp; int stat, tmplen; if (len < 20) return (0); p = buf + 4; /* If not recognizably RPC, try TCP record defragmentation */ if (pntohl(p) != CALL && pntohl(p) != REPLY) { tmp = buf; tmplen = 0; for (;;) { fraghdr = pntohl(tmp); if (FRAGLEN(fraghdr) + 4 > len) return (0); len -= 4; memmove(tmp, tmp + 4, len); tmplen += FRAGLEN(fraghdr); if (LASTFRAG(fraghdr)) break; tmp += FRAGLEN(fraghdr); len -= FRAGLEN(fraghdr); if (len < 4) return (0); } len = tmplen; } /* Decode RPC message. */ memset(msg, 0, sizeof(*msg)); if (ntohl(((struct rpc_msg *)buf)->rm_direction) == CALL) { xdrmem_create(&xdrs, buf, len, XDR_DECODE); if (!xdr_callmsg(&xdrs, msg)) { xdr_destroy(&xdrs); return (0); } } else if (ntohl(((struct rpc_msg *)buf)->rm_direction) == REPLY) { msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; xdrmem_create(&xdrs, buf, len, XDR_DECODE); if (!xdr_replymsg(&xdrs, msg)) { xdr_destroy(&xdrs); return (0); } } stat = xdr_getpos(&xdrs); xdr_destroy(&xdrs); return (stat); }
void asrv::dispatch (ref<xhinfo> xi, const char *msg, ssize_t len, const sockaddr *src) { if (!msg || len < 8 || getint (msg + 4) != CALL) { seteof (xi, src, len < 0); return; } xdrmem x (msg, len, XDR_DECODE); auto_ptr<svccb> sbp (New svccb); rpc_msg *m = &sbp->msg; if (!xdr_callmsg (x.xdrp (), m)) { trace (1) << "asrv::dispatch: xdr_callmsg failed\n"; seteof (xi, src); return; } if (m->rm_call.cb_rpcvers != RPC_MSG_VERSION) { trace (1) << "asrv::dispatch: bad RPC message version\n"; asrv_rpc_mismatch (xi, src, m->rm_xid); return; } asrv *s = xi->stab[progvers (sbp->prog (), sbp->vers ())]; if (!s || !s->cb) { if (asrvtrace >= 1) { if (s) warn ("asrv::dispatch: no callback for %s (proc = %u)\n", s->rpcprog->name, sbp->proc ()); else warn ("asrv::dispatch: invalid prog/vers %u/%u (proc = %u)\n", (u_int) sbp->prog (), (u_int) sbp->vers (), (u_int) sbp->proc ()); } asrv_accepterr (xi, src, PROG_UNAVAIL, m); return; } if (s->recv_hook) s->recv_hook (); sbp->init (s, src); if (sbp->proc () >= s->nproc) { if (asrvtrace >= 1) warn ("asrv::dispatch: invalid procno %s:%u\n", s->rpcprog->name, (u_int) sbp->proc ()); asrv_accepterr (xi, src, PROC_UNAVAIL, m); return; } if (s->isreplay (sbp.get ())) { trace (4, "replay %s:%s x=%x", s->rpcprog->name, s->tbl[m->rm_call.cb_proc].name, xidswap (m->rm_xid)) << sock2str (src) << "\n"; return; } const rpcgen_table *rtp = &s->tbl[sbp->proc ()]; sbp->arg = s->tbl[sbp->proc ()].alloc_arg (); if (!rtp->xdr_arg (x.xdrp (), sbp->arg)) { if (asrvtrace >= 1) warn ("asrv::dispatch: bad message %s:%s x=%x", s->rpcprog->name, rtp->name, xidswap (m->rm_xid)) << sock2str (src) << "\n"; asrv_accepterr (xi, src, GARBAGE_ARGS, m); s->inc_svccb_count (); s->sendreply (sbp.release (), NULL, true); return; } if (asrvtrace >= 2) { if (const authunix_parms *aup = sbp->getaup ()) trace (2, "serve %s:%s x=%x u=%u g=%u", s->rpcprog->name, rtp->name, xidswap (m->rm_xid), aup->aup_uid, aup->aup_gid) << sock2str (src) << "\n"; else if (u_int32_t i = sbp->getaui ()) trace (2, "serve %s:%s x=%x i=%u", s->rpcprog->name, rtp->name, xidswap (m->rm_xid), i) << sock2str (src) << "\n"; else trace (2, "serve %s:%s x=%x", s->rpcprog->name, rtp->name, xidswap (m->rm_xid)) << sock2str (src) << "\n"; } if (asrvtrace >= 5 && rtp->print_arg) rtp->print_arg (sbp->arg, NULL, asrvtrace - 4, "ARGS", ""); s->inc_svccb_count (); (*s->cb) (sbp.release ()); }
enum clnt_stat clnt_broadcast( unsigned long prog, /* program number */ unsigned long vers, /* version number */ unsigned long proc, /* procedure number */ xdrproc_t xargs, /* xdr routine for args */ char* argsp, /* pointer to args */ xdrproc_t xresults, /* xdr routine for results */ char* resultsp, /* pointer to results */ resultproc_t eachresult) /* call with each result obtained */ { enum clnt_stat stat; AUTH *unix_auth = authunix_create_default(); XDR xdr_stream; register XDR *xdrs = &xdr_stream; int outlen, nets; ssize_t inlen; socklen_t fromlen; register int sock; int on = 1; #ifdef FD_SETSIZE fd_set mask; fd_set readfds; #else int readfds; register int mask; #endif /* def FD_SETSIZE */ register int i; bool_t done = FALSE; register unsigned long xid; unsigned long port; struct in_addr addrs[20]; struct sockaddr_in baddr, raddr; /* broadcast and response addresses */ struct rmtcallargs a; struct rmtcallres r; struct rpc_msg msg; struct timeval t; char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE]; /* * initialization: create a socket, a broadcast address, and * preserialize the arguments into a send buffer. */ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("Cannot create socket for broadcast rpc"); stat = RPC_CANTSEND; goto done_broad; } #ifdef SO_BROADCAST if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { perror("Cannot set socket option SO_BROADCAST"); stat = RPC_CANTSEND; goto done_broad; } #endif /* def SO_BROADCAST */ #ifdef FD_SETSIZE FD_ZERO(&mask); FD_SET(sock, &mask); #else mask = (1 << sock); #endif /* def FD_SETSIZE */ nets = getbroadcastnets(addrs, sock, inbuf); bzero((char *)&baddr, sizeof (baddr)); baddr.sin_family = AF_INET; baddr.sin_port = htons(PMAPPORT); baddr.sin_addr.s_addr = htonl(INADDR_ANY); /* baddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); */ (void)gettimeofday(&t, NULL); msg.rm_xid = xid = getpid() ^ t.tv_sec ^ t.tv_usec; t.tv_usec = 0; msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = PMAPPROC_CALLIT; msg.rm_call.cb_cred = unix_auth->ah_cred; msg.rm_call.cb_verf = unix_auth->ah_verf; a.prog = prog; a.vers = vers; a.proc = proc; a.xdr_args = xargs; a.args_ptr = argsp; r.port_ptr = &port; r.xdr_results = xresults; r.results_ptr = resultsp; xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE); if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = (int)xdr_getpos(xdrs); xdr_destroy(xdrs); /* * Basic loop: broadcast a packet and wait a while for response(s). * The response timeout grows larger per iteration. */ for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) { for (i = 0; i < nets; i++) { baddr.sin_addr = addrs[i]; if (sendto(sock, outbuf, outlen, 0, (struct sockaddr *)&baddr, sizeof (struct sockaddr)) != (ssize_t)outlen) { perror("Cannot send broadcast packet"); stat = RPC_CANTSEND; goto done_broad; } } if (eachresult == NULL) { stat = RPC_SUCCESS; goto done_broad; } recv_again: msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (char*)&r; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres; readfds = mask; { struct timeval tmp = t; switch (select(sock+1, &readfds, NULL, NULL, &tmp)) { case 0: /* timed out */ stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error */ if (errno == EINTR) goto recv_again; perror("Broadcast select problem"); stat = RPC_CANTRECV; goto done_broad; } /* end of select results switch */ } /* end of temporary timeout variable */ try_again: fromlen = (socklen_t)sizeof(struct sockaddr); inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0, (struct sockaddr *)&raddr, &fromlen); if (inlen < 0) { if (errno == EINTR) goto try_again; perror("Cannot receive reply to broadcast"); stat = RPC_CANTRECV; goto done_broad; } if (inlen < sizeof(uint32_t)) goto recv_again; /* * see if reply transaction id matches sent id. * If so, decode the results. */ xdrmem_create(xdrs, inbuf, (unsigned)inlen, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if ((msg.rm_xid == xid) && (msg.rm_reply.rp_stat == MSG_ACCEPTED) && (msg.acpted_rply.ar_stat == SUCCESS)) { raddr.sin_port = htons((unsigned short)port); done = (*eachresult)(resultsp, &raddr); } /* otherwise, we just ignore the errors ... */ } else { #ifdef notdef /* some kind of deserialization problem ... */ if (msg.rm_xid == xid) (void)fprintf(stderr, "Broadcast deserialization problem"); /* otherwise, just random garbage */ #endif } xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; (void)xdr_replymsg(xdrs, &msg); (void)(*xresults)(xdrs, resultsp); xdr_destroy(xdrs); if (done) { stat = RPC_SUCCESS; goto done_broad; } else { goto recv_again; } } done_broad: (void)close(sock); AUTH_DESTROY(unix_auth); return (stat); }
enum clnt_stat clnt_broadcast(u_long prog, /* program number */ u_long vers, /* version number */ u_long proc, /* procedure number */ xdrproc_t xargs, /* xdr routine for args */ caddr_t argsp, /* pointer to args */ xdrproc_t xresults, /* xdr routine for results */ caddr_t resultsp, /* pointer to results */ resultproc_t eachresult) /* call with each result obtained */ { enum clnt_stat stat; AUTH *unix_auth; XDR xdr_stream; XDR *xdrs = &xdr_stream; int outlen, inlen, nets; socklen_t fromlen; int sock = -1; int on = 1; struct pollfd pfd[1]; int i; int timo; bool_t done = FALSE; u_long xid; u_long port; struct in_addr *addrs = NULL; struct sockaddr_in baddr, raddr; /* broadcast and response addresses */ struct rmtcallargs a; struct rmtcallres r; struct rpc_msg msg; char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE]; if ((unix_auth = authunix_create_default()) == NULL) { stat = RPC_AUTHERROR; goto done_broad; } /* * initialization: create a socket, a broadcast address, and * preserialize the arguments into a send buffer. */ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { stat = RPC_CANTSEND; goto done_broad; } #ifdef SO_BROADCAST if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { stat = RPC_CANTSEND; goto done_broad; } #endif /* def SO_BROADCAST */ pfd[0].fd = sock; pfd[0].events = POLLIN; nets = newgetbroadcastnets(&addrs); if (nets == 0) { stat = RPC_CANTSEND; goto done_broad; } memset(&baddr, 0, sizeof (baddr)); baddr.sin_len = sizeof(struct sockaddr_in); baddr.sin_family = AF_INET; baddr.sin_port = htons(PMAPPORT); baddr.sin_addr.s_addr = htonl(INADDR_ANY); msg.rm_xid = xid = arc4random(); msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = PMAPPROC_CALLIT; msg.rm_call.cb_cred = unix_auth->ah_cred; msg.rm_call.cb_verf = unix_auth->ah_verf; a.prog = prog; a.vers = vers; a.proc = proc; a.xdr_args = xargs; a.args_ptr = argsp; r.port_ptr = &port; r.xdr_results = xresults; r.results_ptr = resultsp; xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE); if (!xdr_callmsg(xdrs, &msg) || !xdr_rmtcall_args(xdrs, &a)) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = (int)xdr_getpos(xdrs); xdr_destroy(xdrs); /* * Basic loop: broadcast a packet and wait a while for response(s). * The response timeout grows larger per iteration. * * XXX This will loop about 5 times the stop. If there are * lots of signals being received by the process it will quit * send them all in one quick burst, not paying attention to * the intended function of sending them slowly over half a * minute or so */ for (timo = 4000; timo <= 14000; timo += 2000) { for (i = 0; i < nets; i++) { baddr.sin_addr = addrs[i]; if (sendto(sock, outbuf, outlen, 0, (struct sockaddr *)&baddr, sizeof (struct sockaddr)) != outlen) { stat = RPC_CANTSEND; goto done_broad; } } if (eachresult == NULL) { stat = RPC_SUCCESS; goto done_broad; } recv_again: msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)&r; msg.acpted_rply.ar_results.proc = xdr_rmtcallres; switch (poll(pfd, 1, timo)) { case 0: /* timed out */ stat = RPC_TIMEDOUT; continue; case 1: if (pfd[0].revents & POLLNVAL) errno = EBADF; else if (pfd[0].revents & POLLERR) errno = EIO; else break; /* FALLTHROUGH */ case -1: /* some kind of error */ if (errno == EINTR) goto recv_again; stat = RPC_CANTRECV; goto done_broad; } try_again: fromlen = sizeof(struct sockaddr); inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0, (struct sockaddr *)&raddr, &fromlen); if (inlen < 0) { if (errno == EINTR) goto try_again; stat = RPC_CANTRECV; goto done_broad; } if (inlen < sizeof(u_int32_t)) goto recv_again; /* * see if reply transaction id matches sent id. * If so, decode the results. */ xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if ((msg.rm_xid == xid) && (msg.rm_reply.rp_stat == MSG_ACCEPTED) && (msg.acpted_rply.ar_stat == SUCCESS)) { raddr.sin_port = htons((u_short)port); done = (*eachresult)(resultsp, &raddr); } /* otherwise, we just ignore the errors ... */ } xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = xdr_void; (void)xdr_replymsg(xdrs, &msg); (void)(*xresults)(xdrs, resultsp); xdr_destroy(xdrs); if (done) { stat = RPC_SUCCESS; goto done_broad; } else { goto recv_again; } } done_broad: if (addrs) free(addrs); if (sock >= 0) (void)close(sock); if (unix_auth != NULL) AUTH_DESTROY(unix_auth); return (stat); }
/* * rpc_broadcast_exp() * * prog - program number * vers - version number * proc - procedure number * xargs - xdr routine for args * argsp - pointer to args * xresults - xdr routine for results * resultsp - pointer to results * eachresult - call with each result obtained * inittime - how long to wait initially * waittime - maximum time to wait * nettype - transport type */ enum clnt_stat rpc_broadcast_exp(rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp, xdrproc_t xresults, caddr_t resultsp, resultproc_t eachresult, int inittime, int waittime, const char *nettype) { enum clnt_stat stat = RPC_SUCCESS; /* Return status */ XDR xdr_stream; /* XDR stream */ XDR *xdrs = &xdr_stream; struct rpc_msg msg; /* RPC message */ struct timeval t; char *outbuf = NULL; /* Broadcast msg buffer */ char *inbuf = NULL; /* Reply buf */ int inlen; u_int maxbufsize = 0; AUTH *sys_auth = authunix_create_default(); u_int i; void *handle; char uaddress[1024]; /* A self imposed limit */ char *uaddrp = uaddress; int pmap_reply_flag; /* reply recvd from PORTMAP */ /* An array of all the suitable broadcast transports */ struct { int fd; /* File descriptor */ int af; int proto; struct netconfig *nconf; /* Netconfig structure */ u_int asize; /* Size of the addr buf */ u_int dsize; /* Size of the data buf */ struct sockaddr_storage raddr; /* Remote address */ broadlist_t nal; } fdlist[MAXBCAST]; struct pollfd pfd[MAXBCAST]; size_t fdlistno = 0; struct r_rpcb_rmtcallargs barg; /* Remote arguments */ struct r_rpcb_rmtcallres bres; /* Remote results */ size_t outlen; struct netconfig *nconf; int msec; int pollretval; int fds_found; #ifdef PORTMAP size_t outlen_pmap = 0; u_long port; /* Remote port number */ int pmap_flag = 0; /* UDP exists ? */ char *outbuf_pmap = NULL; struct rmtcallargs barg_pmap; /* Remote arguments */ struct rmtcallres bres_pmap; /* Remote results */ u_int udpbufsz = 0; #endif /* PORTMAP */ if (sys_auth == NULL) { return (RPC_SYSTEMERROR); } /* * initialization: create a fd, a broadcast address, and send the * request on the broadcast transport. * Listen on all of them and on replies, call the user supplied * function. */ if (nettype == NULL) nettype = "datagram_n"; if ((handle = __rpc_setconf(nettype)) == NULL) { AUTH_DESTROY(sys_auth); return (RPC_UNKNOWNPROTO); } while ((nconf = __rpc_getconf(handle)) != NULL) { int fd; struct __rpc_sockinfo si; if (nconf->nc_semantics != NC_TPI_CLTS) continue; if (fdlistno >= MAXBCAST) break; /* No more slots available */ if (!__rpc_nconf2sockinfo(nconf, &si)) continue; TAILQ_INIT(&fdlist[fdlistno].nal); if (__rpc_getbroadifs(si.si_af, si.si_proto, si.si_socktype, &fdlist[fdlistno].nal) == 0) continue; fd = _socket(si.si_af, si.si_socktype, si.si_proto); if (fd < 0) { stat = RPC_CANTSEND; continue; } fdlist[fdlistno].af = si.si_af; fdlist[fdlistno].proto = si.si_proto; fdlist[fdlistno].fd = fd; fdlist[fdlistno].nconf = nconf; fdlist[fdlistno].asize = __rpc_get_a_size(si.si_af); pfd[fdlistno].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; pfd[fdlistno].fd = fdlist[fdlistno].fd = fd; fdlist[fdlistno].dsize = __rpc_get_t_size(si.si_af, si.si_proto, 0); if (maxbufsize <= fdlist[fdlistno].dsize) maxbufsize = fdlist[fdlistno].dsize; #ifdef PORTMAP if (si.si_af == AF_INET && si.si_proto == IPPROTO_UDP) { udpbufsz = fdlist[fdlistno].dsize; if ((outbuf_pmap = malloc(udpbufsz)) == NULL) { _close(fd); stat = RPC_SYSTEMERROR; goto done_broad; } pmap_flag = 1; } #endif /* PORTMAP */ fdlistno++; } if (fdlistno == 0) { if (stat == RPC_SUCCESS) stat = RPC_UNKNOWNPROTO; goto done_broad; } if (maxbufsize == 0) { if (stat == RPC_SUCCESS) stat = RPC_CANTSEND; goto done_broad; } inbuf = malloc(maxbufsize); outbuf = malloc(maxbufsize); if ((inbuf == NULL) || (outbuf == NULL)) { stat = RPC_SYSTEMERROR; goto done_broad; } /* Serialize all the arguments which have to be sent */ (void) gettimeofday(&t, NULL); msg.rm_xid = __RPC_GETXID(&t); msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = RPCBPROG; msg.rm_call.cb_vers = RPCBVERS; msg.rm_call.cb_proc = RPCBPROC_CALLIT; barg.prog = prog; barg.vers = vers; barg.proc = proc; barg.args.args_val = argsp; barg.xdr_args = xargs; bres.addr = uaddrp; bres.results.results_val = resultsp; bres.xdr_res = xresults; msg.rm_call.cb_cred = sys_auth->ah_cred; msg.rm_call.cb_verf = sys_auth->ah_verf; xdrmem_create(xdrs, outbuf, maxbufsize, XDR_ENCODE); if ((!xdr_callmsg(xdrs, &msg)) || (!xdr_rpcb_rmtcallargs(xdrs, (struct rpcb_rmtcallargs *)(void *)&barg))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = xdr_getpos(xdrs); xdr_destroy(xdrs); #ifdef PORTMAP /* Prepare the packet for version 2 PORTMAP */ if (pmap_flag) { msg.rm_xid++; /* One way to distinguish */ msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = PMAPPROC_CALLIT; barg_pmap.prog = prog; barg_pmap.vers = vers; barg_pmap.proc = proc; barg_pmap.args_ptr = argsp; barg_pmap.xdr_args = xargs; bres_pmap.port_ptr = &port; bres_pmap.xdr_results = xresults; bres_pmap.results_ptr = resultsp; xdrmem_create(xdrs, outbuf_pmap, udpbufsz, XDR_ENCODE); if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &barg_pmap))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen_pmap = xdr_getpos(xdrs); xdr_destroy(xdrs); } #endif /* PORTMAP */ /* * Basic loop: broadcast the packets to transports which * support data packets of size such that one can encode * all the arguments. * Wait a while for response(s). * The response timeout grows larger per iteration. */ for (msec = inittime; msec <= waittime; msec += msec) { struct broadif *bip; /* Broadcast all the packets now */ for (i = 0; i < fdlistno; i++) { if (fdlist[i].dsize < outlen) { stat = RPC_CANTSEND; continue; } for (bip = TAILQ_FIRST(&fdlist[i].nal); bip != NULL; bip = TAILQ_NEXT(bip, link)) { void *addr; addr = &bip->broadaddr; __rpc_broadenable(fdlist[i].af, fdlist[i].fd, bip); /* * Only use version 3 if lowvers is not set */ if (!__rpc_lowvers) if (_sendto(fdlist[i].fd, outbuf, outlen, 0, (struct sockaddr*)addr, (size_t)fdlist[i].asize) != outlen) { #ifdef RPC_DEBUG perror("sendto"); #endif warnx("clnt_bcast: cannot send " "broadcast packet"); stat = RPC_CANTSEND; continue; } #ifdef RPC_DEBUG if (!__rpc_lowvers) fprintf(stderr, "Broadcast packet sent " "for %s\n", fdlist[i].nconf->nc_netid); #endif #ifdef PORTMAP /* * Send the version 2 packet also * for UDP/IP */ if (pmap_flag && fdlist[i].proto == IPPROTO_UDP) { if (_sendto(fdlist[i].fd, outbuf_pmap, outlen_pmap, 0, addr, (size_t)fdlist[i].asize) != outlen_pmap) { warnx("clnt_bcast: " "Cannot send broadcast packet"); stat = RPC_CANTSEND; continue; } } #ifdef RPC_DEBUG fprintf(stderr, "PMAP Broadcast packet " "sent for %s\n", fdlist[i].nconf->nc_netid); #endif #endif /* PORTMAP */ } /* End for sending all packets on this transport */ } /* End for sending on all transports */ if (eachresult == NULL) { stat = RPC_SUCCESS; goto done_broad; } /* * Get all the replies from these broadcast requests */ recv_again: switch (pollretval = _poll(pfd, fdlistno, msec)) { case 0: /* timed out */ stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error - we ignore it */ goto recv_again; } /* end of poll results switch */ for (i = fds_found = 0; i < fdlistno && fds_found < pollretval; i++) { bool_t done = FALSE; if (pfd[i].revents == 0) continue; else if (pfd[i].revents & POLLNVAL) { /* * Something bad has happened to this descri- * ptor. We can cause _poll() to ignore * it simply by using a negative fd. We do that * rather than compacting the pfd[] and fdlist[] * arrays. */ pfd[i].fd = -1; fds_found++; continue; } else fds_found++; #ifdef RPC_DEBUG fprintf(stderr, "response for %s\n", fdlist[i].nconf->nc_netid); #endif try_again: inlen = _recvfrom(fdlist[i].fd, inbuf, fdlist[i].dsize, 0, (struct sockaddr *)(void *)&fdlist[i].raddr, &fdlist[i].asize); if (inlen < 0) { if (errno == EINTR) goto try_again; warnx("clnt_bcast: Cannot receive reply to " "broadcast"); stat = RPC_CANTRECV; continue; } if (inlen < sizeof (u_int32_t)) continue; /* Drop that and go ahead */ /* * see if reply transaction id matches sent id. * If so, decode the results. If return id is xid + 1 * it was a PORTMAP reply */ if (*((u_int32_t *)(void *)(inbuf)) == *((u_int32_t *)(void *)(outbuf))) { pmap_reply_flag = 0; msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)(void *)&bres; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rpcb_rmtcallres; #ifdef PORTMAP } else if (pmap_flag && *((u_int32_t *)(void *)(inbuf)) == *((u_int32_t *)(void *)(outbuf_pmap))) { pmap_reply_flag = 1; msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)(void *)&bres_pmap; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres; #endif /* PORTMAP */ } else continue; xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && (msg.acpted_rply.ar_stat == SUCCESS)) { struct netbuf taddr, *np; struct sockaddr_in *sin; #ifdef PORTMAP if (pmap_flag && pmap_reply_flag) { sin = (struct sockaddr_in *) (void *)&fdlist[i].raddr; sin->sin_port = htons((u_short)port); taddr.len = taddr.maxlen = fdlist[i].raddr.ss_len; taddr.buf = &fdlist[i].raddr; done = (*eachresult)(resultsp, &taddr, fdlist[i].nconf); } else { #endif /* PORTMAP */ #ifdef RPC_DEBUG fprintf(stderr, "uaddr %s\n", uaddrp); #endif np = uaddr2taddr( fdlist[i].nconf, uaddrp); done = (*eachresult)(resultsp, np, fdlist[i].nconf); free(np); #ifdef PORTMAP } #endif /* PORTMAP */ } /* otherwise, we just ignore the errors ... */ } /* else some kind of deserialization problem ... */ xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; (void) xdr_replymsg(xdrs, &msg); (void) (*xresults)(xdrs, resultsp); XDR_DESTROY(xdrs); if (done) { stat = RPC_SUCCESS; goto done_broad; } else { goto recv_again; } } /* The recv for loop */ } /* The giant for loop */ done_broad: free(inbuf); free(outbuf); #ifdef PORTMAP free(outbuf_pmap); #endif /* PORTMAP */ for (i = 0; i < fdlistno; i++) { (void)_close(fdlist[i].fd); __rpc_freebroadifs(&fdlist[i].nal); } AUTH_DESTROY(sys_auth); (void) __rpc_endconf(handle); return (stat); }
/* * 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); }
static bool_t svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr **addrp, struct mbuf **mp) { struct uio uio; struct sockaddr *raddr; struct mbuf *mreq; XDR xdrs; int error, rcvflag; /* * Serialise access to the socket. */ sx_xlock(&xprt->xp_lock); /* * The socket upcall calls xprt_active() which will eventually * cause the server to call us here. We attempt to read a * packet from the socket and process it. If the read fails, * we have drained all pending requests so we call * xprt_inactive(). */ uio.uio_resid = 1000000000; uio.uio_td = curthread; mreq = NULL; rcvflag = MSG_DONTWAIT; error = soreceive(xprt->xp_socket, &raddr, &uio, &mreq, NULL, &rcvflag); if (error == EWOULDBLOCK) { /* * We must re-test for readability after taking the * lock to protect us in the case where a new packet * arrives on the socket after our call to soreceive * fails with EWOULDBLOCK. The pool lock protects us * from racing the upcall after our soreadable() call * returns false. */ mtx_lock(&xprt->xp_pool->sp_lock); if (!soreadable(xprt->xp_socket)) xprt_inactive_locked(xprt); mtx_unlock(&xprt->xp_pool->sp_lock); sx_xunlock(&xprt->xp_lock); return (FALSE); } if (error) { SOCKBUF_LOCK(&xprt->xp_socket->so_rcv); soupcall_clear(xprt->xp_socket, SO_RCV); SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv); xprt_inactive(xprt); sx_xunlock(&xprt->xp_lock); return (FALSE); } sx_xunlock(&xprt->xp_lock); xdrmbuf_create(&xdrs, mreq, XDR_DECODE); if (! xdr_callmsg(&xdrs, msg)) { XDR_DESTROY(&xdrs); return (FALSE); } *addrp = raddr; *mp = xdrmbuf_getall(&xdrs); XDR_DESTROY(&xdrs); return (TRUE); }
static bool_t svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr **addrp, struct mbuf **mp) { struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1; struct uio uio; struct mbuf *m; XDR xdrs; int error, rcvflag; /* * Serialise access to the socket and our own record parsing * state. */ sx_xlock(&xprt->xp_lock); for (;;) { /* * If we have an mbuf chain in cd->mpending, try to parse a * record from it, leaving the result in cd->mreq. If we don't * have a complete record, leave the partial result in * cd->mreq and try to read more from the socket. */ if (cd->mpending) { /* * If cd->resid is non-zero, we have part of the * record already, otherwise we are expecting a record * marker. */ if (!cd->resid) { /* * See if there is enough data buffered to * make up a record marker. Make sure we can * handle the case where the record marker is * split across more than one mbuf. */ size_t n = 0; uint32_t header; m = cd->mpending; while (n < sizeof(uint32_t) && m) { n += m->m_len; m = m->m_next; } if (n < sizeof(uint32_t)) goto readmore; if (cd->mpending->m_len < sizeof(uint32_t)) cd->mpending = m_pullup(cd->mpending, sizeof(uint32_t)); memcpy(&header, mtod(cd->mpending, uint32_t *), sizeof(header)); header = ntohl(header); cd->eor = (header & 0x80000000) != 0; cd->resid = header & 0x7fffffff; m_adj(cd->mpending, sizeof(uint32_t)); } /* * Start pulling off mbufs from cd->mpending * until we either have a complete record or * we run out of data. We use m_split to pull * data - it will pull as much as possible and * split the last mbuf if necessary. */ while (cd->mpending && cd->resid) { m = cd->mpending; if (cd->mpending->m_next || cd->mpending->m_len > cd->resid) cd->mpending = m_split(cd->mpending, cd->resid, M_WAIT); else cd->mpending = NULL; if (cd->mreq) m_last(cd->mreq)->m_next = m; else cd->mreq = m; while (m) { cd->resid -= m->m_len; m = m->m_next; } } /* * If cd->resid is zero now, we have managed to * receive a record fragment from the stream. Check * for the end-of-record mark to see if we need more. */ if (cd->resid == 0) { if (!cd->eor) continue; /* * Success - we have a complete record in * cd->mreq. */ xdrmbuf_create(&xdrs, cd->mreq, XDR_DECODE); cd->mreq = NULL; sx_xunlock(&xprt->xp_lock); if (! xdr_callmsg(&xdrs, msg)) { XDR_DESTROY(&xdrs); return (FALSE); } *addrp = NULL; *mp = xdrmbuf_getall(&xdrs); XDR_DESTROY(&xdrs); return (TRUE); } } readmore: /* * The socket upcall calls xprt_active() which will eventually * cause the server to call us here. We attempt to * read as much as possible from the socket and put * the result in cd->mpending. If the read fails, * we have drained both cd->mpending and the socket so * we can call xprt_inactive(). */ uio.uio_resid = 1000000000; uio.uio_td = curthread; m = NULL; rcvflag = MSG_DONTWAIT; CURVNET_SET(xprt->xp_socket->so_vnet); error = soreceive(xprt->xp_socket, NULL, &uio, &m, NULL, &rcvflag); CURVNET_RESTORE(); if (error == EWOULDBLOCK) { /* * We must re-test for readability after * taking the lock to protect us in the case * where a new packet arrives on the socket * after our call to soreceive fails with * EWOULDBLOCK. The pool lock protects us from * racing the upcall after our soreadable() * call returns false. */ mtx_lock(&xprt->xp_pool->sp_lock); if (!soreadable(xprt->xp_socket)) xprt_inactive_locked(xprt); mtx_unlock(&xprt->xp_pool->sp_lock); sx_xunlock(&xprt->xp_lock); return (FALSE); } if (error) { SOCKBUF_LOCK(&xprt->xp_socket->so_rcv); if (xprt->xp_upcallset) { xprt->xp_upcallset = 0; soupcall_clear(xprt->xp_socket, SO_RCV); } SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv); xprt_inactive(xprt); cd->strm_stat = XPRT_DIED; sx_xunlock(&xprt->xp_lock); return (FALSE); } if (!m) { /* * EOF - the other end has closed the socket. */ xprt_inactive(xprt); cd->strm_stat = XPRT_DIED; sx_xunlock(&xprt->xp_lock); return (FALSE); } if (cd->mpending) m_last(cd->mpending)->m_next = m; else cd->mpending = m; }
/* * Receive rpc requests. * Pulls a request in off the socket, checks if the packet is intact, * and deserializes the call packet. */ static bool_t svc_clts_krecv(SVCXPRT *clone_xprt, mblk_t *mp, struct rpc_msg *msg) { /* LINTED pointer alignment */ struct udp_data *ud = (struct udp_data *)clone_xprt->xp_p2buf; XDR *xdrs = &clone_xprt->xp_xdrin; struct rpc_clts_server *stats = CLONE2STATS(clone_xprt); union T_primitives *pptr; int hdrsz; TRACE_0(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_START, "svc_clts_krecv_start:"); RSSTAT_INCR(stats, rscalls); /* * The incoming request should start with an M_PROTO message. */ if (mp->b_datap->db_type != M_PROTO) { goto bad; } /* * The incoming request should be an T_UNITDTA_IND. There * might be other messages coming up the stream, but we can * ignore them. */ pptr = (union T_primitives *)mp->b_rptr; if (pptr->type != T_UNITDATA_IND) { goto bad; } /* * Do some checking to make sure that the header at least looks okay. */ hdrsz = (int)(mp->b_wptr - mp->b_rptr); if (hdrsz < TUNITDATAINDSZ || hdrsz < (pptr->unitdata_ind.OPT_offset + pptr->unitdata_ind.OPT_length) || hdrsz < (pptr->unitdata_ind.SRC_offset + pptr->unitdata_ind.SRC_length)) { goto bad; } /* * Make sure that the transport provided a usable address. */ if (pptr->unitdata_ind.SRC_length <= 0) { goto bad; } /* * Point the remote transport address in the service_transport * handle at the address in the request. */ clone_xprt->xp_rtaddr.buf = (char *)mp->b_rptr + pptr->unitdata_ind.SRC_offset; clone_xprt->xp_rtaddr.len = pptr->unitdata_ind.SRC_length; /* * Save the first mblk which contains the T_unidata_ind in * ud_resp. It will be used to generate the T_unitdata_req * during the reply. */ if (ud->ud_resp) { if (ud->ud_resp->b_cont != NULL) { cmn_err(CE_WARN, "svc_clts_krecv: ud_resp %p, " "b_cont %p", (void *)ud->ud_resp, (void *)ud->ud_resp->b_cont); } freeb(ud->ud_resp); } ud->ud_resp = mp; mp = mp->b_cont; ud->ud_resp->b_cont = NULL; xdrmblk_init(xdrs, mp, XDR_DECODE, 0); TRACE_0(TR_FAC_KRPC, TR_XDR_CALLMSG_START, "xdr_callmsg_start:"); if (! xdr_callmsg(xdrs, msg)) { TRACE_1(TR_FAC_KRPC, TR_XDR_CALLMSG_END, "xdr_callmsg_end:(%S)", "bad"); RSSTAT_INCR(stats, rsxdrcall); goto bad; } TRACE_1(TR_FAC_KRPC, TR_XDR_CALLMSG_END, "xdr_callmsg_end:(%S)", "good"); clone_xprt->xp_xid = msg->rm_xid; ud->ud_inmp = mp; TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_END, "svc_clts_krecv_end:(%S)", "good"); return (TRUE); bad: if (mp) freemsg(mp); if (ud->ud_resp) { /* * There should not be any left over results buffer. */ ASSERT(ud->ud_resp->b_cont == NULL); freeb(ud->ud_resp); ud->ud_resp = NULL; } RSSTAT_INCR(stats, rsbadcalls); TRACE_1(TR_FAC_KRPC, TR_SVC_CLTS_KRECV_END, "svc_clts_krecv_end:(%S)", "bad"); return (FALSE); }