+ if (uaddrlen < sizeof (struct sockaddr)) {
+ error = EINVAL;
+ break;
+ }
+
+ bzero(&ss, sizeof (ss));
+ error = copyin(uaddr, (caddr_t)&ss, sizeof (struct sockaddr));
+ if (error != 0)
+ break;
+
+ /* getsockaddr does the same but we need them now */
+ if (uaddrlen < ss.ss_len ||
+ ss.ss_len < offsetof(struct sockaddr, sa_data[0])) {
+ error = EINVAL;
+ break;
+ } else if (ss.ss_len > sizeof (ss)) {
+ /*
+ * sockaddr_storage size is less than SOCK_MAXADDRLEN,
+ * so the check here is inclusive. We could use the
+ * latter instead, but seems like an overkill for now.
+ */
+ error = ENAMETOOLONG;
+ break;
+ }
+
+ se = sockaddrentry_alloc(M_WAITOK);
+ if (se == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+
+ sockaddrlist_insert(sl, se);
+
+ error = getsockaddr(so, &sa, uaddr, ss.ss_len, xlate_unspec);
+ if (error != 0)
+ break;
+
+ VERIFY(sa != NULL && sa->sa_len == ss.ss_len);
+ se->se_addr = sa;
+
+ uaddr += ss.ss_len;
+ VERIFY(((signed)uaddrlen - ss.ss_len) >= 0);
+ uaddrlen -= ss.ss_len;
+ }
+
+ if (error != 0)
+ sockaddrlist_free(sl);
+ else
+ *slp = sl;
+
+ 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:
+
+ 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 = len;
+ }
+ }
+ return (retcnt);
+}
+
+u_int
+externalize_recv_msghdr_array(void *dst, int spacetype, int direction,
+ u_int count, const struct user_msghdr_x *src,
+ struct recv_msg_elem *recv_msg_array)
+{
+ 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;
+ struct recv_msg_elem *recv_msg_elem = recv_msg_array + i;
+ user_ssize_t len;
+
+ len = user_msg->msg_datalen - uio_resid(recv_msg_elem->uio);
+
+ if (direction == UIO_READ) {
+ if ((recv_msg_elem->which & SOCK_MSG_DATA) == 0)
+ seenlast = 1;
+ } else {
+ 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 = len;
+ }
+ }
+ return (retcnt);
+}
+
+void
+free_uio_array(struct uio **uiop, u_int count)
+{
+ u_int i;
+
+ for (i = 0; i < count; i++) {
+ if (uiop[i] != NULL)
+ uio_free(uiop[i]);
+ }
+}
+
+__private_extern__ user_ssize_t
+uio_array_resid(struct uio **uiop, u_int count)
+{
+ user_ssize_t len = 0;
+ u_int i;
+
+ for (i = 0; i < count; i++) {
+ struct uio *auio = uiop[i];
+
+ if (auio != NULL)
+ len += uio_resid(auio);
+ }
+ return (len);
+}
+
+int
+uio_array_is_valid(struct uio **uiop, u_int count)
+{
+ user_ssize_t len = 0;
+ u_int i;
+
+ for (i = 0; i < count; i++) {
+ struct uio *auio = uiop[i];
+
+ if (auio != NULL) {
+ user_ssize_t resid = uio_resid(auio);
+
+ /*
+ * Sanity check on the validity of the iovec:
+ * no point of going over sb_max
+ */
+ if (resid < 0 || (u_int32_t)resid > sb_max)
+ return (0);
+
+ len += resid;
+ if (len < 0 || (u_int32_t)len > sb_max)
+ return (0);
+ }
+ }
+ return (1);
+}
+
+
+struct recv_msg_elem *
+alloc_recv_msg_array(u_int count)
+{
+ struct recv_msg_elem *recv_msg_array;
+
+ recv_msg_array = _MALLOC(count * sizeof(struct recv_msg_elem),
+ M_TEMP, M_WAITOK | M_ZERO);
+
+ return (recv_msg_array);
+}
+
+void
+free_recv_msg_array(struct recv_msg_elem *recv_msg_array, u_int count)
+{
+ u_int i;
+
+ for (i = 0; i < count; i++) {
+ struct recv_msg_elem *recv_msg_elem = recv_msg_array + i;
+
+ if (recv_msg_elem->uio != NULL)
+ uio_free(recv_msg_elem->uio);
+ if (recv_msg_elem->psa != NULL)
+ _FREE(recv_msg_elem->psa, M_TEMP);
+ if (recv_msg_elem->controlp != NULL)
+ m_freem(recv_msg_elem->controlp);
+ }
+ _FREE(recv_msg_array, M_TEMP);
+}