// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "test/syscalls/linux/socket_generic.h"

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "gtest/gtest.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"

// This file is a generic socket test file. It must be built with another file
// that provides the test types.

namespace gvisor {
namespace testing {

TEST_P(AllSocketPairTest, BasicReadWrite) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  char buf[20];
  const std::string data = "abc";
  ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), 3),
              SyscallSucceedsWithValue(3));
  ASSERT_THAT(ReadFd(sockets->second_fd(), buf, 3),
              SyscallSucceedsWithValue(3));
  EXPECT_EQ(data, absl::string_view(buf, 3));
}

TEST_P(AllSocketPairTest, BasicSendRecv) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  char sent_data[512];
  RandomizeBuffer(sent_data, sizeof(sent_data));
  ASSERT_THAT(
      RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0),
      SyscallSucceedsWithValue(sizeof(sent_data)));
  char received_data[sizeof(sent_data)];
  ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data,
                               sizeof(received_data), 0),
              SyscallSucceedsWithValue(sizeof(received_data)));
  EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
}

TEST_P(AllSocketPairTest, BasicSendmmsg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  char sent_data[200];
  RandomizeBuffer(sent_data, sizeof(sent_data));

  std::vector<struct mmsghdr> msgs(10);
  std::vector<struct iovec> iovs(msgs.size());
  const int chunk_size = sizeof(sent_data) / msgs.size();
  for (size_t i = 0; i < msgs.size(); i++) {
    iovs[i].iov_len = chunk_size;
    iovs[i].iov_base = &sent_data[i * chunk_size];
    msgs[i].msg_hdr.msg_iov = &iovs[i];
    msgs[i].msg_hdr.msg_iovlen = 1;
  }

  ASSERT_THAT(
      RetryEINTR(sendmmsg)(sockets->first_fd(), &msgs[0], msgs.size(), 0),
      SyscallSucceedsWithValue(msgs.size()));

  for (const struct mmsghdr& msg : msgs) {
    EXPECT_EQ(chunk_size, msg.msg_len);
  }

  char received_data[sizeof(sent_data)];
  for (size_t i = 0; i < msgs.size(); i++) {
    ASSERT_THAT(ReadFd(sockets->second_fd(), &received_data[i * chunk_size],
                       chunk_size),
                SyscallSucceedsWithValue(chunk_size));
  }
  EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
}

TEST_P(AllSocketPairTest, BasicRecvmmsg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  char sent_data[200];
  RandomizeBuffer(sent_data, sizeof(sent_data));

  char received_data[sizeof(sent_data)];
  std::vector<struct mmsghdr> msgs(10);
  std::vector<struct iovec> iovs(msgs.size());
  const int chunk_size = sizeof(sent_data) / msgs.size();
  for (size_t i = 0; i < msgs.size(); i++) {
    iovs[i].iov_len = chunk_size;
    iovs[i].iov_base = &received_data[i * chunk_size];
    msgs[i].msg_hdr.msg_iov = &iovs[i];
    msgs[i].msg_hdr.msg_iovlen = 1;
  }

  for (size_t i = 0; i < msgs.size(); i++) {
    ASSERT_THAT(
        WriteFd(sockets->first_fd(), &sent_data[i * chunk_size], chunk_size),
        SyscallSucceedsWithValue(chunk_size));
  }

  ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->second_fd(), &msgs[0], msgs.size(),
                                   0, nullptr),
              SyscallSucceedsWithValue(msgs.size()));

  EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));

  for (const struct mmsghdr& msg : msgs) {
    EXPECT_EQ(chunk_size, msg.msg_len);
  }
}

TEST_P(AllSocketPairTest, SendmsgRecvmsg10KB) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  std::vector<char> sent_data(10 * 1024);
  RandomizeBuffer(sent_data.data(), sent_data.size());
  ASSERT_NO_FATAL_FAILURE(
      SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size()));

  std::vector<char> received_data(sent_data.size());
  ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->second_fd(), received_data.data(),
                                     received_data.size()));

  EXPECT_EQ(0,
            memcmp(sent_data.data(), received_data.data(), sent_data.size()));
}

// This test validates that a sendmsg/recvmsg w/ MSG_CTRUNC is a no-op on
// input flags.
TEST_P(AllSocketPairTest, SendmsgRecvmsgMsgCtruncNoop) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  std::vector<char> sent_data(10 * 1024);
  RandomizeBuffer(sent_data.data(), sent_data.size());
  ASSERT_NO_FATAL_FAILURE(
      SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size()));

  std::vector<char> received_data(sent_data.size());
  struct msghdr msg = {};
  char control[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred))];
  msg.msg_control = control;
  msg.msg_controllen = sizeof(control);

  struct iovec iov;
  iov.iov_base = &received_data[0];
  iov.iov_len = received_data.size();
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  // MSG_CTRUNC should be a no-op.
  ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_CTRUNC),
              SyscallSucceedsWithValue(received_data.size()));
  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
  EXPECT_EQ(cmsg, nullptr);
  EXPECT_EQ(msg.msg_controllen, 0);
  EXPECT_EQ(0,
            memcmp(sent_data.data(), received_data.data(), sent_data.size()));
}

TEST_P(AllSocketPairTest, SendmsgRecvmsg16KB) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  std::vector<char> sent_data(16 * 1024);
  RandomizeBuffer(sent_data.data(), sent_data.size());
  ASSERT_NO_FATAL_FAILURE(
      SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size()));

  std::vector<char> received_data(sent_data.size());
  ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->second_fd(), received_data.data(),
                                     received_data.size()));

  EXPECT_EQ(0,
            memcmp(sent_data.data(), received_data.data(), sent_data.size()));
}

TEST_P(AllSocketPairTest, RecvmsgMsghdrFlagsNotClearedOnFailure) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  char received_data[10] = {};

  struct iovec iov;
  iov.iov_base = received_data;
  iov.iov_len = sizeof(received_data);
  struct msghdr msg = {};
  msg.msg_flags = -1;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_DONTWAIT),
              SyscallFailsWithErrno(EAGAIN));

  // Check that msghdr flags were not changed.
  EXPECT_EQ(msg.msg_flags, -1);
}

TEST_P(AllSocketPairTest, RecvmsgMsghdrFlagsCleared) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  char sent_data[10];
  RandomizeBuffer(sent_data, sizeof(sent_data));
  ASSERT_THAT(
      RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0),
      SyscallSucceedsWithValue(sizeof(sent_data)));

  char received_data[sizeof(sent_data)] = {};

  struct iovec iov;
  iov.iov_base = received_data;
  iov.iov_len = sizeof(received_data);
  struct msghdr msg = {};
  msg.msg_flags = -1;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
              SyscallSucceedsWithValue(sizeof(sent_data)));
  EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(sent_data)));

  // Check that msghdr flags were cleared.
  EXPECT_EQ(msg.msg_flags, 0);
}

TEST_P(AllSocketPairTest, RecvmsgPeekMsghdrFlagsCleared) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  char sent_data[10];
  RandomizeBuffer(sent_data, sizeof(sent_data));
  ASSERT_THAT(
      RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0),
      SyscallSucceedsWithValue(sizeof(sent_data)));

  char received_data[sizeof(sent_data)] = {};

  struct iovec iov;
  iov.iov_base = received_data;
  iov.iov_len = sizeof(received_data);
  struct msghdr msg = {};
  msg.msg_flags = -1;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_PEEK),
              SyscallSucceedsWithValue(sizeof(sent_data)));
  EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(sent_data)));

  // Check that msghdr flags were cleared.
  EXPECT_EQ(msg.msg_flags, 0);
}

TEST_P(AllSocketPairTest, RecvmsgIovNotUpdated) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  char sent_data[10];
  RandomizeBuffer(sent_data, sizeof(sent_data));
  ASSERT_THAT(
      RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0),
      SyscallSucceedsWithValue(sizeof(sent_data)));

  char received_data[sizeof(sent_data) * 2] = {};

  struct iovec iov;
  iov.iov_base = received_data;
  iov.iov_len = sizeof(received_data);
  struct msghdr msg = {};
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0),
              SyscallSucceedsWithValue(sizeof(sent_data)));
  EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(sent_data)));

  // Check that the iovec length was not updated.
  EXPECT_EQ(msg.msg_iov->iov_len, sizeof(received_data));
}

TEST_P(AllSocketPairTest, RecvmmsgInvalidTimeout) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  char buf[10];
  struct mmsghdr msg = {};
  struct iovec iov = {};
  iov.iov_len = sizeof(buf);
  iov.iov_base = buf;
  msg.msg_hdr.msg_iov = &iov;
  msg.msg_hdr.msg_iovlen = 1;
  struct timespec timeout = {-1, -1};
  ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->first_fd(), &msg, 1, 0, &timeout),
              SyscallFailsWithErrno(EINVAL));
}

TEST_P(AllSocketPairTest, RecvmmsgTimeoutBeforeRecv) {
  // There is a known bug in the Linux recvmmsg(2) causing it to block forever
  // if the timeout expires while blocking for the first message.
  SKIP_IF(!IsRunningOnGvisor());

  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  char buf[10];
  struct mmsghdr msg = {};
  struct iovec iov = {};
  iov.iov_len = sizeof(buf);
  iov.iov_base = buf;
  msg.msg_hdr.msg_iov = &iov;
  msg.msg_hdr.msg_iovlen = 1;
  struct timespec timeout = {};
  ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->first_fd(), &msg, 1, 0, &timeout),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, MsgPeek) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  char sent_data[50];
  memset(&sent_data, 0, sizeof(sent_data));
  ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)),
              SyscallSucceedsWithValue(sizeof(sent_data)));

  char received_data[sizeof(sent_data)];
  for (int i = 0; i < 3; i++) {
    memset(received_data, 0, sizeof(received_data));
    EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data,
                                 sizeof(received_data), MSG_PEEK),
                SyscallSucceedsWithValue(sizeof(received_data)));
    EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data)));
  }

  ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data,
                               sizeof(received_data), 0),
              SyscallSucceedsWithValue(sizeof(received_data)));
  EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data)));
}

TEST_P(AllSocketPairTest, LingerSocketOption) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  struct linger got_linger = {-1, -1};
  socklen_t length = sizeof(struct linger);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
                         &got_linger, &length),
              SyscallSucceedsWithValue(0));
  struct linger want_linger = {};
  EXPECT_EQ(0, memcmp(&want_linger, &got_linger, sizeof(struct linger)));
  EXPECT_EQ(sizeof(struct linger), length);
}

TEST_P(AllSocketPairTest, KeepAliveSocketOption) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  int keepalive = -1;
  socklen_t length = sizeof(int);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE,
                         &keepalive, &length),
              SyscallSucceedsWithValue(0));
  EXPECT_EQ(0, keepalive);
  EXPECT_EQ(sizeof(int), length);
}

TEST_P(AllSocketPairTest, RcvBufSucceeds) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  int size = 0;
  socklen_t size_size = sizeof(size);
  EXPECT_THAT(
      getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &size, &size_size),
      SyscallSucceeds());
  EXPECT_GT(size, 0);
}

TEST_P(AllSocketPairTest, SndBufSucceeds) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  int size = 0;
  socklen_t size_size = sizeof(size);
  EXPECT_THAT(
      getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &size, &size_size),
      SyscallSucceeds());
  EXPECT_GT(size, 0);
}

TEST_P(AllSocketPairTest, RecvTimeoutReadSucceeds) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 10
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  EXPECT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvTimeoutRecvSucceeds) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 10
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvTimeoutRecvOneSecondSucceeds) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 1, .tv_usec = 0
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgSucceeds) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 10
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  struct msghdr msg = {};
  char buf[20] = {};
  struct iovec iov;
  iov.iov_base = buf;
  iov.iov_len = sizeof(buf);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, SendTimeoutDefault) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  timeval actual_tv = {.tv_sec = -1, .tv_usec = -1};
  socklen_t len = sizeof(actual_tv);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO,
                         &actual_tv, &len),
              SyscallSucceeds());
  EXPECT_EQ(actual_tv.tv_sec, 0);
  EXPECT_EQ(actual_tv.tv_usec, 0);
}

TEST_P(AllSocketPairTest, SetGetSendTimeout) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  timeval tv = {.tv_sec = 89, .tv_usec = 42000};
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  timeval actual_tv = {};
  socklen_t len = sizeof(actual_tv);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO,
                         &actual_tv, &len),
              SyscallSucceeds());
  EXPECT_EQ(actual_tv.tv_sec, 89);
  EXPECT_EQ(actual_tv.tv_usec, 42000);
}

TEST_P(AllSocketPairTest, SetGetSendTimeoutLargerArg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval_with_extra {
    struct timeval tv;
    int64_t extra_data;
  } ABSL_ATTRIBUTE_PACKED;

  timeval_with_extra tv_extra = {
      .tv = {.tv_sec = 0, .tv_usec = 123000},
  };

  EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO,
                         &tv_extra, sizeof(tv_extra)),
              SyscallSucceeds());

  timeval_with_extra actual_tv = {};
  socklen_t len = sizeof(actual_tv);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO,
                         &actual_tv, &len),
              SyscallSucceeds());
  EXPECT_EQ(actual_tv.tv.tv_sec, 0);
  EXPECT_EQ(actual_tv.tv.tv_usec, 123000);
}

TEST_P(AllSocketPairTest, SendTimeoutAllowsWrite) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 10
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
              SyscallSucceedsWithValue(sizeof(buf)));
}

TEST_P(AllSocketPairTest, SendTimeoutAllowsSend) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 10
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0),
              SyscallSucceedsWithValue(sizeof(buf)));
}

TEST_P(AllSocketPairTest, SendTimeoutAllowsSendmsg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 10
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  ASSERT_NO_FATAL_FAILURE(SendNullCmsg(sockets->first_fd(), buf, sizeof(buf)));
}

TEST_P(AllSocketPairTest, RecvTimeoutDefault) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  timeval actual_tv = {.tv_sec = -1, .tv_usec = -1};
  socklen_t len = sizeof(actual_tv);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO,
                         &actual_tv, &len),
              SyscallSucceeds());
  EXPECT_EQ(actual_tv.tv_sec, 0);
  EXPECT_EQ(actual_tv.tv_usec, 0);
}

TEST_P(AllSocketPairTest, SetGetRecvTimeout) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  timeval tv = {.tv_sec = 123, .tv_usec = 456000};
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  timeval actual_tv = {};
  socklen_t len = sizeof(actual_tv);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO,
                         &actual_tv, &len),
              SyscallSucceeds());
  EXPECT_EQ(actual_tv.tv_sec, 123);
  EXPECT_EQ(actual_tv.tv_usec, 456000);
}

TEST_P(AllSocketPairTest, SetGetRecvTimeoutLargerArg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval_with_extra {
    struct timeval tv;
    int64_t extra_data;
  } ABSL_ATTRIBUTE_PACKED;

  timeval_with_extra tv_extra = {
      .tv = {.tv_sec = 0, .tv_usec = 432000},
  };

  EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO,
                         &tv_extra, sizeof(tv_extra)),
              SyscallSucceeds());

  timeval_with_extra actual_tv = {};
  socklen_t len = sizeof(actual_tv);
  EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO,
                         &actual_tv, &len),
              SyscallSucceeds());
  EXPECT_EQ(actual_tv.tv.tv_sec, 0);
  EXPECT_EQ(actual_tv.tv.tv_usec, 432000);
}

TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgOneSecondSucceeds) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 1, .tv_usec = 0
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  struct msghdr msg = {};
  char buf[20] = {};
  struct iovec iov;
  iov.iov_base = buf;
  iov.iov_len = sizeof(buf);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvTimeoutUsecTooLarge) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 2000000  // 2 seconds.
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallFailsWithErrno(EDOM));
}

TEST_P(AllSocketPairTest, SendTimeoutUsecTooLarge) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 2000000  // 2 seconds.
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
      SyscallFailsWithErrno(EDOM));
}

TEST_P(AllSocketPairTest, RecvTimeoutUsecNeg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = -1
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallFailsWithErrno(EDOM));
}

TEST_P(AllSocketPairTest, SendTimeoutUsecNeg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = -1
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),
      SyscallFailsWithErrno(EDOM));
}

TEST_P(AllSocketPairTest, RecvTimeoutNegSecRead) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = -1, .tv_usec = 0
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  EXPECT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecv) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = -1, .tv_usec = 0
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  char buf[20] = {};
  EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecvmsg) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = -1, .tv_usec = 0
  };
  EXPECT_THAT(
      setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),
      SyscallSucceeds());

  struct msghdr msg = {};
  char buf[20] = {};
  struct iovec iov;
  iov.iov_base = buf;
  iov.iov_len = sizeof(buf);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvWaitAll) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  char sent_data[100];
  RandomizeBuffer(sent_data, sizeof(sent_data));

  ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
              SyscallSucceedsWithValue(sizeof(sent_data)));

  char received_data[sizeof(sent_data)] = {};
  ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data,
                               sizeof(received_data), MSG_WAITALL),
              SyscallSucceedsWithValue(sizeof(sent_data)));

  EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
}

TEST_P(AllSocketPairTest, RecvWaitAllDontWait) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  char data[100] = {};
  ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), data, sizeof(data),
                               MSG_WAITALL | MSG_DONTWAIT),
              SyscallFailsWithErrno(EAGAIN));
}

TEST_P(AllSocketPairTest, RecvTimeoutWaitAll) {
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

  struct timeval tv {
    .tv_sec = 0, .tv_usec = 200000  // 200ms
  };
  EXPECT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv,
                         sizeof(tv)),
              SyscallSucceeds());

  char sent_data[100];
  RandomizeBuffer(sent_data, sizeof(sent_data));

  ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
              SyscallSucceedsWithValue(sizeof(sent_data)));

  char received_data[sizeof(sent_data) * 2] = {};
  ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data,
                               sizeof(received_data), MSG_WAITALL),
              SyscallSucceedsWithValue(sizeof(sent_data)));

  EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data)));
}

TEST_P(AllSocketPairTest, GetSockoptType) {
  int type = GetParam().type;
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  for (const int fd : {sockets->first_fd(), sockets->second_fd()}) {
    int opt;
    socklen_t optlen = sizeof(opt);
    EXPECT_THAT(getsockopt(fd, SOL_SOCKET, SO_TYPE, &opt, &optlen),
                SyscallSucceeds());

    // Type may have SOCK_NONBLOCK and SOCK_CLOEXEC ORed into it. Remove these
    // before comparison.
    type &= ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
    EXPECT_EQ(opt, type) << absl::StrFormat(
        "getsockopt(%d, SOL_SOCKET, SO_TYPE, &opt, &optlen) => opt=%d was "
        "unexpected",
        fd, opt);
  }
}

TEST_P(AllSocketPairTest, GetSockoptDomain) {
  const int domain = GetParam().domain;
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  for (const int fd : {sockets->first_fd(), sockets->second_fd()}) {
    int opt;
    socklen_t optlen = sizeof(opt);
    EXPECT_THAT(getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &opt, &optlen),
                SyscallSucceeds());
    EXPECT_EQ(opt, domain) << absl::StrFormat(
        "getsockopt(%d, SOL_SOCKET, SO_DOMAIN, &opt, &optlen) => opt=%d was "
        "unexpected",
        fd, opt);
  }
}

TEST_P(AllSocketPairTest, GetSockoptProtocol) {
  const int protocol = GetParam().protocol;
  auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  for (const int fd : {sockets->first_fd(), sockets->second_fd()}) {
    int opt;
    socklen_t optlen = sizeof(opt);
    EXPECT_THAT(getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &opt, &optlen),
                SyscallSucceeds());
    EXPECT_EQ(opt, protocol) << absl::StrFormat(
        "getsockopt(%d, SOL_SOCKET, SO_PROTOCOL, &opt, &optlen) => opt=%d was "
        "unexpected",
        fd, opt);
  }
}

}  // namespace testing
}  // namespace gvisor