diff --git a/libc/sock/internal.h b/libc/sock/internal.h index 2ea9b0aa0..78077ba79 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -95,6 +95,8 @@ int64_t sys_writev(int32_t, const struct iovec *, int32_t) hidden; ssize_t sys_recvfrom(int, void *, size_t, int, void *, uint32_t *) hidden; ssize_t sys_sendto(int, const void *, size_t, int, const void *, uint32_t) hidden; +ssize_t sys_sendmsg(int, const struct msghdr *, int) hidden; +ssize_t sys_recvmsg(int, struct msghdr *, int) hidden; int32_t sys_select(int32_t, fd_set *, fd_set *, fd_set *, struct timeval *) hidden; int sys_setsockopt(int, int, int, const void *, uint32_t) hidden; diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c new file mode 100644 index 000000000..704c6eba5 --- /dev/null +++ b/libc/sock/recvmsg.c @@ -0,0 +1,54 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" +#include "libc/dce.h" +#include "libc/sock/internal.h" +#include "libc/sock/sock.h" +#include "libc/str/str.h" +#include "libc/sysv/errfuns.h" + +/** + * Sends a message from a socket. + * + * @param fd is the file descriptor returned by socket() + * @param msg is a pointer to a struct msghdr containing all the allocated + * buffers where to store incoming data. + * @param flags MSG_OOB, MSG_DONTROUTE, MSG_PARTIAL, MSG_NOSIGNAL, etc. + * @return number of bytes received, or -1 w/ errno + * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), + * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. + * @asyncsignalsafe + */ +ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { + if (!IsWindows()) { + ssize_t got = sys_recvmsg(fd, msg, flags); + /* An address was provided, convert from BSD form */ + if (msg->msg_name && IsBsd() && got != -1) { + sockaddr2linux(msg->msg_name); + } + return got; + } else if (__isfdkind(fd, kFdSocket)) { + return sys_recvfrom_nt(&g_fds.p[fd], msg->msg_iov, msg->msg_iovlen, flags, + msg->msg_name, &msg->msg_namelen); + } else { + return ebadf(); + } +} diff --git a/libc/sock/sendmsg.c b/libc/sock/sendmsg.c new file mode 100644 index 000000000..fc082c59a --- /dev/null +++ b/libc/sock/sendmsg.c @@ -0,0 +1,64 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" +#include "libc/dce.h" +#include "libc/sock/internal.h" +#include "libc/sock/sock.h" +#include "libc/str/str.h" +#include "libc/sysv/errfuns.h" + +/** + * Sends a message on a socket. + * + * @param fd is the file descriptor returned by socket() + * @param msg is a pointer to a struct msghdr containing all the required + * parameters (the destination address, buffers, ...) + * @param flags MSG_OOB, MSG_DONTROUTE, MSG_PARTIAL, MSG_NOSIGNAL, etc. + * @return number of bytes transmitted, or -1 w/ errno + * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), + * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. + * @asyncsignalsafe + */ +ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) { + if (!IsWindows()) { + if (IsBsd() && msg->msg_name) { + /* An optional address is provided, convert it to the BSD form */ + char addr2[128]; + struct msghdr msg2; + if (msg->msg_namelen > sizeof(addr2)) return einval(); + memcpy(&addr2[0], msg->msg_name, msg->msg_namelen); + sockaddr2bsd(&addr2[0]); + + /* Copy all of msg (except for msg_name) into the new ephemeral local */ + memcpy(&msg2, msg, sizeof(msg2)); + msg2.msg_name = &addr2[0]; + return sys_sendmsg(fd, &msg2, flags); + } + /* else do the syscall */ + return sys_sendmsg(fd, msg, flags); + } else if (__isfdkind(fd, kFdSocket)) { + if (msg->msg_control != NULL) return einval(); /* Control msg not supported */ + return sys_sendto_nt(&g_fds.p[fd], msg->msg_iov, msg->msg_iovlen, flags, + msg->msg_name, msg->msg_namelen); + } else { + return ebadf(); + } +} diff --git a/libc/sock/sock.h b/libc/sock/sock.h index f1e444ad7..2601bd3b5 100644 --- a/libc/sock/sock.h +++ b/libc/sock/sock.h @@ -67,7 +67,7 @@ struct pollfd { struct msghdr { /* Linux+NT ABI */ void *msg_name; /* optional address */ - int32_t msg_namelen; /* size of msg_name */ + uint32_t msg_namelen; /* size of msg_name */ struct iovec *msg_iov; /* scatter/gather array */ uint64_t msg_iovlen; /* iovec count */ void *msg_control; /* credentials and stuff */ @@ -94,7 +94,7 @@ int getsockname(int, void *, uint32_t *) paramsnonnull(); int getpeername(int, void *, uint32_t *) paramsnonnull(); ssize_t send(int, const void *, size_t, int) paramsnonnull(); ssize_t recv(int, void *, size_t, int); -ssize_t recvmsg(int, struct msghdr *, uint32_t) paramsnonnull(); +ssize_t recvmsg(int, struct msghdr *, int) paramsnonnull(); ssize_t recvfrom(int, void *, size_t, uint32_t, void *, uint32_t *); ssize_t sendmsg(int, const struct msghdr *, int) paramsnonnull(); ssize_t readv(int, const struct iovec *, int); diff --git a/test/libc/sock/sendrecvmsg_test.c b/test/libc/sock/sendrecvmsg_test.c new file mode 100644 index 000000000..41bcfc0b0 --- /dev/null +++ b/test/libc/sock/sendrecvmsg_test.c @@ -0,0 +1,62 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/dce.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sock.h" +#include "libc/testlib/testlib.h" +#include "libc/runtime/gc.internal.h" +#include "libc/x/x.h" + +TEST(sendrecvmsg, testPingPong) { + int fd[2]; + const char hello[] = "HELLO"; + const char world[] = "WORLD"; + struct msghdr msg; + struct iovec data[2]; + const uint32_t hwLen = strlen(hello)+strlen(world); + + memset(&msg, 0, sizeof(msg)); + memset(&data[0], 0, sizeof(data)); + + data[0].iov_base = hello; + data[0].iov_len = strlen(hello); + data[1].iov_base = world; + data[1].iov_len = strlen(world); /* Don't send the '\0' */ + + msg.msg_iov = &data[0]; + msg.msg_iovlen = 2; + + ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); + ASSERT_EQ(hwLen, sendmsg(fd[0], &msg, 0)); + + data[0].iov_base = gc(xcalloc(20, 1)); + data[0].iov_len = 20; + msg.msg_iovlen = 1; + ASSERT_EQ(hwLen, recvmsg(fd[1], &msg, 0)); + + EXPECT_STREQ("HELLOWORLD", msg.msg_iov[0].iov_base); + + ASSERT_NE(-1, close(fd[0])); + ASSERT_NE(-1, close(fd[1])); +} + +