|  | // Copyright 2023 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. | 
|  |  | 
|  | // UDP socket implementation for Windows.  Feature overview: | 
|  | // * Supported: setting and getting self IP address. | 
|  | // * Unsupported but could work on a sufficiently new Windows version: | 
|  | //   - Timestamping | 
|  | //   - Setting and getting TTL. | 
|  |  | 
|  | #include <mswsock.h> | 
|  | #include <winsock2.h> | 
|  |  | 
|  | #include <cstddef> | 
|  |  | 
|  | #include "quiche/quic/core/quic_utils.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | namespace { | 
|  | constexpr size_t kMinCmsgSpaceForRead = | 
|  | CMSG_SPACE(sizeof(in_pktinfo))      // V4 Self IP | 
|  | + CMSG_SPACE(sizeof(in6_pktinfo));  // V6 Self IP | 
|  |  | 
|  | constexpr int kIpv6RecvPacketInfo = IPV6_PKTINFO; | 
|  |  | 
|  | void SetV4SelfIpInControlMessage(const QuicIpAddress& self_address, | 
|  | WSACMSGHDR* cmsg) { | 
|  | QUICHE_DCHECK(self_address.IsIPv4()); | 
|  | in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(WSA_CMSG_DATA(cmsg)); | 
|  | memset(pktinfo, 0, sizeof(in_pktinfo)); | 
|  | pktinfo->ipi_addr = self_address.GetIPv4(); | 
|  | } | 
|  |  | 
|  | void SetV6SelfIpInControlMessage(const QuicIpAddress& self_address, | 
|  | cmsghdr* cmsg) { | 
|  | QUICHE_DCHECK(self_address.IsIPv6()); | 
|  | in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(WSA_CMSG_DATA(cmsg)); | 
|  | memset(pktinfo, 0, sizeof(in6_pktinfo)); | 
|  | pktinfo->ipi6_addr = self_address.GetIPv6(); | 
|  | } | 
|  |  | 
|  | bool NextCmsg(WSAMSG* hdr, char* control_buffer, size_t control_buffer_len, | 
|  | int cmsg_level, int cmsg_type, size_t data_size, | 
|  | WSACMSGHDR** cmsg /*in, out*/) { | 
|  | // msg_controllen needs to be increased first, otherwise CMSG_NXTHDR will | 
|  | // return nullptr. | 
|  | hdr->Control.len += WSA_CMSG_SPACE(data_size); | 
|  | if (hdr->Control.len > control_buffer_len) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if ((*cmsg) == nullptr) { | 
|  | QUICHE_DCHECK_EQ(nullptr, hdr->Control.buf); | 
|  | memset(control_buffer, 0, control_buffer_len); | 
|  | hdr->Control.buf = control_buffer; | 
|  | (*cmsg) = WSA_CMSG_FIRSTHDR(hdr); | 
|  | } else { | 
|  | QUICHE_DCHECK_NE(nullptr, hdr->Control.buf); | 
|  | (*cmsg) = WSA_CMSG_NXTHDR(hdr, (*cmsg)); | 
|  | } | 
|  |  | 
|  | if (nullptr == (*cmsg)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | (*cmsg)->cmsg_len = WSA_CMSG_LEN(data_size); | 
|  | (*cmsg)->cmsg_level = cmsg_level; | 
|  | (*cmsg)->cmsg_type = cmsg_type; | 
|  |  | 
|  | return true; | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | bool QuicUdpSocketApi::SetupSocket(QuicUdpSocketFd fd, int address_family, | 
|  | int receive_buffer_size, | 
|  | int send_buffer_size, bool /*ipv6_only*/) { | 
|  | // Receive buffer size. | 
|  | if (absl::Status status = | 
|  | socket_api::SetReceiveBufferSize(fd, receive_buffer_size); | 
|  | !status.ok()) { | 
|  | QUIC_LOG_FIRST_N(ERROR, 100) | 
|  | << "Failed to set socket recv size: " << status; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Send buffer size. | 
|  | if (absl::Status status = socket_api::SetSendBufferSize(fd, send_buffer_size); | 
|  | !status.ok()) { | 
|  | QUIC_LOG_FIRST_N(ERROR, 100) | 
|  | << "Failed to set socket send size: " << status; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (address_family == AF_INET) { | 
|  | if (!EnableReceiveSelfIpAddressForV4(fd)) { | 
|  | QUIC_LOG_FIRST_N(ERROR, 100) | 
|  | << "Failed to enable receiving of self v4 ip"; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (address_family == AF_INET6) { | 
|  | if (!EnableReceiveSelfIpAddressForV6(fd)) { | 
|  | QUIC_LOG_FIRST_N(ERROR, 100) | 
|  | << "Failed to enable receiving of self v6 ip"; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void QuicUdpSocketApi::ReadPacket( | 
|  | QuicUdpSocketFd fd, QuicUdpPacketInfoBitMask packet_info_interested, | 
|  | ReadPacketResult* result) { | 
|  | result->ok = false; | 
|  |  | 
|  | // WSARecvMsg is an extension to Windows Socket API that requires us to fetch | 
|  | // the function pointer via an ioctl. | 
|  | DWORD recvmsg_fn_out_bytes; | 
|  | LPFN_WSARECVMSG recvmsg_fn = nullptr; | 
|  | GUID recvmsg_guid = WSAID_WSARECVMSG; | 
|  | int ioctl_result = | 
|  | WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &recvmsg_guid, | 
|  | sizeof(recvmsg_guid), &recvmsg_fn, sizeof(recvmsg_fn), | 
|  | &recvmsg_fn_out_bytes, nullptr, nullptr); | 
|  | if (ioctl_result != 0) { | 
|  | QUICHE_LOG(ERROR) << "Failed to load WSARecvMsg() function, error code: " | 
|  | << WSAGetLastError(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | BufferSpan& packet_buffer = result->packet_buffer; | 
|  | BufferSpan& control_buffer = result->control_buffer; | 
|  | QuicUdpPacketInfo* packet_info = &result->packet_info; | 
|  |  | 
|  | QUICHE_DCHECK_GE(control_buffer.buffer_len, kMinCmsgSpaceForRead); | 
|  |  | 
|  | WSABUF iov; | 
|  | iov.buf = packet_buffer.buffer; | 
|  | iov.len = packet_buffer.buffer_len; | 
|  | sockaddr_storage raw_peer_address; | 
|  |  | 
|  | if (control_buffer.buffer_len > 0) { | 
|  | reinterpret_cast<WSACMSGHDR*>(control_buffer.buffer)->cmsg_len = | 
|  | control_buffer.buffer_len; | 
|  | } | 
|  |  | 
|  | WSAMSG hdr; | 
|  | memset(&hdr, 0, sizeof(hdr)); | 
|  | hdr.name = reinterpret_cast<sockaddr*>(&raw_peer_address); | 
|  | hdr.namelen = sizeof(raw_peer_address); | 
|  | hdr.lpBuffers = &iov; | 
|  | hdr.dwBufferCount = 1; | 
|  | hdr.dwFlags = 0; | 
|  | hdr.Control.buf = control_buffer.buffer; | 
|  | hdr.Control.len = control_buffer.buffer_len; | 
|  |  | 
|  | DWORD bytes_read; | 
|  | int recvmsg_result = recvmsg_fn(fd, &hdr, &bytes_read, nullptr, nullptr); | 
|  | if (recvmsg_result != 0) { | 
|  | const int error_num = WSAGetLastError(); | 
|  | if (error_num != WSAEWOULDBLOCK) { | 
|  | QUIC_LOG_FIRST_N(ERROR, 100) << "Error reading packet: " << error_num; | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | packet_buffer.buffer_len = bytes_read; | 
|  | if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::PEER_ADDRESS)) { | 
|  | packet_info->SetPeerAddress(QuicSocketAddress(raw_peer_address)); | 
|  | } | 
|  |  | 
|  | if (hdr.Control.len > 0) { | 
|  | for (WSACMSGHDR* cmsg = WSA_CMSG_FIRSTHDR(&hdr); cmsg != nullptr; | 
|  | cmsg = WSA_CMSG_NXTHDR(&hdr, cmsg)) { | 
|  | QuicUdpPacketInfoBitMask prior_bitmask = packet_info->bitmask(); | 
|  | PopulatePacketInfoFromControlMessageBase(cmsg, packet_info, | 
|  | packet_info_interested); | 
|  | if (packet_info->bitmask() == prior_bitmask) { | 
|  | QUIC_DLOG(INFO) << "Ignored cmsg_level:" << cmsg->cmsg_level | 
|  | << ", cmsg_type:" << cmsg->cmsg_type; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | result->ok = true; | 
|  | } | 
|  |  | 
|  | size_t QuicUdpSocketApi::ReadMultiplePackets( | 
|  | QuicUdpSocketFd fd, QuicUdpPacketInfoBitMask packet_info_interested, | 
|  | ReadPacketResults* results) { | 
|  | size_t num_packets = 0; | 
|  | for (ReadPacketResult& result : *results) { | 
|  | result.ok = false; | 
|  | } | 
|  | for (ReadPacketResult& result : *results) { | 
|  | ReadPacket(fd, packet_info_interested, &result); | 
|  | if (!result.ok && WSAGetLastError() == WSAEWOULDBLOCK) { | 
|  | break; | 
|  | } | 
|  | ++num_packets; | 
|  | } | 
|  | return num_packets; | 
|  | } | 
|  |  | 
|  | WriteResult QuicUdpSocketApi::WritePacket( | 
|  | QuicUdpSocketFd fd, const char* packet_buffer, size_t packet_buffer_len, | 
|  | const QuicUdpPacketInfo& packet_info) { | 
|  | if (!packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)) { | 
|  | return WriteResult(WRITE_STATUS_ERROR, WSAEINVAL); | 
|  | } | 
|  |  | 
|  | char control_buffer[512]; | 
|  | sockaddr_storage raw_peer_address = | 
|  | packet_info.peer_address().generic_address(); | 
|  | WSABUF iov; | 
|  | iov.buf = const_cast<char*>(packet_buffer); | 
|  | iov.len = packet_buffer_len; | 
|  |  | 
|  | WSAMSG hdr; | 
|  | memset(&hdr, 0, sizeof(hdr)); | 
|  | hdr.name = reinterpret_cast<sockaddr*>(&raw_peer_address); | 
|  | hdr.namelen = packet_info.peer_address().host().IsIPv4() | 
|  | ? sizeof(sockaddr_in) | 
|  | : sizeof(sockaddr_in6); | 
|  | hdr.lpBuffers = &iov; | 
|  | hdr.dwBufferCount = 1; | 
|  |  | 
|  | WSACMSGHDR* cmsg = nullptr; | 
|  |  | 
|  | // Set self IP. | 
|  | if (packet_info.HasValue(QuicUdpPacketInfoBit::V4_SELF_IP) && | 
|  | packet_info.self_v4_ip().IsInitialized()) { | 
|  | if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), IPPROTO_IP, | 
|  | IP_PKTINFO, sizeof(in_pktinfo), &cmsg)) { | 
|  | QUIC_LOG_FIRST_N(ERROR, 100) | 
|  | << "Not enough buffer to set self v4 ip address."; | 
|  | return WriteResult(WRITE_STATUS_ERROR, EINVAL); | 
|  | } | 
|  | SetV4SelfIpInControlMessage(packet_info.self_v4_ip(), cmsg); | 
|  | } else if (packet_info.HasValue(QuicUdpPacketInfoBit::V6_SELF_IP) && | 
|  | packet_info.self_v6_ip().IsInitialized()) { | 
|  | if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), IPPROTO_IPV6, | 
|  | IPV6_PKTINFO, sizeof(in6_pktinfo), &cmsg)) { | 
|  | QUIC_LOG_FIRST_N(ERROR, 100) | 
|  | << "Not enough buffer to set self v6 ip address."; | 
|  | return WriteResult(WRITE_STATUS_ERROR, EINVAL); | 
|  | } | 
|  | SetV6SelfIpInControlMessage(packet_info.self_v6_ip(), cmsg); | 
|  | } | 
|  |  | 
|  | DWORD bytes_sent; | 
|  | int result = | 
|  | WSASendMsg(fd, &hdr, /*dwFlags=*/0, &bytes_sent, nullptr, nullptr); | 
|  | if (result == 0) { | 
|  | return WriteResult(WRITE_STATUS_OK, bytes_sent); | 
|  | } | 
|  | int error = WSAGetLastError(); | 
|  | return WriteResult( | 
|  | (error == WSAEWOULDBLOCK) ? WRITE_STATUS_BLOCKED : WRITE_STATUS_ERROR, | 
|  | error); | 
|  | } | 
|  |  | 
|  | bool QuicUdpSocketApi::WaitUntilReadable(QuicUdpSocketFd fd, | 
|  | QuicTime::Delta timeout) { | 
|  | WSAPOLLFD polled_fd; | 
|  | polled_fd.fd = fd; | 
|  | polled_fd.events = POLLIN; | 
|  | polled_fd.revents = 0; | 
|  |  | 
|  | int result = ::WSAPoll(&polled_fd, 1, timeout.ToMilliseconds()); | 
|  | if (result == SOCKET_ERROR) { | 
|  | QUICHE_LOG(ERROR) << "Error while calling WSAPoll(): " << WSAGetLastError(); | 
|  | } | 
|  |  | 
|  | return result > 0; | 
|  | } | 
|  |  | 
|  | }  // namespace quic |