[libc] Add the MSG_ flags (#194375)
I've included all of the flags defined on linux, even those not documented in POSIX (or the manpage). I've also added tests to exercise the flags, for cases where this can be done with domain sockets.
This commit is contained in:
@@ -52,6 +52,29 @@
|
||||
|
||||
#define SCM_RIGHTS 1
|
||||
|
||||
#define MSG_OOB 0x01
|
||||
#define MSG_PEEK 0x02
|
||||
#define MSG_DONTROUTE 0x04
|
||||
#define MSG_CTRUNC 0x08
|
||||
#define MSG_PROXY 0x10
|
||||
#define MSG_TRUNC 0x20
|
||||
#define MSG_DONTWAIT 0x40
|
||||
#define MSG_EOR 0x80
|
||||
#define MSG_WAITALL 0x100
|
||||
#define MSG_FIN 0x200
|
||||
#define MSG_SYN 0x400
|
||||
#define MSG_CONFIRM 0x800
|
||||
#define MSG_RST 0x1000
|
||||
#define MSG_ERRQUEUE 0x2000
|
||||
#define MSG_NOSIGNAL 0x4000
|
||||
#define MSG_MORE 0x8000
|
||||
#define MSG_WAITFORONE 0x10000
|
||||
#define MSG_BATCH 0x40000
|
||||
#define MSG_SOCK_DEVMEM 0x2000000
|
||||
#define MSG_ZEROCOPY 0x4000000
|
||||
#define MSG_FASTOPEN 0x20000000
|
||||
#define MSG_CMSG_CLOEXEC 0x40000000
|
||||
|
||||
#define CMSG_ALIGN(len) (((len) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1))
|
||||
#define CMSG_LEN(len) (sizeof(struct cmsghdr) + (len))
|
||||
#define CMSG_SPACE(len) (sizeof(struct cmsghdr) + CMSG_ALIGN(len))
|
||||
|
||||
@@ -125,7 +125,7 @@ add_libc_unittest(
|
||||
SRCS
|
||||
send_recv_test.cpp
|
||||
DEPENDS
|
||||
libc.include.sys_socket
|
||||
libc.hdr.sys_socket_macros
|
||||
libc.src.errno.errno
|
||||
libc.src.sys.socket.socketpair
|
||||
libc.src.sys.socket.send
|
||||
@@ -159,9 +159,11 @@ add_libc_unittest(
|
||||
SRCS
|
||||
sendmsg_recvmsg_test.cpp
|
||||
DEPENDS
|
||||
libc.include.sys_socket
|
||||
libc.hdr.fcntl_macros
|
||||
libc.hdr.sys_socket_macros
|
||||
libc.hdr.types.struct_cmsghdr
|
||||
libc.src.errno.errno
|
||||
libc.src.fcntl.fcntl
|
||||
libc.src.string.memcpy
|
||||
libc.src.string.memset
|
||||
libc.src.sys.socket.getsockopt
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "hdr/sys_socket_macros.h"
|
||||
#include "src/sys/socket/recv.h"
|
||||
#include "src/sys/socket/send.h"
|
||||
#include "src/sys/socket/socketpair.h"
|
||||
@@ -16,8 +17,6 @@
|
||||
#include "test/UnitTest/ErrnoSetterMatcher.h"
|
||||
#include "test/UnitTest/Test.h"
|
||||
|
||||
#include <sys/socket.h> // For AF_UNIX and SOCK_DGRAM
|
||||
|
||||
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
|
||||
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
|
||||
using LlvmLibcSendRecvTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
|
||||
@@ -46,6 +45,47 @@ TEST_F(LlvmLibcSendRecvTest, SucceedsWithSocketPair) {
|
||||
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcSendRecvTest, MsgFlagsTest) {
|
||||
int sockpair[2] = {0, 0};
|
||||
|
||||
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockpair),
|
||||
Succeeds(0));
|
||||
|
||||
char buffer[256] = {};
|
||||
|
||||
// MSG_DONTWAIT on an empty socket returns EAGAIN
|
||||
ASSERT_THAT(
|
||||
LIBC_NAMESPACE::recv(sockpair[1], buffer, sizeof(buffer), MSG_DONTWAIT),
|
||||
Fails<ssize_t>(EAGAIN));
|
||||
|
||||
const char TEST_MESSAGE[] = "this is a long message";
|
||||
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);
|
||||
|
||||
ASSERT_THAT(LIBC_NAMESPACE::send(sockpair[0], TEST_MESSAGE, MESSAGE_LEN, 0),
|
||||
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
|
||||
|
||||
// MSG_PEEK does not remove the message from the socket
|
||||
ASSERT_THAT(
|
||||
LIBC_NAMESPACE::recv(sockpair[1], buffer, sizeof(buffer), MSG_PEEK),
|
||||
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
|
||||
ASSERT_STREQ(buffer, TEST_MESSAGE);
|
||||
|
||||
// Read the message again, but use a smaller buffer to test MSG_TRUNC. Return
|
||||
// value should be real length.
|
||||
char small_buffer[6] = {};
|
||||
ASSERT_THAT(LIBC_NAMESPACE::recv(sockpair[1], small_buffer,
|
||||
sizeof(small_buffer) - 1, MSG_TRUNC),
|
||||
Succeeds(static_cast<ssize_t>(MESSAGE_LEN)));
|
||||
ASSERT_STREQ(small_buffer, "this ");
|
||||
|
||||
// Sending with MSG_NOSIGNAL to a closed socket should fail with EPIPE
|
||||
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
|
||||
ASSERT_THAT(LIBC_NAMESPACE::send(sockpair[0], "x", 1, MSG_NOSIGNAL),
|
||||
Fails<ssize_t>(EPIPE));
|
||||
|
||||
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcSendRecvTest, SendFails) {
|
||||
const char TEST_MESSAGE[] = "connection terminated";
|
||||
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "hdr/fcntl_macros.h"
|
||||
#include "hdr/sys_socket_macros.h"
|
||||
#include "hdr/types/struct_cmsghdr.h"
|
||||
#include "src/fcntl/fcntl.h"
|
||||
#include "src/string/memcpy.h"
|
||||
#include "src/string/memset.h"
|
||||
#include "src/sys/socket/getsockopt.h"
|
||||
@@ -22,8 +24,6 @@
|
||||
#include "test/UnitTest/LibcTest.h"
|
||||
#include "test/UnitTest/Test.h"
|
||||
|
||||
#include <sys/socket.h> // For AF_UNIX and SOCK_DGRAM
|
||||
|
||||
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
|
||||
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
|
||||
using LlvmLibcSendMsgRecvMsgTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
|
||||
@@ -201,6 +201,71 @@ TEST_F(LlvmLibcSendMsgRecvMsgTest, SendAndReceiveFileDescriptor) {
|
||||
ASSERT_THAT(LIBC_NAMESPACE::close(new_fd), Succeeds(0));
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcSendMsgRecvMsgTest, MsgCmsgCloexec) {
|
||||
int sockpair[2] = {0, 0};
|
||||
|
||||
ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair),
|
||||
Succeeds(0));
|
||||
|
||||
struct iovec iov;
|
||||
iov.iov_base = reinterpret_cast<void *>(const_cast<char *>("x"));
|
||||
iov.iov_len = 1;
|
||||
|
||||
char control_buf[CMSG_SPACE(sizeof(int))] = {};
|
||||
|
||||
struct msghdr msg;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = control_buf;
|
||||
msg.msg_controllen = CMSG_LEN(sizeof(int));
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
LIBC_NAMESPACE::memcpy(CMSG_DATA(cmsg), sockpair + 1, sizeof(int));
|
||||
|
||||
ASSERT_THAT(LIBC_NAMESPACE::sendmsg(sockpair[0], &msg, 0),
|
||||
Succeeds(static_cast<ssize_t>(1)));
|
||||
|
||||
char buffer[256];
|
||||
|
||||
iov.iov_base = buffer;
|
||||
iov.iov_len = sizeof(buffer);
|
||||
|
||||
LIBC_NAMESPACE::memset(control_buf, 0, sizeof(control_buf));
|
||||
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = control_buf;
|
||||
msg.msg_controllen = sizeof(control_buf);
|
||||
msg.msg_flags = 0;
|
||||
|
||||
// Receive with MSG_CMSG_CLOEXEC
|
||||
ASSERT_THAT(LIBC_NAMESPACE::recvmsg(sockpair[1], &msg, MSG_CMSG_CLOEXEC),
|
||||
Succeeds(static_cast<ssize_t>(1)));
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
ASSERT_TRUE(cmsg != nullptr);
|
||||
|
||||
int new_fd;
|
||||
LIBC_NAMESPACE::memcpy(&new_fd, CMSG_DATA(cmsg), sizeof(int));
|
||||
|
||||
// Check FD_CLOEXEC
|
||||
int flags = LIBC_NAMESPACE::fcntl(new_fd, F_GETFD);
|
||||
ASSERT_GE(flags, 0);
|
||||
ASSERT_NE(flags & FD_CLOEXEC, 0);
|
||||
|
||||
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
|
||||
ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
|
||||
ASSERT_THAT(LIBC_NAMESPACE::close(new_fd), Succeeds(0));
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcSendMsgRecvMsgTest, SendFails) {
|
||||
const char TEST_MESSAGE[] = "connection terminated";
|
||||
const size_t MESSAGE_LEN = sizeof(TEST_MESSAGE);
|
||||
|
||||
Reference in New Issue
Block a user