+ KERNEL_DEBUG(DBG_FNC_RECVMSG | DBG_FUNC_END, error, 0, 0, 0, 0);
+ return error;
+}
+
+int
+recvmsg_x(struct proc *p, struct recvmsg_x_args *uap, user_ssize_t *retval)
+{
+ int error = EOPNOTSUPP;
+ struct user_msghdr_x *user_msg_x = NULL;
+ struct recv_msg_elem *recv_msg_array = NULL;
+ struct socket *so;
+ user_ssize_t len_before = 0, len_after;
+ int need_drop = 0;
+ size_t size_of_msghdr;
+ void *umsgp = NULL;
+ u_int i;
+ u_int uiocnt;
+
+ KERNEL_DEBUG(DBG_FNC_RECVMSG_X | DBG_FUNC_START, 0, 0, 0, 0, 0);
+
+ size_of_msghdr = IS_64BIT_PROCESS(p) ?
+ sizeof(struct user64_msghdr_x) : sizeof(struct user32_msghdr_x);
+
+ error = file_socket(uap->s, &so);
+ if (error) {
+ goto out;
+ }
+ need_drop = 1;
+ if (so == NULL) {
+ error = EBADF;
+ goto out;
+ }
+ /*
+ * Support only a subset of message flags
+ */
+ if (uap->flags & ~(MSG_PEEK | MSG_WAITALL | MSG_DONTWAIT | MSG_NEEDSA | MSG_NBIO)) {
+ return EOPNOTSUPP;
+ }
+ /*
+ * Input parameter range check
+ */
+ if (uap->cnt == 0 || uap->cnt > UIO_MAXIOV) {
+ error = EINVAL;
+ goto out;
+ }
+ if (uap->cnt > somaxrecvmsgx) {
+ uap->cnt = somaxrecvmsgx;
+ }
+
+ user_msg_x = kheap_alloc(KHEAP_TEMP,
+ uap->cnt * sizeof(struct user_msghdr_x), Z_WAITOK | Z_ZERO);
+ if (user_msg_x == NULL) {
+ DBG_PRINTF("%s kheap_alloc user_msg_x failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ recv_msg_array = alloc_recv_msg_array(uap->cnt);
+ if (recv_msg_array == NULL) {
+ DBG_PRINTF("%s alloc_recv_msg_array() failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+
+ umsgp = kheap_alloc(KHEAP_TEMP,
+ uap->cnt * size_of_msghdr, Z_WAITOK | Z_ZERO);
+ if (umsgp == NULL) {
+ DBG_PRINTF("%s kheap_alloc umsgp failed\n", __func__);
+ error = ENOMEM;
+ goto out;
+ }
+ error = copyin(uap->msgp, umsgp, uap->cnt * size_of_msghdr);
+ if (error) {
+ DBG_PRINTF("%s copyin() failed\n", __func__);
+ goto out;
+ }
+ error = internalize_recv_msghdr_array(umsgp,
+ IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32,
+ UIO_READ, uap->cnt, user_msg_x, recv_msg_array);
+ if (error) {
+ DBG_PRINTF("%s copyin_user_msghdr_array() failed\n", __func__);
+ goto out;
+ }
+ /*
+ * Make sure the size of each message iovec and
+ * the aggregate size of all the iovec is valid
+ */
+ if (recv_msg_array_is_valid(recv_msg_array, uap->cnt) == 0) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Sanity check on passed arguments
+ */
+ for (i = 0; i < uap->cnt; i++) {
+ struct user_msghdr_x *mp = user_msg_x + i;
+
+ if (mp->msg_flags != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+#if CONFIG_MACF_SOCKET_SUBSET
+ /*
+ * We check the state without holding the socket lock;
+ * if a race condition occurs, it would simply result
+ * in an extra call to the MAC check function.
+ */
+ if (!(so->so_state & SS_DEFUNCT) &&
+ !(so->so_state & SS_ISCONNECTED) &&
+ !(so->so_proto->pr_flags & PR_CONNREQUIRED) &&
+ (error = mac_socket_check_receive(kauth_cred_get(), so)) != 0) {
+ goto out;
+ }
+#endif /* MAC_SOCKET_SUBSET */
+
+ len_before = recv_msg_array_resid(recv_msg_array, uap->cnt);
+
+ if (so->so_proto->pr_usrreqs->pru_soreceive_list !=
+ pru_soreceive_list_notsupp &&
+ somaxrecvmsgx == 0) {
+ error = so->so_proto->pr_usrreqs->pru_soreceive_list(so,
+ recv_msg_array, uap->cnt, &uap->flags);
+ } else {
+ int flags = uap->flags;
+
+ for (i = 0; i < uap->cnt; i++) {
+ struct recv_msg_elem *recv_msg_elem;
+ uio_t auio;
+ struct sockaddr **psa;
+ struct mbuf **controlp;
+
+ recv_msg_elem = recv_msg_array + i;
+ auio = recv_msg_elem->uio;
+
+ /*
+ * Do not block if we got at least one packet
+ */
+ if (i > 0) {
+ flags |= MSG_DONTWAIT;
+ }
+
+ psa = (recv_msg_elem->which & SOCK_MSG_SA) ?
+ &recv_msg_elem->psa : NULL;
+ controlp = (recv_msg_elem->which & SOCK_MSG_CONTROL) ?
+ &recv_msg_elem->controlp : NULL;
+
+ error = so->so_proto->pr_usrreqs->pru_soreceive(so, psa,
+ auio, (struct mbuf **)NULL, controlp, &flags);
+ if (error) {
+ break;
+ }
+ /*
+ * We have some data
+ */
+ recv_msg_elem->which |= SOCK_MSG_DATA;
+ /*
+ * Set the messages flags for this packet
+ */
+ flags &= ~MSG_DONTWAIT;
+ recv_msg_elem->flags = flags;
+ /*
+ * Stop on partial copy
+ */
+ if (recv_msg_elem->flags & (MSG_RCVMORE | MSG_TRUNC)) {
+ break;
+ }
+ }
+ }
+
+ len_after = recv_msg_array_resid(recv_msg_array, uap->cnt);
+
+ if (error) {
+ if (len_after != len_before && (error == ERESTART ||
+ error == EINTR || error == EWOULDBLOCK)) {
+ error = 0;
+ } else {
+ goto out;
+ }
+ }
+
+ uiocnt = externalize_recv_msghdr_array(p, so, umsgp,
+ uap->cnt, user_msg_x, recv_msg_array, &error);
+ if (error != 0) {
+ goto out;
+ }
+
+ error = copyout(umsgp, uap->msgp, uap->cnt * size_of_msghdr);
+ if (error) {
+ DBG_PRINTF("%s copyout() failed\n", __func__);
+ goto out;
+ }
+ *retval = (int)(uiocnt);
+
+out:
+ if (need_drop) {
+ file_drop(uap->s);
+ }
+ kheap_free(KHEAP_TEMP, umsgp, uap->cnt * size_of_msghdr);
+ free_recv_msg_array(recv_msg_array, uap->cnt);
+ kheap_free(KHEAP_TEMP, user_msg_x,
+ uap->cnt * sizeof(struct user_msghdr_x));
+
+ KERNEL_DEBUG(DBG_FNC_RECVMSG_X | DBG_FUNC_END, error, 0, 0, 0, 0);
+
+ return error;