// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#if defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)
// This must be defined before including any system headers.
#define __APPLE_USE_RFC_3542
#endif  // defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542)

#include "quiche/quic/core/quic_udp_socket.h"

#include "absl/base/optimization.h"
#include "quiche/quic/core/io/socket.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"

// Common cmsg-related functions are defined below.
// Windows and POSIX cmsg formats are actually fairly similar, except the
// Windows ones have all of the macros prefixed with WSA_ and all the type names
// are different.

namespace quic {
namespace {

#if defined(_WIN32)
using PlatformCmsghdr = ::WSACMSGHDR;
#if !defined(CMSG_DATA)
#define CMSG_DATA WSA_CMSG_DATA
#endif  // !defined(CMSG_DATA)
#else
using PlatformCmsghdr = ::cmsghdr;
#endif  // defined(_WIN32)

void PopulatePacketInfoFromControlMessageBase(
    PlatformCmsghdr* cmsg, QuicUdpPacketInfo* packet_info,
    QuicUdpPacketInfoBitMask packet_info_interested) {
  if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
    if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V6_SELF_IP)) {
      const in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
      const char* addr_data = reinterpret_cast<const char*>(&info->ipi6_addr);
      int addr_len = sizeof(in6_addr);
      QuicIpAddress self_v6_ip;
      if (self_v6_ip.FromPackedString(addr_data, addr_len)) {
        packet_info->SetSelfV6Ip(self_v6_ip);
      } else {
        QUIC_BUG(quic_bug_10751_1) << "QuicIpAddress::FromPackedString failed";
      }
    }
    return;
  }

  if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
    if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V4_SELF_IP)) {
      const in_pktinfo* info = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
      const char* addr_data = reinterpret_cast<const char*>(&info->ipi_addr);
      int addr_len = sizeof(in_addr);
      QuicIpAddress self_v4_ip;
      if (self_v4_ip.FromPackedString(addr_data, addr_len)) {
        packet_info->SetSelfV4Ip(self_v4_ip);
      } else {
        QUIC_BUG(quic_bug_10751_2) << "QuicIpAddress::FromPackedString failed";
      }
    }
    return;
  }
}

}  // namespace
}  // namespace quic

#if defined(_WIN32)
#include "quiche/quic/core/quic_udp_socket_win.inc"
#else
#include "quiche/quic/core/quic_udp_socket_posix.inc"
#endif

namespace quic {

QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family,
                                         int receive_buffer_size,
                                         int send_buffer_size, bool ipv6_only) {
  // QUICHE_DCHECK here so the program exits early(before reading packets) in
  // debug mode. This should have been a static_assert, however it can't be done
  // on ios/osx because CMSG_SPACE isn't a constant expression there.
  QUICHE_DCHECK_GE(kDefaultUdpPacketControlBufferSize, kMinCmsgSpaceForRead);

  absl::StatusOr<SocketFd> socket = socket_api::CreateSocket(
      quiche::FromPlatformAddressFamily(address_family),
      socket_api::SocketProtocol::kUdp,
      /*blocking=*/false);

  if (!socket.ok()) {
    QUIC_LOG_FIRST_N(ERROR, 100)
        << "UDP non-blocking socket creation for address_family="
        << address_family << " failed: " << socket.status();
    return kQuicInvalidSocketFd;
  }

#if !defined(_WIN32)
  SetGoogleSocketOptions(*socket);
#endif

  if (!SetupSocket(*socket, address_family, receive_buffer_size,
                   send_buffer_size, ipv6_only)) {
    Destroy(*socket);
    return kQuicInvalidSocketFd;
  }

  return *socket;
}

void QuicUdpSocketApi::Destroy(QuicUdpSocketFd fd) {
  if (fd != kQuicInvalidSocketFd) {
    absl::Status result = socket_api::Close(fd);
    if (!result.ok()) {
      QUIC_LOG_FIRST_N(WARNING, 100)
          << "Failed to close UDP socket with error " << result;
    }
  }
}

bool QuicUdpSocketApi::Bind(QuicUdpSocketFd fd, QuicSocketAddress address) {
  sockaddr_storage addr = address.generic_address();
  int addr_len =
      address.host().IsIPv4() ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
  return 0 == bind(fd, reinterpret_cast<sockaddr*>(&addr), addr_len);
}

bool QuicUdpSocketApi::BindInterface(QuicUdpSocketFd fd,
                                     const std::string& interface_name) {
#if defined(__linux__) && !defined(__ANDROID_API__)
  if (interface_name.empty() || interface_name.size() >= IFNAMSIZ) {
    QUIC_BUG(udp_bad_interface_name)
        << "interface_name must be nonempty and shorter than " << IFNAMSIZ;
    return false;
  }

  return 0 == setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
                         interface_name.c_str(), interface_name.length());
#else
  (void)fd;
  (void)interface_name;
  QUIC_BUG(interface_bind_not_implemented)
      << "Interface binding is not implemented on this platform";
  return false;
#endif
}

bool QuicUdpSocketApi::EnableDroppedPacketCount(QuicUdpSocketFd fd) {
#if defined(__linux__) && defined(SO_RXQ_OVFL)
  int get_overflow = 1;
  return 0 == setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
                         sizeof(get_overflow));
#else
  (void)fd;
  return false;
#endif
}

bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV4(QuicUdpSocketFd fd) {
  int get_self_ip = 1;
  return 0 == setsockopt(fd, IPPROTO_IP, IP_PKTINFO,
                         reinterpret_cast<char*>(&get_self_ip),
                         sizeof(get_self_ip));
}

bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV6(QuicUdpSocketFd fd) {
  int get_self_ip = 1;
  return 0 == setsockopt(fd, IPPROTO_IPV6, kIpv6RecvPacketInfo,
                         reinterpret_cast<char*>(&get_self_ip),
                         sizeof(get_self_ip));
}

bool QuicUdpSocketApi::EnableReceiveTimestamp(QuicUdpSocketFd fd) {
#if defined(QUIC_UDP_SOCKET_SUPPORT_LINUX_TIMESTAMPING)
  int timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
  return 0 == setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &timestamping,
                         sizeof(timestamping));
#else
  (void)fd;
  return false;
#endif
}

bool QuicUdpSocketApi::EnableReceiveTtlForV4(QuicUdpSocketFd fd) {
#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL)
  int get_ttl = 1;
  return 0 == setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &get_ttl, sizeof(get_ttl));
#else
  (void)fd;
  return false;
#endif
}

bool QuicUdpSocketApi::EnableReceiveTtlForV6(QuicUdpSocketFd fd) {
#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL)
  int get_ttl = 1;
  return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &get_ttl,
                         sizeof(get_ttl));
#else
  (void)fd;
  return false;
#endif
}

}  // namespace quic
