diff --git a/net/socket/recvmsg.c b/net/socket/recvmsg.c index 9543c6b6f6e5b..99cf3e4c3a557 100644 --- a/net/socket/recvmsg.c +++ b/net/socket/recvmsg.c @@ -28,9 +28,13 @@ #include #include +#include + +#include #include #include +#include #include #include "socket/socket.h" @@ -165,11 +169,139 @@ ssize_t recvmsg(int sockfd, FAR struct msghdr *msg, int flags) FAR struct socket *psock; FAR struct file *filep; ssize_t ret; +#ifdef CONFIG_BUILD_KERNEL + struct msghdr kmsg; + struct msghdr umsg; + FAR struct msghdr *recv_msg = msg; + FAR struct iovec *user_iov = NULL; + FAR struct iovec *kiov = NULL; + FAR uint8_t *kdata = NULL; + FAR void *kname = NULL; + FAR void *kcontrol = NULL; + size_t total_len; + size_t i; + FAR uint8_t *ptr; + ssize_t remaining; + size_t chunk; +#endif /* recvmsg() is a cancellation point */ enter_cancellation_point(); +#ifdef CONFIG_BUILD_KERNEL + /* Receive into kernel buffers, then copy out to the user msghdr (see + * recvfrom()). + */ + + memcpy(&umsg, msg, sizeof(struct msghdr)); + + if (umsg.msg_iov == NULL) + { + ret = -EINVAL; + goto errout_with_cleanup; + } + + if (umsg.msg_iovlen > 0) + { + user_iov = kmm_malloc(umsg.msg_iovlen * sizeof(struct iovec)); + if (user_iov == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + memcpy(user_iov, msg->msg_iov, umsg.msg_iovlen * sizeof(struct iovec)); + + kiov = kmm_malloc(umsg.msg_iovlen * sizeof(struct iovec)); + if (kiov == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + total_len = 0; + for (i = 0; i < umsg.msg_iovlen; i++) + { + total_len += user_iov[i].iov_len; + } + + if (total_len > 0) + { + kdata = kmm_malloc(total_len); + if (kdata == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + ptr = kdata; + for (i = 0; i < umsg.msg_iovlen; i++) + { + if (user_iov[i].iov_len > 0) + { + kiov[i].iov_base = ptr; + kiov[i].iov_len = user_iov[i].iov_len; + ptr += user_iov[i].iov_len; + } + else + { + kiov[i].iov_base = NULL; + kiov[i].iov_len = 0; + } + } + } + else + { + for (i = 0; i < umsg.msg_iovlen; i++) + { + kiov[i].iov_base = NULL; + kiov[i].iov_len = 0; + } + } + } + + memcpy(&kmsg, &umsg, sizeof(kmsg)); + kmsg.msg_iov = kiov; + kmsg.msg_iovlen = umsg.msg_iovlen; + + if (umsg.msg_name != NULL && umsg.msg_namelen > 0) + { + kname = kmm_malloc(umsg.msg_namelen); + if (kname == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + kmsg.msg_name = (FAR struct sockaddr *)kname; + } + else + { + kmsg.msg_name = NULL; + kmsg.msg_namelen = 0; + } + + if (umsg.msg_control != NULL && umsg.msg_controllen > 0) + { + kcontrol = kmm_malloc(umsg.msg_controllen); + if (kcontrol == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + kmsg.msg_control = kcontrol; + } + else + { + kmsg.msg_control = NULL; + kmsg.msg_controllen = 0; + } + + recv_msg = &kmsg; +#endif + /* Get the underlying socket structure */ ret = sockfd_socket(sockfd, &filep, &psock); @@ -178,10 +310,50 @@ ssize_t recvmsg(int sockfd, FAR struct msghdr *msg, int flags) if (ret == OK) { +#ifdef CONFIG_BUILD_KERNEL + ret = psock_recvmsg(psock, recv_msg, flags); +#else ret = psock_recvmsg(psock, msg, flags); +#endif file_put(filep); } +#ifdef CONFIG_BUILD_KERNEL + if (ret >= 0) + { + remaining = ret; + for (i = 0; i < umsg.msg_iovlen && remaining > 0; i++) + { + chunk = kiov[i].iov_len < (size_t)remaining ? kiov[i].iov_len : + (size_t)remaining; + memcpy(user_iov[i].iov_base, kiov[i].iov_base, chunk); + remaining -= (ssize_t)chunk; + } + + msg->msg_namelen = kmsg.msg_namelen; + if (umsg.msg_name != NULL && kmsg.msg_namelen > 0) + { + memcpy(umsg.msg_name, kname, kmsg.msg_namelen); + } + + msg->msg_flags = kmsg.msg_flags; + + if (umsg.msg_control != NULL && umsg.msg_controllen > 0) + { + memcpy(umsg.msg_control, kcontrol, kmsg.msg_controllen); + } + + msg->msg_controllen = kmsg.msg_controllen; + } + +errout_with_cleanup: + kmm_free(kcontrol); + kmm_free(kname); + kmm_free(kdata); + kmm_free(kiov); + kmm_free(user_iov); +#endif + if (ret < 0) { set_errno(-ret); diff --git a/net/socket/sendmsg.c b/net/socket/sendmsg.c index b20f940932781..af052888d500b 100644 --- a/net/socket/sendmsg.c +++ b/net/socket/sendmsg.c @@ -28,9 +28,13 @@ #include #include +#include + +#include #include #include +#include #include #include "socket/socket.h" @@ -145,11 +149,129 @@ ssize_t sendmsg(int sockfd, FAR const struct msghdr *msg, int flags) FAR struct socket *psock; FAR struct file *filep; ssize_t ret; +#ifdef CONFIG_BUILD_KERNEL + struct msghdr kmsg; + struct msghdr umsg; + FAR struct iovec *uiov = NULL; + FAR uint8_t *kdata = NULL; + FAR void *kname = NULL; + FAR void *kcontrol = NULL; + FAR const struct msghdr *send_msg = msg; + size_t total_len; + size_t i; + FAR uint8_t *ptr; +#endif /* sendmsg() is a cancellation point */ enter_cancellation_point(); +#ifdef CONFIG_BUILD_KERNEL + /* Copy user msghdr, iovec array, and payload into kernel memory so the + * network stack can safely memcpy from iov bases (see sendto()). + */ + + memcpy(&umsg, msg, sizeof(struct msghdr)); + + if (umsg.msg_iov == NULL) + { + ret = -EINVAL; + goto errout_with_cleanup; + } + + if (umsg.msg_iovlen > 0) + { + uiov = kmm_malloc(umsg.msg_iovlen * sizeof(struct iovec)); + if (uiov == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + memcpy(uiov, msg->msg_iov, umsg.msg_iovlen * sizeof(struct iovec)); + + total_len = 0; + for (i = 0; i < umsg.msg_iovlen; i++) + { + total_len += uiov[i].iov_len; + } + + if (total_len > 0) + { + kdata = kmm_malloc(total_len); + if (kdata == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + ptr = kdata; + for (i = 0; i < umsg.msg_iovlen; i++) + { + if (uiov[i].iov_len > 0) + { + memcpy(ptr, uiov[i].iov_base, uiov[i].iov_len); + uiov[i].iov_base = ptr; + ptr += uiov[i].iov_len; + } + else + { + uiov[i].iov_base = NULL; + } + } + } + else + { + for (i = 0; i < umsg.msg_iovlen; i++) + { + uiov[i].iov_base = NULL; + } + } + } + + memcpy(&kmsg, &umsg, sizeof(kmsg)); + kmsg.msg_iov = uiov; + kmsg.msg_iovlen = umsg.msg_iovlen; + + if (umsg.msg_name != NULL && umsg.msg_namelen > 0) + { + kname = kmm_malloc(umsg.msg_namelen); + if (kname == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + memcpy(kname, umsg.msg_name, umsg.msg_namelen); + kmsg.msg_name = (FAR struct sockaddr *)kname; + } + else + { + kmsg.msg_name = NULL; + kmsg.msg_namelen = 0; + } + + if (umsg.msg_control != NULL && umsg.msg_controllen > 0) + { + kcontrol = kmm_malloc(umsg.msg_controllen); + if (kcontrol == NULL) + { + ret = -ENOMEM; + goto errout_with_cleanup; + } + + memcpy(kcontrol, umsg.msg_control, umsg.msg_controllen); + kmsg.msg_control = kcontrol; + } + else + { + kmsg.msg_control = NULL; + kmsg.msg_controllen = 0; + } + + send_msg = &kmsg; +#endif + /* Get the underlying socket structure */ ret = sockfd_socket(sockfd, &filep, &psock); @@ -158,10 +280,22 @@ ssize_t sendmsg(int sockfd, FAR const struct msghdr *msg, int flags) if (ret == OK) { +#ifdef CONFIG_BUILD_KERNEL + ret = psock_sendmsg(psock, send_msg, flags); +#else ret = psock_sendmsg(psock, msg, flags); +#endif file_put(filep); } +#ifdef CONFIG_BUILD_KERNEL +errout_with_cleanup: + kmm_free(kcontrol); + kmm_free(kname); + kmm_free(kdata); + kmm_free(uiov); +#endif + if (ret < 0) { set_errno(-ret);