svn commit: r192913 - head/lib/libc/rpc

Zachary Loafman zml at FreeBSD.org
Wed May 27 17:02:20 UTC 2009


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.
 	 */


More information about the svn-src-all mailing list