Date: Wed, 27 May 2009 17:02:15 +0000 (UTC) From: Zachary Loafman <zml@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r192913 - head/lib/libc/rpc Message-ID: <200905271702.n4RH2FLo004874@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: zml Date: Wed May 27 17:02:15 2009 New Revision: 192913 URL: http://svn.freebsd.org/changeset/base/192913 Log: Handle UDP RPC replies correctly on a multi-homed system, in userland RPC. Corrects an issue with mountd replies to OS X. Approved by: dfr (mentor) Modified: head/lib/libc/rpc/svc_dg.c head/lib/libc/rpc/svc_generic.c Modified: head/lib/libc/rpc/svc_dg.c ============================================================================== --- head/lib/libc/rpc/svc_dg.c Wed May 27 17:02:10 2009 (r192912) +++ head/lib/libc/rpc/svc_dg.c Wed May 27 17:02:15 2009 (r192913) @@ -98,6 +98,7 @@ int svc_dg_enablecache(SVCXPRT *, u_int) static const char svc_dg_str[] = "svc_dg_create: %s"; static const char svc_dg_err1[] = "could not get transport information"; static const char svc_dg_err2[] = " transport does not support data transfer"; +static const char svc_dg_err3[] = "getsockname failed"; static const char __no_mem_str[] = "out of memory"; SVCXPRT * @@ -146,8 +147,10 @@ svc_dg_create(fd, sendsize, recvsize) xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage); slen = sizeof ss; - if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) - goto freedata; + if (_getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) { + warnx(svc_dg_str, svc_dg_err3); + goto freedata_nowarn; + } xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage)); xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage); xprt->xp_ltaddr.len = slen; @@ -157,6 +160,7 @@ svc_dg_create(fd, sendsize, recvsize) return (xprt); freedata: (void) warnx(svc_dg_str, __no_mem_str); +freedata_nowarn: if (xprt) { if (su) (void) mem_free(su, sizeof (*su)); @@ -173,6 +177,58 @@ svc_dg_stat(xprt) return (XPRT_IDLE); } +static int +svc_dg_recvfrom(int fd, char *buf, int buflen, + struct sockaddr *raddr, socklen_t *raddrlen, + struct sockaddr *laddr, socklen_t *laddrlen) +{ + struct msghdr msg; + struct iovec msg_iov[1]; + struct sockaddr_in *lin = (struct sockaddr_in *)laddr; + int rlen; + bool_t have_lin = FALSE; + char tmp[CMSG_LEN(sizeof(*lin))]; + struct cmsghdr *cmsg; + + memset((char *)&msg, 0, sizeof(msg)); + msg_iov[0].iov_base = buf; + msg_iov[0].iov_len = buflen; + msg.msg_iov = msg_iov; + msg.msg_iovlen = 1; + msg.msg_namelen = *raddrlen; + msg.msg_name = (char *)raddr; + msg.msg_control = (caddr_t)tmp; + msg.msg_controllen = CMSG_LEN(sizeof(*lin)); + rlen = _recvmsg(fd, &msg, 0); + if (rlen >= 0) + *raddrlen = msg.msg_namelen; + + if (rlen == -1 || !laddr || + msg.msg_controllen < sizeof(struct cmsghdr) || + msg.msg_flags & MSG_CTRUNC) + return rlen; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)){ + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVDSTADDR) { + have_lin = TRUE; + memcpy(&lin->sin_addr, + (struct in_addr *)CMSG_DATA(cmsg), sizeof(struct in_addr)); + break; + } + } + + if (!have_lin) + return rlen; + + lin->sin_family = AF_INET; + lin->sin_port = 0; + *laddrlen = sizeof(struct sockaddr_in); + + return rlen; +} + static bool_t svc_dg_recv(xprt, msg) SVCXPRT *xprt; @@ -188,8 +244,9 @@ svc_dg_recv(xprt, msg) again: alen = sizeof (struct sockaddr_storage); - rlen = _recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, 0, - (struct sockaddr *)(void *)&ss, &alen); + rlen = svc_dg_recvfrom(xprt->xp_fd, rpc_buffer(xprt), su->su_iosz, + (struct sockaddr *)(void *)&ss, &alen, + (struct sockaddr *)xprt->xp_ltaddr.buf, &xprt->xp_ltaddr.len); if (rlen == -1 && errno == EINTR) goto again; if (rlen == -1 || (rlen < (ssize_t)(4 * sizeof (u_int32_t)))) @@ -223,6 +280,39 @@ again: return (TRUE); } +static int +svc_dg_sendto(int fd, char *buf, int buflen, + const struct sockaddr *raddr, socklen_t raddrlen, + const struct sockaddr *laddr, socklen_t laddrlen) +{ + struct msghdr msg; + struct iovec msg_iov[1]; + struct sockaddr_in *laddr_in = (struct sockaddr_in *)laddr; + struct in_addr *lin = &laddr_in->sin_addr; + char tmp[CMSG_SPACE(sizeof(*lin))]; + struct cmsghdr *cmsg; + + memset((char *)&msg, 0, sizeof(msg)); + msg_iov[0].iov_base = buf; + msg_iov[0].iov_len = buflen; + msg.msg_iov = msg_iov; + msg.msg_iovlen = 1; + msg.msg_namelen = raddrlen; + msg.msg_name = (char *)raddr; + + if (laddr->sa_family == AF_INET) { + msg.msg_control = (caddr_t)tmp; + msg.msg_controllen = CMSG_LEN(sizeof(*lin)); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(*lin)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + memcpy(CMSG_DATA(cmsg), lin, sizeof(*lin)); + } + + return _sendmsg(fd, &msg, 0); +} + static bool_t svc_dg_reply(xprt, msg) SVCXPRT *xprt; @@ -253,9 +343,11 @@ svc_dg_reply(xprt, msg) } if (stat) { slen = XDR_GETPOS(xdrs); - if (_sendto(xprt->xp_fd, rpc_buffer(xprt), slen, 0, + if (svc_dg_sendto(xprt->xp_fd, rpc_buffer(xprt), slen, (struct sockaddr *)xprt->xp_rtaddr.buf, - (socklen_t)xprt->xp_rtaddr.len) == (ssize_t) slen) { + (socklen_t)xprt->xp_rtaddr.len, + (struct sockaddr *)xprt->xp_ltaddr.buf, + xprt->xp_ltaddr.len) == (ssize_t) slen) { stat = TRUE; if (su->su_cache) cache_set(xprt, slen); Modified: head/lib/libc/rpc/svc_generic.c ============================================================================== --- head/lib/libc/rpc/svc_generic.c Wed May 27 17:02:10 2009 (r192912) +++ head/lib/libc/rpc/svc_generic.c Wed May 27 17:02:15 2009 (r192913) @@ -199,6 +199,7 @@ svc_tli_create(fd, nconf, bindaddr, send struct __rpc_sockinfo si; struct sockaddr_storage ss; socklen_t slen; + static const uint32_t true_value = 1; if (fd == RPC_ANYFD) { if (nconf == NULL) { @@ -225,6 +226,14 @@ svc_tli_create(fd, nconf, bindaddr, send } } + if (si.si_af == AF_INET && si.si_socktype == SOCK_DGRAM) { + if (_setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, + &true_value, sizeof(true_value))) { + warnx("svc_tli_create: cannot set IP_RECVDSTADDR"); + return (NULL); + } + } + /* * If the fd is unbound, try to bind it. */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200905271702.n4RH2FLo004874>