+ sa_len = sa->sa_len;
+ len = MIN(len, sa_len);
+ error = copyout(sa, uap->asa, len);
+ if (error)
+ goto bad;
+ /* return the actual, untruncated address length */
+ len = sa_len;
+gotnothing:
+ error = copyout((caddr_t)&len, uap->alen, sizeof (socklen_t));
+bad:
+ if (sa) FREE(sa, M_SONAME);
+out:
+ file_drop(uap->fdes);
+ return (error);
+}
+
+int
+sockargs(struct mbuf **mp, user_addr_t data, int buflen, int type)
+{
+ struct sockaddr *sa;
+ struct mbuf *m;
+ int error;
+
+ size_t alloc_buflen = (size_t)buflen;
+
+ if (alloc_buflen > INT_MAX/2)
+ return (EINVAL);
+#ifdef __LP64__
+ /*
+ * The fd's in the buffer must expand to be pointers, thus we need twice
+ * as much space
+ */
+ if (type == MT_CONTROL)
+ alloc_buflen = ((buflen - sizeof(struct cmsghdr))*2) +
+ sizeof(struct cmsghdr);
+#endif
+ if (alloc_buflen > MLEN) {
+ if (type == MT_SONAME && alloc_buflen <= 112)
+ alloc_buflen = MLEN; /* unix domain compat. hack */
+ else if (alloc_buflen > MCLBYTES)
+ return (EINVAL);
+ }
+ m = m_get(M_WAIT, type);
+ if (m == NULL)
+ return (ENOBUFS);
+ if (alloc_buflen > MLEN) {
+ MCLGET(m, M_WAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ return (ENOBUFS);
+ }
+ }
+ /*
+ * K64: We still copyin the original buflen because it gets expanded
+ * later and we lie about the size of the mbuf because it only affects
+ * unp_* functions
+ */
+ m->m_len = buflen;
+ error = copyin(data, mtod(m, caddr_t), (u_int)buflen);
+ if (error) {
+ (void) m_free(m);
+ } else {
+ *mp = m;
+ if (type == MT_SONAME) {
+ sa = mtod(m, struct sockaddr *);
+ sa->sa_len = buflen;
+ }
+ }
+ return (error);
+}
+
+/*
+ * Given a user_addr_t of length len, allocate and fill out a *sa.
+ *
+ * Returns: 0 Success
+ * ENAMETOOLONG Filename too long
+ * EINVAL Invalid argument
+ * ENOMEM Not enough space
+ * copyin:EFAULT Bad address
+ */
+static int
+getsockaddr(struct socket *so, struct sockaddr **namp, user_addr_t uaddr,
+ size_t len, boolean_t translate_unspec)
+{
+ struct sockaddr *sa;
+ int error;
+
+ if (len > SOCK_MAXADDRLEN)
+ return (ENAMETOOLONG);
+
+ if (len < offsetof(struct sockaddr, sa_data[0]))
+ return (EINVAL);
+
+ MALLOC(sa, struct sockaddr *, len, M_SONAME, M_WAITOK | M_ZERO);
+ if (sa == NULL) {
+ return (ENOMEM);
+ }
+ error = copyin(uaddr, (caddr_t)sa, len);
+ if (error) {
+ FREE(sa, M_SONAME);
+ } else {
+ /*
+ * Force sa_family to AF_INET on AF_INET sockets to handle
+ * legacy applications that use AF_UNSPEC (0). On all other
+ * sockets we leave it unchanged and let the lower layer
+ * handle it.
+ */
+ if (translate_unspec && sa->sa_family == AF_UNSPEC &&
+ SOCK_CHECK_DOM(so, PF_INET) &&
+ len == sizeof (struct sockaddr_in))
+ sa->sa_family = AF_INET;
+
+ sa->sa_len = len;
+ *namp = sa;
+ }
+ return (error);
+}
+
+static int
+getsockaddr_s(struct socket *so, struct sockaddr_storage *ss,
+ user_addr_t uaddr, size_t len, boolean_t translate_unspec)
+{
+ int error;
+
+ if (ss == NULL || uaddr == USER_ADDR_NULL ||
+ len < offsetof(struct sockaddr, sa_data[0]))
+ return (EINVAL);
+
+ /*
+ * sockaddr_storage size is less than SOCK_MAXADDRLEN,
+ * so the check here is inclusive.
+ */
+ if (len > sizeof (*ss))
+ return (ENAMETOOLONG);
+
+ bzero(ss, sizeof (*ss));
+ error = copyin(uaddr, (caddr_t)ss, len);
+ if (error == 0) {
+ /*
+ * Force sa_family to AF_INET on AF_INET sockets to handle
+ * legacy applications that use AF_UNSPEC (0). On all other
+ * sockets we leave it unchanged and let the lower layer
+ * handle it.
+ */
+ if (translate_unspec && ss->ss_family == AF_UNSPEC &&
+ SOCK_CHECK_DOM(so, PF_INET) &&
+ len == sizeof (struct sockaddr_in))
+ ss->ss_family = AF_INET;
+
+ ss->ss_len = len;
+ }
+ return (error);
+}
+
+int
+internalize_user_msghdr_array(const void *src, int spacetype, int direction,
+ u_int count, struct user_msghdr_x *dst, struct uio **uiop)
+{
+ int error = 0;
+ u_int i;
+ u_int namecnt = 0;
+ u_int ctlcnt = 0;
+
+ for (i = 0; i < count; i++) {
+ uio_t auio;
+ struct user_iovec *iovp;
+ struct user_msghdr_x *user_msg = dst + i;
+
+ if (spacetype == UIO_USERSPACE64) {
+ const struct user64_msghdr_x *msghdr64;
+
+ msghdr64 = ((const struct user64_msghdr_x *)src) + i;
+
+ user_msg->msg_name = msghdr64->msg_name;
+ user_msg->msg_namelen = msghdr64->msg_namelen;
+ user_msg->msg_iov = msghdr64->msg_iov;
+ user_msg->msg_iovlen = msghdr64->msg_iovlen;
+ user_msg->msg_control = msghdr64->msg_control;
+ user_msg->msg_controllen = msghdr64->msg_controllen;
+ user_msg->msg_flags = msghdr64->msg_flags;
+ user_msg->msg_datalen = msghdr64->msg_datalen;
+ } else {
+ const struct user32_msghdr_x *msghdr32;
+
+ msghdr32 = ((const struct user32_msghdr_x *)src) + i;
+
+ user_msg->msg_name = msghdr32->msg_name;
+ user_msg->msg_namelen = msghdr32->msg_namelen;
+ user_msg->msg_iov = msghdr32->msg_iov;
+ user_msg->msg_iovlen = msghdr32->msg_iovlen;
+ user_msg->msg_control = msghdr32->msg_control;
+ user_msg->msg_controllen = msghdr32->msg_controllen;
+ user_msg->msg_flags = msghdr32->msg_flags;
+ user_msg->msg_datalen = msghdr32->msg_datalen;
+ }
+
+ if (user_msg->msg_iovlen <= 0 ||
+ user_msg->msg_iovlen > UIO_MAXIOV) {
+ error = EMSGSIZE;
+ goto done;
+ }
+ auio = uio_create(user_msg->msg_iovlen, 0, spacetype,
+ direction);
+ if (auio == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+ uiop[i] = auio;
+
+ iovp = uio_iovsaddr(auio);
+ if (iovp == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+ error = copyin_user_iovec_array(user_msg->msg_iov,
+ spacetype, user_msg->msg_iovlen, iovp);
+ if (error)
+ goto done;
+ user_msg->msg_iov = CAST_USER_ADDR_T(iovp);
+
+ error = uio_calculateresid(auio);
+ if (error)
+ goto done;
+ user_msg->msg_datalen = uio_resid(auio);
+
+ if (user_msg->msg_name && user_msg->msg_namelen)
+ namecnt++;
+ if (user_msg->msg_control && user_msg->msg_controllen)
+ ctlcnt++;
+ }
+done:
+
+ return (error);
+}
+
+int
+internalize_recv_msghdr_array(const void *src, int spacetype, int direction,
+ u_int count, struct user_msghdr_x *dst,
+ struct recv_msg_elem *recv_msg_array)
+{
+ int error = 0;
+ u_int i;
+
+ for (i = 0; i < count; i++) {
+ struct user_iovec *iovp;
+ struct user_msghdr_x *user_msg = dst + i;
+ struct recv_msg_elem *recv_msg_elem = recv_msg_array + i;
+
+ if (spacetype == UIO_USERSPACE64) {
+ const struct user64_msghdr_x *msghdr64;
+
+ msghdr64 = ((const struct user64_msghdr_x *)src) + i;
+
+ user_msg->msg_name = msghdr64->msg_name;
+ user_msg->msg_namelen = msghdr64->msg_namelen;
+ user_msg->msg_iov = msghdr64->msg_iov;
+ user_msg->msg_iovlen = msghdr64->msg_iovlen;
+ user_msg->msg_control = msghdr64->msg_control;
+ user_msg->msg_controllen = msghdr64->msg_controllen;
+ user_msg->msg_flags = msghdr64->msg_flags;
+ user_msg->msg_datalen = msghdr64->msg_datalen;
+ } else {
+ const struct user32_msghdr_x *msghdr32;
+
+ msghdr32 = ((const struct user32_msghdr_x *)src) + i;
+
+ user_msg->msg_name = msghdr32->msg_name;
+ user_msg->msg_namelen = msghdr32->msg_namelen;
+ user_msg->msg_iov = msghdr32->msg_iov;
+ user_msg->msg_iovlen = msghdr32->msg_iovlen;
+ user_msg->msg_control = msghdr32->msg_control;
+ user_msg->msg_controllen = msghdr32->msg_controllen;
+ user_msg->msg_flags = msghdr32->msg_flags;
+ user_msg->msg_datalen = msghdr32->msg_datalen;
+ }
+
+ if (user_msg->msg_iovlen <= 0 ||
+ user_msg->msg_iovlen > UIO_MAXIOV) {
+ error = EMSGSIZE;
+ goto done;
+ }
+ recv_msg_elem->uio = uio_create(user_msg->msg_iovlen, 0,
+ spacetype, direction);
+ if (recv_msg_elem->uio == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+
+ iovp = uio_iovsaddr(recv_msg_elem->uio);
+ if (iovp == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+ error = copyin_user_iovec_array(user_msg->msg_iov,
+ spacetype, user_msg->msg_iovlen, iovp);
+ if (error)
+ goto done;
+ user_msg->msg_iov = CAST_USER_ADDR_T(iovp);
+
+ error = uio_calculateresid(recv_msg_elem->uio);
+ if (error)
+ goto done;
+ user_msg->msg_datalen = uio_resid(recv_msg_elem->uio);
+
+ if (user_msg->msg_name && user_msg->msg_namelen)
+ recv_msg_elem->which |= SOCK_MSG_SA;
+ if (user_msg->msg_control && user_msg->msg_controllen)
+ recv_msg_elem->which |= SOCK_MSG_CONTROL;
+ }
+done:
+