+ return error;
+}
+
+int
+sockargs(struct mbuf **mp, user_addr_t data, socklen_t buflen, int type)
+{
+ struct sockaddr *sa;
+ struct mbuf *m;
+ int error;
+ socklen_t alloc_buflen = buflen;
+
+ if (buflen > INT_MAX / 2) {
+ return EINVAL;
+ }
+ if (type == MT_SONAME && buflen > SOCK_MAXADDRLEN) {
+ 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 *);
+ VERIFY(buflen <= SOCK_MAXADDRLEN);
+ sa->sa_len = (__uint8_t)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;
+ }
+ VERIFY(len <= SOCK_MAXADDRLEN);
+ sa->sa_len = (__uint8_t)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 = (__uint8_t)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 = (user_addr_t)msghdr64->msg_name;
+ user_msg->msg_namelen = msghdr64->msg_namelen;
+ user_msg->msg_iov = (user_addr_t)msghdr64->msg_iov;
+ user_msg->msg_iovlen = msghdr64->msg_iovlen;
+ user_msg->msg_control = (user_addr_t)msghdr64->msg_control;
+ user_msg->msg_controllen = msghdr64->msg_controllen;
+ user_msg->msg_flags = msghdr64->msg_flags;
+ user_msg->msg_datalen = (size_t)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 = (user_addr_t)msghdr64->msg_name;
+ user_msg->msg_namelen = msghdr64->msg_namelen;
+ user_msg->msg_iov = (user_addr_t)msghdr64->msg_iov;
+ user_msg->msg_iovlen = msghdr64->msg_iovlen;
+ user_msg->msg_control = (user_addr_t)msghdr64->msg_control;
+ user_msg->msg_controllen = msghdr64->msg_controllen;
+ user_msg->msg_flags = msghdr64->msg_flags;
+ user_msg->msg_datalen = (size_t)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:
+
+ return error;
+}
+
+u_int
+externalize_user_msghdr_array(void *dst, int spacetype, int direction,
+ u_int count, const struct user_msghdr_x *src, struct uio **uiop)
+{
+#pragma unused(direction)
+ u_int i;
+ int seenlast = 0;
+ u_int retcnt = 0;
+
+ for (i = 0; i < count; i++) {
+ const struct user_msghdr_x *user_msg = src + i;
+ uio_t auio = uiop[i];
+ user_ssize_t len = user_msg->msg_datalen - uio_resid(auio);
+
+ if (user_msg->msg_datalen != 0 && len == 0) {
+ seenlast = 1;
+ }
+
+ if (seenlast == 0) {
+ retcnt++;
+ }
+
+ if (spacetype == UIO_USERSPACE64) {
+ struct user64_msghdr_x *msghdr64;
+
+ msghdr64 = ((struct user64_msghdr_x *)dst) + i;
+
+ msghdr64->msg_flags = user_msg->msg_flags;
+ msghdr64->msg_datalen = len;
+ } else {
+ struct user32_msghdr_x *msghdr32;
+
+ msghdr32 = ((struct user32_msghdr_x *)dst) + i;
+
+ msghdr32->msg_flags = user_msg->msg_flags;
+ msghdr32->msg_datalen = (user32_size_t)len;
+ }
+ }
+ return retcnt;